mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-05-18 11:46:18 +00:00
Add registry backup & restore (#566)
Starting from this commit, Win11Debloat will automatically create a registry backup every time the script is run. This registry backup can be used to revert any registry changes made by the script.
This commit is contained in:
145
Scripts/Features/RestoreRegistryBackup.ps1
Normal file
145
Scripts/Features/RestoreRegistryBackup.ps1
Normal file
@@ -0,0 +1,145 @@
|
||||
function Load-RegistryBackupFromFile {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$FilePath
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $FilePath)) {
|
||||
throw "Backup file was not found: $FilePath"
|
||||
}
|
||||
|
||||
try {
|
||||
$rawBackup = Get-Content -LiteralPath $FilePath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
throw "Failed to read backup file '$FilePath'. The file is not valid JSON."
|
||||
}
|
||||
|
||||
return Normalize-RegistryBackup -Backup $rawBackup
|
||||
}
|
||||
|
||||
function Normalize-RegistryBackup {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
$Backup
|
||||
)
|
||||
|
||||
$errors = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
if (-not $Backup.PSObject.Properties['Version']) {
|
||||
$errors.Add('Missing property: Version')
|
||||
}
|
||||
elseif ([string]$Backup.Version -ne '1.0') {
|
||||
$errors.Add("Unsupported backup version '$($Backup.Version)'.")
|
||||
}
|
||||
|
||||
if (-not $Backup.PSObject.Properties['BackupType']) {
|
||||
$errors.Add('Missing property: BackupType')
|
||||
}
|
||||
elseif ([string]$Backup.BackupType -ne 'RegistryState') {
|
||||
$errors.Add("Unsupported BackupType '$($Backup.BackupType)'.")
|
||||
}
|
||||
|
||||
$normalizedTarget = ''
|
||||
if (-not $Backup.PSObject.Properties['Target'] -or [string]::IsNullOrWhiteSpace([string]$Backup.Target)) {
|
||||
$errors.Add('Missing property: Target')
|
||||
}
|
||||
else {
|
||||
$normalizedTarget = [string]$Backup.Target
|
||||
|
||||
if ($normalizedTarget -eq 'DefaultUserProfile') {
|
||||
# Valid target format.
|
||||
}
|
||||
elseif ($normalizedTarget -like 'User:*') {
|
||||
$targetUserName = $normalizedTarget.Substring(5)
|
||||
$targetValidation = Test-TargetUserName -UserName $targetUserName
|
||||
if (-not $targetValidation.IsValid) {
|
||||
$errors.Add("Invalid user '$normalizedTarget'")
|
||||
}
|
||||
}
|
||||
elseif ($normalizedTarget -like 'CurrentUser:*') {
|
||||
$targetCurrentUserName = $normalizedTarget.Substring(12)
|
||||
if ([string]::IsNullOrWhiteSpace($targetCurrentUserName) -or ($targetCurrentUserName -ne $env:USERNAME)) {
|
||||
$errors.Add("Backup was made for '$targetCurrentUserName', this does not match current user '$env:USERNAME'.")
|
||||
}
|
||||
}
|
||||
else {
|
||||
$errors.Add("Unsupported Target '$normalizedTarget'.")
|
||||
}
|
||||
}
|
||||
|
||||
$registryKeys = @()
|
||||
if (-not $Backup.PSObject.Properties['RegistryKeys']) {
|
||||
$errors.Add('Missing property: RegistryKeys')
|
||||
}
|
||||
else {
|
||||
$registryKeys = @($Backup.RegistryKeys)
|
||||
}
|
||||
|
||||
$normalizedKeys = @()
|
||||
foreach ($keySnapshot in $registryKeys) {
|
||||
$normalizedKeys += @(Normalize-RegistryKeySnapshot -Snapshot $keySnapshot)
|
||||
}
|
||||
|
||||
$selectedFeatureParseResult = Get-NormalizedSelectedFeatureIdsFromBackup -Backup $Backup
|
||||
$selectedFeatures = @($selectedFeatureParseResult.SelectedFeatures)
|
||||
foreach ($selectedFeatureParseError in @($selectedFeatureParseResult.Errors)) {
|
||||
$errors.Add([string]$selectedFeatureParseError)
|
||||
}
|
||||
|
||||
$allowListValidationErrors = @(Test-RegistryBackupMatchesSelectedFeatures -SelectedFeatureIds @($selectedFeatures) -RegistryKeys @($normalizedKeys))
|
||||
foreach ($allowListValidationError in $allowListValidationErrors) {
|
||||
$errors.Add([string]$allowListValidationError)
|
||||
}
|
||||
|
||||
if ($errors.Count -gt 0) {
|
||||
Write-Error "Backup validation failed: $($errors -join ' ')"
|
||||
if ($errors.Count -eq 1) {
|
||||
throw ("Validation failed: $($errors[0])")
|
||||
}
|
||||
else {
|
||||
throw ("Validation failed with $($errors.Count) errors. See console output for details.")
|
||||
}
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Version = [string]$Backup.Version
|
||||
BackupType = [string]$Backup.BackupType
|
||||
CreatedAt = [string]$Backup.CreatedAt
|
||||
CreatedBy = [string]$Backup.CreatedBy
|
||||
ComputerName = [string]$Backup.ComputerName
|
||||
Target = $normalizedTarget
|
||||
SelectedFeatures = @($selectedFeatures)
|
||||
RegistryKeys = @($normalizedKeys)
|
||||
}
|
||||
}
|
||||
|
||||
function Restore-RegistryBackupState {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
$Backup
|
||||
)
|
||||
|
||||
$friendlyTarget = GetFriendlyRegistryBackupTarget -Target ([string]$Backup.Target)
|
||||
|
||||
$restoreAction = {
|
||||
param($normalizedBackup)
|
||||
|
||||
Write-Host "Applying registry restore from $(@($normalizedBackup.RegistryKeys).Count) root snapshot(s)."
|
||||
foreach ($rootSnapshot in @($normalizedBackup.RegistryKeys)) {
|
||||
Restore-RegistryKeySnapshot -Snapshot $rootSnapshot
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Starting restore for $friendlyTarget."
|
||||
|
||||
if ($Backup.Target -eq 'DefaultUserProfile' -or $Backup.Target -like 'User:*') {
|
||||
Write-Host "Restore requires loading target user hive."
|
||||
Invoke-WithLoadedRestoreHive -Target $Backup.Target -ScriptBlock $restoreAction -ArgumentObject $Backup
|
||||
Write-Host "Restore completed for $friendlyTarget."
|
||||
return
|
||||
}
|
||||
|
||||
& $restoreAction $Backup
|
||||
Write-Host "Restore completed for $friendlyTarget."
|
||||
}
|
||||
Reference in New Issue
Block a user