<# .SYNOPSIS SharePoint Production Toolkit - Script 02 - Storage Usage Report.PARAMETER OutputCsvSharePoint Production Toolkit - Script 02 - Storage Usage Report Full path to the detail CSV output file. .PARAMETER SiteCollectionUrl Optional. Limit the scan to a single site collection. .PARAMETER NoPrompt Optional. Skips confirmation prompt. .PARAMETER Remediate Optional. Included for toolkit consistency only. No changes are made. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$WebAppUrl, [Parameter(Mandatory = $true)] [string]$OutputCsv, [string]$SiteCollectionUrl, [switch]$NoPrompt, [switch]$Remediate ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" Write-Host "" Write-Host "SCRIPT 02 - STORAGE USAGE REPORT" -ForegroundColor Cyan Write-Host "READ-ONLY. NO CHANGES ARE MADE." -ForegroundColor Green Write-Host "" # Load SharePoint Snap-in try { if (-not (Get-PSSnapin -Name "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue)) { Add-PSSnapin "Microsoft.SharePoint.PowerShell" } } catch { throw "Run this script in SharePoint Management Shell. Error: $($_.Exception.Message)" } # Output setup $outDir = Split-Path -Path $OutputCsv -Parent if ([string]::IsNullOrWhiteSpace($outDir)) { throw "OutputCsv must be a full path." } if (-not (Test-Path $outDir)) { New-Item -Path $outDir -ItemType Directory -Force | Out-Null } $timestamp = (Get-Date).ToString("yyyyMMdd_HHmmss") $baseName = [System.IO.Path]::GetFileNameWithoutExtension($OutputCsv) $summaryPath = Join-Path $outDir ("{0}_{1}_TopConsumers.csv" -f $baseName, $timestamp) $logPath = Join-Path $outDir ("{0}_{1}_RunLog.txt" -f $baseName, $timestamp) $errorPath = Join-Path $outDir ("{0}_{1}_Errors.csv" -f $baseName, $timestamp) # Helpers $errors = New-Object System.Collections.Generic.List[object] function Add-Error { param($Scope, $Message) $errors.Add([pscustomobject]@{ Timestamp = Get-Date Scope = $Scope Message = $Message }) | Out-Null } $log = New-Object System.Collections.Generic.List[string] function Log { param($msg) $line = "[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $msg $log.Add($line) | Out-Null Write-Host $line } # Prompt if ($Remediate) { Write-Host "Note: -Remediate has no effect (report-only script)." -ForegroundColor Yellow } if (-not $NoPrompt) { if ((Read-Host "Type YES to continue") -ne "YES") { return } } # Resolve sites function Get-Sites { if ($SiteCollectionUrl) { return @(Get-SPSite $SiteCollectionUrl) } return @(Get-SPSite -WebApplication $WebAppUrl -Limit All) } # Risk evaluation function Get-RiskLevel($percentUsed, $sizeMB) { if ($percentUsed -ge 90 -or $sizeMB -gt 10240) { return "High" } elseif ($percentUsed -ge 70 -or $sizeMB -gt 2048) { return "Medium" } else { return "Low" } } function Get-Recommendation($risk) { switch ($risk) { "High" { return "Review for cleanup, archive, or split before migration." } "Medium" { return "Monitor growth and validate migration readiness." } "Low" { return "Low risk. Suitable for standard migration." } } } # Main $results = New-Object System.Collections.Generic.List[object] try { $sites = Get-Sites Log ("Scanning {0} site collections..." -f $sites.Count) } catch { Add-Error $WebAppUrl $_.Exception.Message throw } foreach ($site in $sites) { try { Log ("Analyzing: {0}" -f $site.Url) $sizeMB = 0 try { $sizeMB = [math]::Round(($site.Usage.Storage / 1MB), 2) } catch {} $quotaMB = 0 try { $quotaMB = [math]::Round(($site.Quota.StorageMaximumLevel / 1MB), 2) } catch {} $percentUsed = 0 if ($quotaMB -gt 0) { $percentUsed = [math]::Round(($sizeMB / $quotaMB) * 100, 2) } $dbName = "" try { $dbName = $site.ContentDatabase.Name } catch {} $risk = Get-RiskLevel $percentUsed $sizeMB $recommendation = Get-Recommendation $risk $results.Add([pscustomobject]@{ SiteCollectionUrl = $site.Url ContentDatabase = $dbName SizeMB = $sizeMB QuotaMB = $quotaMB PercentUsed = $percentUsed RiskLevel = $risk Category = "StorageUsage" ActionRecommendation = $recommendation }) | Out-Null } catch { Add-Error $site.Url $_.Exception.Message Log ("FAILED: {0}" -f $site.Url) } finally { $site.Dispose() } } # Export detail $results | Export-Csv $OutputCsv -NoTypeInformation -Encoding UTF8 # Top consumers summary $results | Sort-Object SizeMB -Descending | Select-Object -First 20 | Export-Csv $summaryPath -NoTypeInformation -Encoding UTF8 # Log + errors $log | Set-Content $logPath if ($errors.Count -gt 0) { $errors | Export-Csv $errorPath -NoTypeInformation Write-Host "ERROR REPORT: $errorPath" -ForegroundColor Yellow } Write-Host "" Write-Host "DETAIL REPORT: $OutputCsv" -ForegroundColor Green Write-Host "SUMMARY REPORT: $summaryPath" -ForegroundColor Green Write-Host "RUN LOG: $logPath" -ForegroundColor Green Write-Host "" Write-Host "Complete." -ForegroundColor Green .DESCRIPTION READ-ONLY. Analyzes storage usage across site collections and identifies large or high-usage sites for migration planning and optimization. .PARAMETER WebAppUrl Target SharePoint Web Application URL.