<# .SYNOPSIS SharePoint Production Toolkit - Script 08 - Large Lists Report :.DESCRIPTION - Migration readiness - Performance tuning - List optimization planning .PARAMETER WebAppUrl Target SharePoint Web Application URL. .PARAMETER OutputCsv Full path to output CSV file. .PARAMETER SiteCollectionUrl Optional. Limit to a single site collection. .PARAMETER ThresholdWarning Optional. Default = 3000 items (approaching threshold). .PARAMETER ThresholdCritical Optional. Default = 5000 items (SharePoint threshold). .PARAMETER NoPrompt Optional. Skip confirmation prompt. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$WebAppUrl, [Parameter(Mandatory = $true)] [string]$OutputCsv, [string]$SiteCollectionUrl, [int]$ThresholdWarning = 3000, [int]$ThresholdCritical = 5000, [switch]$NoPrompt ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" Write-Host "" Write-Host "SCRIPT 08 - LARGE LISTS REPORT" -ForegroundColor Cyan Write-Host "READ-ONLY. NO CHANGES ARE MADE." -ForegroundColor Green Write-Host "" # ------------------------------------------------------------ # Load SharePoint snap-in # ------------------------------------------------------------ try { if (-not (Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue)) { Add-PSSnapin "Microsoft.SharePoint.PowerShell" } } catch { throw "Run in SharePoint Management Shell. Error: $($_.Exception.Message)" } # ------------------------------------------------------------ # Output setup # ------------------------------------------------------------ $outDir = Split-Path -Path $OutputCsv -Parent if ([string]::IsNullOrWhiteSpace($outDir)) { throw "Provide full OutputCsv path. Example: C:\Temp\LargeLists.csv" } 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}_Summary_ByRisk.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) # ------------------------------------------------------------ # Logging helpers # ------------------------------------------------------------ $errors = New-Object System.Collections.Generic.List[object] $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 } function Add-Error { param($scope, $msg) $errors.Add([pscustomobject]@{ Timestamp = Get-Date Scope = $scope Message = $msg }) | Out-Null } # ------------------------------------------------------------ # Prompt # ------------------------------------------------------------ if (-not $NoPrompt) { Write-Host "This script scans lists and identifies large lists." -ForegroundColor Yellow if ((Read-Host "Type YES to continue") -ne "YES") { return } } # ------------------------------------------------------------ # Resolve sites # ------------------------------------------------------------ function Get-Sites { if ($SiteCollectionUrl) { return @(Get-SPSite -Identity $SiteCollectionUrl) } return @(Get-SPSite -WebApplication $WebAppUrl -Limit All) } # ------------------------------------------------------------ # Risk Model (Threshold Based) # ------------------------------------------------------------ function Get-RiskLevel { param($ItemCount, $IsHidden) if ($IsHidden -eq $true) { return "Low" } if ($ItemCount -ge $ThresholdCritical) { return "High" } if ($ItemCount -ge $ThresholdWarning) { return "Medium" } return "Low" } function Get-Score { param($risk) switch ($risk) { "High" { return 30 } "Medium" { return 60 } "Low" { return 90 } default { return 50 } } } function Get-Recommendation { param($risk, $ItemCount) switch ($risk) { "High" { return "Exceeds threshold. Refactor list (index columns, archive data, split lists) before migration." } "Medium" { return "Approaching threshold. Review indexing, cleanup strategy, and validate migration performance." } "Low" { return "Within acceptable limits. Suitable for standard migration." } } } # ------------------------------------------------------------ # Main execution # ------------------------------------------------------------ $results = New-Object System.Collections.Generic.List[object] try { $sites = Get-Sites Log ("Resolved {0} site collection(s)." -f $sites.Count) } catch { Add-Error $WebAppUrl $_.Exception.Message throw } foreach ($site in $sites) { try { Log ("Scanning site collection: {0}" -f $site.Url) foreach ($web in $site.AllWebs) { try { foreach ($list in $web.Lists) { $itemCount = 0 $isHidden = $false try { $itemCount = [int]$list.ItemCount } catch {} try { $isHidden = [bool]$list.Hidden } catch {} if (-not $itemCount) { continue } $risk = Get-RiskLevel -ItemCount $itemCount -IsHidden $isHidden if ($risk -eq "Low") { continue } # focus only on impactful lists $results.Add([pscustomobject]@{ SiteCollectionUrl = $site.Url WebUrl = $web.Url ListTitle = $list.Title ListType = $list.BaseType TemplateId = $list.BaseTemplate ItemCount = $itemCount ThresholdWarning = $ThresholdWarning ThresholdCritical = $ThresholdCritical IsHidden = $isHidden HasUniquePermissions = $list.HasUniqueRoleAssignments LastModified = $list.LastItemModifiedDate DefaultViewUrl = $list.DefaultViewUrl RiskLevel = $risk Score = Get-Score $risk Category = "LargeLists" ActionRecommendation = Get-Recommendation -risk $risk -ItemCount $itemCount }) | Out-Null } } catch { Add-Error $web.Url $_.Exception.Message } finally { try { $web.Dispose() } catch {} } } try { $site.Dispose() } catch {} } catch { Add-Error $site.Url $_.Exception.Message } } # ------------------------------------------------------------ # Export # ------------------------------------------------------------ $results | Export-Csv -Path $OutputCsv -NoTypeInformation -Encoding UTF8 $results | Group-Object RiskLevel | ForEach-Object { [pscustomobject]@{ RiskLevel = $_.Name Count = $_.Count } } | Export-Csv -Path $summaryPath -NoTypeInformation -Encoding UTF8 $log | Set-Content $logPath if ($errors.Count -gt 0) { $errors | Export-Csv -Path $errorPath -NoTypeInformation Write-Host "ERROR REPORT: $errorPath" -ForegroundColor Yellow } Write-Host "" Write-Host ("DETAIL REPORT: {0}" -f $OutputCsv) -ForegroundColor Green Write-Host ("SUMMARY REPORT: {0}" -f $summaryPath) -ForegroundColor Green Write-Host ("RUN LOG: {0}" -f $logPath) -ForegroundColor Green Write-Host "" Write-Host "Complete." -ForegroundColor Green READ-ONLY. Identifies large SharePoint lists and libraries across a web application or site collection. Focus areas: - Item count thresholds (performance + migration risk) - Lists approaching or exceeding the 5,000 item threshold - Hidden/system lists flagged separately