mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-05-18 19:56:25 +00:00
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.
146 lines
4.9 KiB
PowerShell
146 lines
4.9 KiB
PowerShell
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."
|
|
}
|