Public/Restore-OrphanedSidBackup.ps1
|
function Restore-OrphanedSidBackup { <# .EXTERNALHELP OrphanedSid-Help.xml .SYNOPSIS Restores Active Directory ACLs from a backup folder written by Remove-OrphanedSid. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType('RestoreWorkflowResult')] param( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('DistinguishedName', 'SearchBase')] [string[]]$TargetObjectDN, [Parameter(Mandatory)] [ValidateScript({ if (-not (Test-Path -Path $_ -PathType Container)) { throw "Backup folder not found: $_" } $true })] [string]$BackupPath, [Parameter()] [switch]$Force, [Parameter()] [ValidateScript({ $directory = Split-Path -Parent $_ if (-not $directory) { $directory = (Get-Location).Path } if (-not (Test-Path -Path $directory -PathType Container)) { throw "The folder for -OutputPath does not exist: $directory" } $true })] [string]$OutputPath, [Parameter()] [string]$LogPath, [Parameter()] [string]$CorrelationId = [System.Guid]::NewGuid().ToString(), [Parameter()] [ValidateSet('Critical', 'Error', 'Warning', 'Information', 'Debug', 'Verbose')] [string]$LogLevel = 'Information', [Parameter()] [switch]$SuppressConsoleOutput ) begin { if ($Force) { $ConfirmPreference = 'None' } $sessionParams = @{ CommandName = $MyInvocation.MyCommand.Name LogPath = $LogPath CorrelationId = $CorrelationId LogLevel = $LogLevel LogLevelBound = $PSBoundParameters.ContainsKey('LogLevel') SuppressConsoleOutput = $SuppressConsoleOutput RequireActiveDirectory = $true } $null = Start-OrphanedSidSession @sessionParams $targets = [System.Collections.Generic.List[string]]::new() } process { foreach ($dn in $TargetObjectDN) { if (-not [string]::IsNullOrWhiteSpace($dn)) { $targets.Add($dn.Trim()) } } } end { try { $targetList = $targets -join ', ' if (-not $PSCmdlet.ShouldProcess($targetList, "Restore ACLs from backup folder $BackupPath")) { if (-not $WhatIfPreference) { Write-StructuredLog "Restore declined at the confirmation prompt for: $targetList" -Level Warning -Component 'Main' -CorrelationId $CorrelationId return } } $allResults = [System.Collections.Generic.List[object]]::new() foreach ($target in $targets) { Write-StructuredLog "Starting restore for: $target" -Level Information -Component 'Main' -CorrelationId $CorrelationId $restoreParameters = @{ TargetObjectDN = $target BackupPath = $BackupPath CorrelationId = $CorrelationId } if ($WhatIfPreference) { $restoreParameters.WhatIf = $true } try { $result = Start-OrchestrationWorkflow -OperationType 'Restore' -Parameters $restoreParameters -CorrelationId $CorrelationId if ($result) { $allResults.Add($result) } } catch { Write-StructuredLog "Restore failed for $target : $($_.Exception.Message)" -Level Error -Component 'Main' -CorrelationId $CorrelationId $allResults.Add([PSCustomObject]@{ PSTypeName = 'RestoreWorkflowResult' Success = $false TargetObjectDN = $target EntriesRestored = 0 ErrorMessage = $_.Exception.Message CorrelationId = $CorrelationId }) } } foreach ($result in $allResults) { Write-Output $result } $successful = @($allResults | Where-Object { $_.Success -eq $true }).Count $failed = @($allResults | Where-Object { $_.Success -ne $true }).Count $entries = ($allResults | Where-Object { $_.Success -eq $true -and $_.EntriesRestored } | Measure-Object -Property EntriesRestored -Sum).Sum Write-StructuredLog '=== RESTORE OPERATION SUMMARY ===' -Level Information -Component 'Summary' -CorrelationId $CorrelationId Write-StructuredLog "Total restore operations: $($allResults.Count)" -Level Information -Component 'Summary' -CorrelationId $CorrelationId Write-StructuredLog "Successful restores: $successful" -Level Information -Component 'Summary' -CorrelationId $CorrelationId Write-StructuredLog "Failed restores: $failed" -Level Information -Component 'Summary' -CorrelationId $CorrelationId if ($entries -gt 0) { Write-StructuredLog "Total ACL entries restored: $entries" -Level Information -Component 'Summary' -CorrelationId $CorrelationId } Write-StructuredLog "Backup source: $BackupPath" -Level Information -Component 'Summary' -CorrelationId $CorrelationId if ($OutputPath -and $allResults.Count -gt 0) { $allResults | Select-Object Success, TargetObjectDN, EntriesRestored, ErrorMessage, CorrelationId | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 Write-StructuredLog "Restore results exported to: $OutputPath" -Level Information -Component 'Summary' -CorrelationId $CorrelationId } } catch { Write-StructuredLog "Restore failed: $($_.Exception.Message)" -Level Error -Component 'Main' -CorrelationId $CorrelationId Write-Error -ErrorRecord $_ -ErrorAction Stop } finally { Stop-OrphanedSidSession -CorrelationId $CorrelationId } } } |