Public/Scan/Get-SPCInactiveSite.ps1
|
function Get-SPCInactiveSite { <# .SYNOPSIS Identifies inactive, dormant, or stale SharePoint Online sites across the tenant. .DESCRIPTION Get-SPCInactiveSite correlates Microsoft Graph Site Usage activity metrics across 30, 90, or 180-day reporting windows to discover dormant site collections. It identifies sites with no recent file or page activity, calculates potential monthly cost avoidance, and provides actionable governance recommendations (e.g., 'Review with Owner', 'Set ReadOnly', or 'Archive or Delete'). .PARAMETER InactiveDays The inactivity threshold in days. Sites with no recorded activity for longer than this value are flagged. Default is 180 days. Range: 30 to 1,825 days. .PARAMETER IncludeTeamSitesOnly Filters results to include only Microsoft 365 Group-connected Team Sites (Group-backed sites). .PARAMETER MinStorageMB Filters results to include only inactive sites consuming at least the specified storage capacity in megabytes. Default is 0 MB. .PARAMETER Top Limits output to the top N inactive sites with the largest storage consumption. Default is 0 (all matching sites). .INPUTS None. Does not accept pipeline input. .OUTPUTS SPC.InactiveSite Returns custom objects containing site URL, title, Group ID, template, storage used in MB, file count, last activity date, inactive days, lock state, owner UPN, monthly cost avoidance, and recommendation. .EXAMPLE Get-SPCInactiveSite -InactiveDays 180 Finds all tenant sites that have had no user activity in the last 180 days. .EXAMPLE Get-SPCInactiveSite -InactiveDays 365 -MinStorageMB 10240 -Top 20 Discovers the top 20 inactive sites that have been dormant for over a year and occupy at least 10 GB of storage. .EXAMPLE Get-SPCInactiveSite -IncludeTeamSitesOnly -InactiveDays 90 | Where-Object Recommendation -eq 'Set ReadOnly' | Export-SPCStorageReport -Format CSV -OutputPath 'C:\Governance' Finds inactive Team Sites dormant for 90+ days, filtering for sites recommended for ReadOnly locking, and exports to CSV. .NOTES Requires an active SPClean connection initialized via Connect-SPCTenant. Relies on Microsoft Graph Reports API (`Get-SPCGraphSiteUsageInternal`). .LINK Get-SPCStorageWaste Export-SPCStorageReport #> [CmdletBinding()] [OutputType('SPC.InactiveSite')] param( [Parameter()] [ValidateRange(30, 1825)] [int]$InactiveDays = 180, [Parameter()] [switch]$IncludeTeamSitesOnly, [Parameter()] [ValidateRange(0, [double]::MaxValue)] [double]$MinStorageMB = 0, [Parameter()] [ValidateRange(0, 10000)] [int]$Top = 0 ) begin { Test-SPCConnection Write-Verbose "Get-SPCInactiveSite: Querying Graph Site Usage..." $reportPeriod = if ($InactiveDays -gt 90) { 'D180' } elseif ($InactiveDays -gt 30) { 'D90' } else { 'D30' } $usageData = Get-SPCGraphSiteUsageInternal -Period $reportPeriod $inactiveSites = [System.Collections.Generic.List[PSCustomObject]]::new() } process { $now = (Get-Date).ToUniversalTime() if ($null -ne $usageData) { foreach ($row in $usageData) { $lastActivity = $row.LastActivityDate $daysInactive = if ($null -eq $lastActivity) { 9999 } else { [int]($now - $lastActivity.ToUniversalTime()).TotalDays } if ($daysInactive -ge $InactiveDays) { if ($IncludeTeamSitesOnly -and -not $row.IsTeamSite) { continue } if ($row.StorageUsedMB -lt $MinStorageMB) { continue } $costAvoidance = [Math]::Round((($row.StorageUsedMB / 1024) * 0.20), 2) $recommendation = if ($daysInactive -ge 365) { 'Archive or Delete' } elseif ($daysInactive -ge 180) { 'Set ReadOnly' } else { 'Review with Owner' } $record = [PSCustomObject][ordered]@{ PSTypeName = 'SPC.InactiveSite' SiteUrl = $row.SiteUrl SiteTitle = $row.SiteTitle GroupId = $row.GroupId Template = $row.Template StorageUsedMB = $row.StorageUsedMB FileCount = $row.FileCount LastActivityDate = $lastActivity InactiveDays = $daysInactive LockState = $row.LockState OwnerUPN = $row.OwnerUPN MonthlyCostAvoidanceUSD= $costAvoidance Recommendation = $recommendation } $inactiveSites.Add($record) } } } } end { $sorted = $inactiveSites | Sort-Object -Property StorageUsedMB -Descending if ($Top -gt 0) { $sorted | Select-Object -First $Top } else { $sorted } } } |