<# .SYNOPSIS Script 20 - Site Lock / Access State Report .DESCRIPTION READ-ONLY. Evaluates site collection access posture: - Read-only state - Write capability - Access model classification Used for: - Migration cutover readiness - Archive validation - Governance review .PARAMETER WebAppUrl Target SharePoint Web Application URL .PARAMETER OutputCsv Path to output CSV .PARAMETER SiteCollectionUrl Optional: Limit to one site .PARAMETER NoPrompt Skip confirmation #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$WebAppUrl, [Parameter(Mandatory = $true)] [string]$OutputCsv, [string]$SiteCollectionUrl, [switch]$NoPrompt ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" Write-Host "" Write-Host "SCRIPT 19 - SITE ACCESS / LOCK STATE REPORT" -ForegroundColor Cyan Write-Host "READ-ONLY. NO CHANGES ARE MADE." -ForegroundColor Green Write-Host "" # Load SharePoint snap-in if (-not (Get-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue)) { Add-PSSnapin Microsoft.SharePoint.PowerShell } # Output setup $outDir = Split-Path $OutputCsv -Parent if ([string]::IsNullOrWhiteSpace($outDir)) { throw "OutputCsv must be a full path" } if (!(Test-Path $outDir)) { New-Item $outDir -ItemType Directory -Force | Out-Null } $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $summaryPath = Join-Path $outDir "Summary_AccessState_$timestamp.csv" $logPath = Join-Path $outDir "RunLog_$timestamp.txt" $errorPath = Join-Path $outDir "Errors_$timestamp.csv" # Logging $log = @() function Log($msg){ $line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $msg" $log += $line Write-Host $line } # Prompt if (-not $NoPrompt) { $resp = Read-Host "Type YES to continue" if ($resp -ne "YES") { return } } # Helper: Site resolution function Get-Sites { if ($SiteCollectionUrl) { return @(Get-SPSite $SiteCollectionUrl) } return @(Get-SPSite -WebApplication $WebAppUrl -Limit All) } # Helper: Detect write capability function Test-WriteCapability { param($web) try { $testList = $web.Lists.Add("TempTestList_" + (Get-Random), "", 100) $web.Lists.Delete($testList) return $true } catch { return $false } } # Helper: Access classification function Get-AccessState($isReadOnly, $canWrite) { if ($isReadOnly -and -not $canWrite) { return "ReadOnly" } elseif (-not $canWrite) { return "RestrictedWrite" } else { return "Unlocked" } } # Helper: Risk function Get-RiskLevel($accessState) { switch ($accessState) { "ReadOnly" { return "Low" } "RestrictedWrite" { return "Medium" } "Unlocked" { return "High" } } } function Get-Recommendation($accessState) { switch ($accessState) { "ReadOnly" { return "Site is properly locked for migration or archive." } "RestrictedWrite" { return "Validate permissions; site may partially allow changes." } "Unlocked" { return "High risk. Lock site before migration or cutover." } } } # Execute $results = @() $errors = @() $sites = Get-Sites Log "Scanning $($sites.Count) site collections" foreach ($site in $sites) { $web = $null try { $web = $site.RootWeb Log "Evaluating: $($site.Url)" # Signals $isReadOnly = $site.ReadOnly $canWrite = Test-WriteCapability $web $accessState = Get-AccessState $isReadOnly $canWrite $risk = Get-RiskLevel $accessState $results += [pscustomobject]@{ SiteCollectionUrl = $site.Url Title = $web.Title ContentDB = $site.ContentDatabase.Name IsReadOnly = $isReadOnly CanWrite = $canWrite AccessState = $accessState RiskLevel = $risk Category = "SiteAccessState" Recommendation = (Get-Recommendation $accessState) } } catch { $errors += [pscustomobject]@{ Site = $site.Url Error = $_.Exception.Message } } finally { if ($web) { $web.Dispose() } $site.Dispose() } } # Export detail $results | Export-Csv $OutputCsv -NoTypeInformation -Encoding UTF8 # Export summary $results | Group-Object AccessState | ForEach-Object { [pscustomobject]@{ AccessState = $_.Name Count = $_.Count } } | Export-Csv $summaryPath -NoTypeInformation -Encoding UTF8 # Log + Errors $log | Set-Content $logPath if ($errors.Count -gt 0) { $errors | Export-Csv $errorPath -NoTypeInformation } 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 "COMPLETE" -ForegroundColor Green