public/Clear-PSProfileSnapshotOrphans.ps1
|
<# .SYNOPSIS Removes orphaned snapshot files that lack their corresponding backup file. .DESCRIPTION Cleans up hash, metadata, and pre-restore safety backup files that no longer have an associated snapshot backup file. This can happen if backup files are manually deleted or due to incomplete removal operations. Pre-restore files (.backup.pre-restore) are safety backups created before restoration operations. Orphaned pre-restore files are those whose corresponding snapshot no longer exists. .INPUTS None .OUTPUTS None .EXAMPLE Clear-PSProfileSnapshotOrphans Removes all orphaned snapshot files (hash, metadata, and pre-restore backups). .NOTES This is a maintenance function and should be run periodically if manual deletions occur. Pre-restore files are automatically cleaned up if their corresponding snapshot backups no longer exist. #> function Clear-PSProfileSnapshotOrphans { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param() try { $profilesPath = Split-Path -Path $profile -Parent # Get all orphan candidate files $hashFiles = Get-ChildItem -Path $profilesPath -Filter "*.hash" -ErrorAction Stop $metadataFiles = Get-ChildItem -Path $profilesPath -Filter "*.metadata" -ErrorAction Stop $preRestoreFiles = Get-ChildItem -Path $profilesPath -Filter "*.backup.pre-restore" -ErrorAction Stop $backupFiles = Get-ChildItem -Path $profilesPath -Filter "*.backup" -ErrorAction Stop if (-not $hashFiles -and -not $metadataFiles -and -not $preRestoreFiles) { Write-Host "✅ No orphaned files found." -ForegroundColor Green return } # Extract all GUIDs from backup files for quick lookup (exclude pre-restore files) $backupGUIDs = @{} foreach ($backupFile in $backupFiles) { # Skip pre-restore files when building the backup GUID list if ($backupFile.Name -notlike "*.backup.pre-restore") { $guid = $backupFile.BaseName -replace '^.*_([a-f0-9\-]+)$', '$1' $backupGUIDs[$guid] = $true } } # Find orphaned hash files $orphanedHashes = @() foreach ($hashFile in $hashFiles) { $guid = $hashFile.BaseName -replace '^.*_([a-f0-9\-]+)$', '$1' if (-not $backupGUIDs.ContainsKey($guid)) { $orphanedHashes += $hashFile } } # Find orphaned metadata files $orphanedMetadata = @() foreach ($metadataFile in $metadataFiles) { $guid = $metadataFile.BaseName -replace '^.*_([a-f0-9\-]+)$', '$1' if (-not $backupGUIDs.ContainsKey($guid)) { $orphanedMetadata += $metadataFile } } # Find orphaned pre-restore files $orphanedPreRestore = @() foreach ($preRestoreFile in $preRestoreFiles) { # Extract GUID from pre-restore filename (format: profile_GUID.backup.pre-restore) $guid = $preRestoreFile.BaseName -replace '^.*_([a-f0-9\-]+)\.backup$', '$1' if (-not $backupGUIDs.ContainsKey($guid)) { $orphanedPreRestore += $preRestoreFile } } $totalOrphaned = $orphanedHashes.Count + $orphanedMetadata.Count + $orphanedPreRestore.Count if ($totalOrphaned -eq 0) { Write-Host "✅ No orphaned files found." -ForegroundColor Green return } $orphanSummary = @() if ($orphanedHashes.Count -gt 0) { $orphanSummary += "$($orphanedHashes.Count) hash file(s)" } if ($orphanedMetadata.Count -gt 0) { $orphanSummary += "$($orphanedMetadata.Count) metadata file(s)" } if ($orphanedPreRestore.Count -gt 0) { $orphanSummary += "$($orphanedPreRestore.Count) pre-restore file(s)" } Write-Host "Found $($orphanSummary -join ', ')" -ForegroundColor Yellow # Remove orphaned files $removed = 0 foreach ($hashFile in $orphanedHashes) { if ($PSCmdlet.ShouldProcess($hashFile.Name, 'Remove orphaned hash file')) { try { Remove-Item -Path $hashFile.FullName -Force -ErrorAction Stop $removed++ Write-Host "✅ Removed: $($hashFile.Name)" -ForegroundColor Green } catch { Write-Error "❌ Could not remove: $($hashFile.Name)" } } } foreach ($metadataFile in $orphanedMetadata) { if ($PSCmdlet.ShouldProcess($metadataFile.Name, 'Remove orphaned metadata file')) { try { Remove-Item -Path $metadataFile.FullName -Force -ErrorAction Stop $removed++ Write-Host "✅ Removed: $($metadataFile.Name)" -ForegroundColor Green } catch { Write-Error "❌ Could not remove: $($metadataFile.Name)" } } } foreach ($preRestoreFile in $orphanedPreRestore) { if ($PSCmdlet.ShouldProcess($preRestoreFile.Name, 'Remove orphaned pre-restore file')) { try { Remove-Item -Path $preRestoreFile.FullName -Force -ErrorAction Stop $removed++ Write-Host "✅ Removed: $($preRestoreFile.Name)" -ForegroundColor Green } catch { Write-Error "❌ Could not remove: $($preRestoreFile.Name)" } } } Write-Host "`n✅ Cleanup complete. Removed $removed orphaned file(s)." -ForegroundColor Green } catch { Write-Error "❌ An error occurred while cleaning up orphaned files." throw $_.Exception.Message } } |