Update Folder Approval Statuses in SharePoint Online
This PnP PowerShell script provides comprehensive automation for managing content approval statuses across multiple folders in SharePoint Online document libraries. Perfect for bulk operations and maintaining consistent governance standards.
Purpose
Folder approval status management enables you to:
- Bulk approve SharePoint folders and their contents
- Standardize content approval workflows across libraries
- Automate compliance with organizational governance policies
- Maintain audit trails for content approval decisions
- Streamline document lifecycle management processes
Prerequisites
- PnP PowerShell module installed and updated
- Site Owner or Full Control permissions on target sites
- Content approval feature enabled on document libraries
- Active connection to your SharePoint Online tenant
- Understanding of your organization's approval workflows
PowerShell Script for Content Approval
# Error handling for the entire script
try {
# Connect to your SharePoint site
$ClientId = ""
Write-Host "Connecting to SharePoint..." -ForegroundColor Cyan
Connect-PnPOnline -Url ""https://tenantName.sharepoint.com/sites/siteName"" -Interactive -ClientId $ClientId
Write-Host "Successfully connected to SharePoint." -ForegroundColor Green
}
catch {
Write-Host "Failed to connect to SharePoint: $($_.Exception.Message)" -ForegroundColor Red
# Create error result for CSV
$errorResult = [PSCustomObject]@{
DateTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
FolderPath = "N/A"
FolderName = "N/A"
Status = "Connection Error"
Message = "Failed to connect to SharePoint: $($_.Exception.Message)"
PreviousStatus = "N/A"
}
# Export error to CSV
$csvFileName = "SharePoint_Folder_Approval_Results_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
$csvPath = Join-Path $PSScriptRoot $csvFileName
@($errorResult) | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
Write-Host "Error logged to: $csvPath" -ForegroundColor Yellow
exit 1
}
# Initialize array to store results for CSV output
$results = @()
Write-Host "Searching for folders with PENDING status (2) in Site Pages library..." -ForegroundColor Cyan
# Get ALL items from Site Pages library (including nested folders)
# _ModerationStatus: 0 = Approved, 1 = Rejected, 2 = Pending, 3 = Draft
try {
Write-Host "Getting ALL items from Site Pages library to find nested folders..." -ForegroundColor Cyan
# Get ALL items with a high page size to ensure we get everything including subfolders
$allItems = Get-PnPListItem -List "Site Pages" -PageSize 2000
Write-Host "Retrieved $($allItems.Count) total items from Site Pages library." -ForegroundColor Green
# Filter to only folders (including nested ones) - use FileSystemObjectType as primary method
$allFolders = $allItems | Where-Object { $_.FileSystemObjectType -eq "Folder" }
Write-Host "Found $($allFolders.Count) folders using FileSystemObjectType detection." -ForegroundColor Cyan
# Show all folders we found with their paths
Write-Host "`nAll folders found:" -ForegroundColor Yellow
foreach ($folder in $allFolders) {
$folderName = $folder["FileLeafRef"]
$folderPath = $folder["FileRef"]
$moderationStatus = $folder["_ModerationStatus"]
Write-Host " - '$folderName'" -ForegroundColor White
Write-Host " Path: '$folderPath' | Status: $moderationStatus" -ForegroundColor Gray
}
# Filter for folders with PENDING status (2) using PowerShell
$foldersToApprove = $allFolders | Where-Object { $_["_ModerationStatus"] -eq 2 }
Write-Host "`nFound $($foldersToApprove.Count) folders with PENDING status that need approval." -ForegroundColor Green
# Show what we found that needs approval
if ($foldersToApprove.Count -gt 0) {
Write-Host "`nFolders with PENDING status (will be approved):" -ForegroundColor Red
foreach ($folder in $foldersToApprove) {
$folderName = $folder["FileLeafRef"]
$folderPath = $folder["FileRef"]
$moderationStatus = $folder["_ModerationStatus"]
Write-Host " - '$folderName' | Path: '$folderPath' | Status: $moderationStatus" -ForegroundColor White
}
}
}
catch {
Write-Host "Failed to query Site Pages library: $($_.Exception.Message)" -ForegroundColor Red
# Create error result for CSV
$result = [PSCustomObject]@{
DateTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
FolderPath = "N/A"
FolderName = "N/A"
Status = "Query Error"
Message = "Failed to query Site Pages library: $($_.Exception.Message)"
PreviousStatus = "N/A"
}
$results += $result
# Export error and exit
$csvFileName = "SharePoint_Folder_Approval_Results_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
$csvPath = Join-Path $PSScriptRoot $csvFileName
$results | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
Write-Host "Error logged to: $csvPath" -ForegroundColor Yellow
exit 1
}
if ($foldersToApprove.Count -eq 0) {
Write-Host "No folders found that require approval." -ForegroundColor Green
# Still create a CSV to document the run
$result = [PSCustomObject]@{
DateTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
FolderPath = "N/A"
FolderName = "N/A"
Status = "No Action Required"
Message = "All folders already approved"
PreviousStatus = "N/A"
}
$results += $result
}
else {
Write-Host "`nProcessing $($foldersToApprove.Count) folder(s) that need approval..." -ForegroundColor Yellow
# Loop through each folder and approve it
foreach ($folder in $foldersToApprove) {
# Initialize variables to avoid scope issues
$folderName = "Unknown"
$folderPath = "Unknown"
$currentStatus = "Unknown"
# Create result object for this folder
$result = [PSCustomObject]@{
DateTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
FolderPath = ""
FolderName = ""
Status = ""
Message = ""
PreviousStatus = ""
}
try {
# Safely extract folder properties with null checks
$folderName = if ($folder["FileLeafRef"]) { $folder["FileLeafRef"] } else { "Unknown" }
$folderPath = if ($folder["FileRef"]) { $folder["FileRef"] } else { "Unknown" }
$currentStatus = if ($null -ne $folder["_ModerationStatus"]) { $folder["_ModerationStatus"] } else { "Unknown" }
# Update result object with extracted values
$result.FolderPath = $folderPath
$result.FolderName = $folderName
$result.PreviousStatus = $currentStatus
Write-Host "Processing folder: '$folderName' (Current status: $currentStatus)"
# Approve the folder
$folder["_ModerationStatus"] = 0 # 0 = Approved
$folder.Update()
Invoke-PnPQuery
Write-Host "✓ Folder '$folderName' approved successfully" -ForegroundColor Green
$result.Status = "Success"
$result.Message = "Folder approved successfully (was status: $currentStatus)"
}
catch {
$errorMessage = $_.Exception.Message
Write-Host "✗ Failed to approve folder '$folderName': $errorMessage" -ForegroundColor Red
$result.FolderPath = $folderPath
$result.FolderName = $folderName
$result.PreviousStatus = $currentStatus
$result.Status = "Error"
$result.Message = "Failed to approve: $errorMessage"
}
# Add result to array
$results += $result
}
}
# Generate CSV output file
$csvFileName = "SharePoint_Folder_Approval_Results_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
$csvPath = Join-Path $PSScriptRoot $csvFileName
try {
$results | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
Write-Host "`nCSV results exported to: $csvPath" -ForegroundColor Cyan
}
catch {
Write-Host "`nFailed to export CSV: $($_.Exception.Message)" -ForegroundColor Red
}
Write-Host "`nFolder approval process completed."
Usage Notes
- Always test with a small subset of folders before running bulk operations
- Verify content approval is enabled on target libraries before execution
- Consider email notifications - approved/rejected items may trigger workflows
- Monitor SharePoint usage limits when processing large numbers of items
- Use appropriate approval comments for audit trail purposes
- Schedule regular approval status reports for governance compliance
Best Practices
- Implement approval workflows that align with organizational policies
- Document approval criteria and decision rationale
- Set up automated notifications for stakeholders
- Regular reviews of pending items to prevent bottlenecks
- Use version history to track approval decision changes