#==================================================================================================
# Retention Exception Update + PHL Cleanup + Recycle Bin Cleanup (PnP)
#==================================================================================================
#------------------------------ CONFIG -------------------------------------
$PolicyName = "Document Retention Policy" # Purview retention policy name
$ExceptionsCsvPath = "E:\AReports\RP_Sites_DEL_2026_3.csv"
$SitesCsvPath = "E:\AReports\Sites_DEL_2026_3.csv"
$IPPSSessionUPN = "spadmin@spadmins.onmicrosoft.com"
$PnPClientId = "12a34567-f123-4567-890e-1cch23456789"
# Batch size for policy exception updates (helps manage large lists)
$PolicyBatchSize = 50
# PHL paging size
$PHLPageSize = 2000
# Recycle bin cleanup controls
$EnableRecycleBinCleanup = $true
$RecycleBinRowLimit = 0 # 0 = no limit; otherwise e.g. 10000 (PnP supports -RowLimit).
#------------------------------ SECTION 1: RETENTION POLICY EXCEPTIONS ---------------------------
Write-Host "=== Updating Retention Policy Exceptions ===" -ForegroundColor Cyan
# Load, normalize, and de-dupe site URLs from CSV
[array]$excludeSites = Import-Csv -Path $ExceptionsCsvPath |
Select-Object -ExpandProperty URL |
ForEach-Object { $_.Trim().TrimEnd("/") } |
Where-Object { $_ } |
Sort-Object -Unique
Write-Host "Loaded $($excludeSites.Count) exception site URLs from $ExceptionsCsvPath"
# Connect to Purview / Security & Compliance PowerShell
Connect-IPPSSession -UserPrincipalName $IPPSSessionUPN
# IMPORTANT:
# - Microsoft notes Set-RetentionCompliancePolicy triggers a full orgsync and recommends waiting for distribution between updates.
# - Static scoping limit: 100 SharePoint sites per retention policy when specifying sites.
# This script batches updates to reduce risk; adjust batching per your change control.
for ($i = 0; $i -lt $excludeSites.Count; $i += $PolicyBatchSize) {
$end = [Math]::Min($i + $PolicyBatchSize - 1, $excludeSites.Count - 1)
$batch = $excludeSites[$i..$end]
try {
Set-RetentionCompliancePolicy -Identity $PolicyName -AddSharePointLocationException $batch
Write-Host "Added exception batch: $($i+1) - $($end+1)" -ForegroundColor Green
}
catch {
Write-Host "FAILED adding exception batch: $($i+1) - $($end+1). Error: $($_.Exception.Message)" -ForegroundColor Red
}
}
#------------------------------ SECTION 2: PHL CLEANUP + RECYCLE BIN CLEANUP ---------------------
Write-Host "`n=== PHL Cleanup + Recycle Bin Cleanup ===" -ForegroundColor Cyan
$sites = Import-Csv -Path $SitesCsvPath
Write-Host "Loaded $($sites.Count) sites from $SitesCsvPath"
# Check if Clear-PnPRecycleBinItem exists in the current PnP.PowerShell install
# PnP docs indicate this cmdlet is available in the Nightly release.
$hasClearRecycleCmd = $null -ne (Get-Command Clear-PnPRecycleBinItem -ErrorAction SilentlyContinue)
foreach ($site in $sites) {
$siteUrl = $site.URL.Trim().TrimEnd("/")
if (-not $siteUrl) { continue }
Write-Host "'nProcessing site: $siteUrl" -ForegroundColor Yellow
# Connect to the site
try {
Connect-PnPOnline -Url $siteUrl -Interactive -ClientId $PnPClientId -ErrorAction Stop
}
catch {
Write-Host "Failed to connect to $siteUrl : $($_.Exception.Message)" -ForegroundColor Red
continue
}
# Check if Preservation Hold Library exists
$phl = Get-PnPList -Identity "Preservation Hold Library" -ErrorAction SilentlyContinue
if (-not $phl) {
Write-Host "No Preservation Hold Library found at $siteUrl" -ForegroundColor DarkGray
}
else {
Write-Host "Preservation Hold Library found at $siteUrl" -ForegroundColor Green
# Retrieve items in pages idle large libraries (PnP supports -PageSize and -ScriptBlock pattern).
$items = @()
try {
$items = Get-PnPListItem -List "Preservation Hold Library" -PageSize $PHLPageSize -ScriptBlock {
param($pagedItems)
$pagedItems.Context.ExecuteQuery()
}
}
catch {
Write-Host "Failed to list PHL items at $siteUrl : $($_.Exception.Message)" -ForegroundColor Red
$items = @()
}
if ($items.Count -gt 0) {
Write-Host "$($items.Count) items found in Preservation Hold Library" -ForegroundColor Cyan
foreach ($item in $items) {
try {
Remove-PnPListItem -List "Preservation Hold Library" -Identity $item.Id -Force -ErrorAction Stop
}
catch {
Write-Host "Failed to delete PHL item ID $($item.Id): $($_.Exception.Message)" -ForegroundColor DarkYellow
}
}
}
else {
Write-Host "No items found in Preservation Hold Library" -ForegroundColor DarkGray
}
}
# Clear Recycle Bins (1st + 2nd stage) to free storage immediately.
# Your internal recycle bin guidance notes both stages exist; second stage typically needs site collection admin.
if ($EnableRecycleBinCleanup) {
if (-not $hasClearRecycleCmd) {
Write-Host "Clear-PnPRecycleBinItem cmdlet not found. Per PnP docs, it may require PnP.PowerShell Nightly. Skipping recycle bin cleanup." -ForegroundColor DarkYellow
}
else {
try {
if ($RecycleBinRowLimit -gt 0) {
# -RowLimit supported by PnP.
Clear-PnPRecycleBinItem -All -Force -RowLimit $RecycleBinRowLimit
}
else {
# Clears all items; -Force suppresses confirmation.
Clear-PnPRecycleBinItem -All -Force
}
Write-Host "Recycle bins cleared for: $siteUrl" -ForegroundColor Green
}
catch {
Write-Host "Failed to clear recycle bins for $siteUrl : $($_.Exception.Message)" -ForegroundColor Red
}
}
}
}
Write-Host "`n=== DONE ===" -ForegroundColor Cyan
No comments:
Post a Comment