Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Scripts/AppRemoval/RemoveApps.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ function RemoveApps {
$appslist
)

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
foreach ($app in $appslist) {
Write-Host "[WhatIf] Remove App Package: $app" -ForegroundColor Cyan
}
return
}

# Determine target from script-level params, defaulting to AllUsers
$targetUser = GetTargetUserForAppRemoval

Expand Down
12 changes: 12 additions & 0 deletions Scripts/Features/DisableStoreSearchSuggestions.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ function DisableStoreSearchSuggestions {

$userName = [regex]::Match($StoreAppsDatabase, '(?:Users\\)([^\\]+)(?:\\AppData)').Groups[1].Value

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Disable Microsoft Store search suggestions for user $userName by restricting access to store.db" -ForegroundColor Cyan
return
}

# This file doesn't exist in EEA (No Store app suggestions).
if (-not (Test-Path -Path $StoreAppsDatabase))
{
Expand Down Expand Up @@ -79,6 +85,12 @@ function EnableStoreSearchSuggestions {
$userName = [regex]::Match($StoreAppsDatabase, '(?:Users\\)([^\\]+)(?:\\AppData)').Groups[1].Value
if (-not $userName) { $userName = '<unknown>' }

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Re-enable Microsoft Store search suggestions for user $userName by restoring access to store.db" -ForegroundColor Cyan
return
}

if (-not (Test-Path -Path $StoreAppsDatabase)) {
Write-Host "Store app database not found for user $userName, nothing to undo"
return
Expand Down
45 changes: 30 additions & 15 deletions Scripts/Features/ExecuteChanges.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ function ExecuteParameter {
'DisableWidgets' {
Write-Host "> $($feature.ApplyText)..."
# Stop widgets related processes before removing the app packages to prevent potential issues
Get-Process *Widget* -ErrorAction SilentlyContinue | Stop-Process
$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if (-not $isWhatIf) {
Get-Process *Widget* -ErrorAction SilentlyContinue | Stop-Process
}

RemoveApps @('Microsoft.StartExperiencesApp','MicrosoftWindows.Client.WebExperience','Microsoft.WidgetsPlatformRuntime')
}
Expand Down Expand Up @@ -172,24 +175,30 @@ function ExecuteAllChanges {
if ($script:Params.ContainsKey("CreateRestorePoint")) { $totalSteps++ }
$currentStep = 0

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($hasRegistryBackedFeature) {
$currentStep++
if ($script:ApplyProgressCallback) {
& $script:ApplyProgressCallback $currentStep $totalSteps "Creating registry backup..."
}

Write-Host "> Creating registry backup..."
try {
$undoSyntheticFeatures = @($script:UndoParams.Keys | ForEach-Object {
$f = if ($script:Features.ContainsKey($_)) { $script:Features[$_] } else { $null }
if ($f -and $f.RegistryUndoKey) {
[PSCustomObject]@{ FeatureId = $_; RegistryKey = (Resolve-UndoRegFilePath $f.RegistryUndoKey) }
}
} | Where-Object { $_ })
New-RegistrySettingsBackup -ActionableKeys $actionableKeys -ExtraFeatures $undoSyntheticFeatures | Out-Null
if ($isWhatIf) {
Write-Host "[WhatIf] Create registry backup" -ForegroundColor Cyan
}
catch {
throw "Registry backup failed before applying changes. $($_.Exception.Message)"
else {
Write-Host "> Creating registry backup..."
try {
$undoSyntheticFeatures = @($script:UndoParams.Keys | ForEach-Object {
$f = if ($script:Features.ContainsKey($_)) { $script:Features[$_] } else { $null }
if ($f -and $f.RegistryUndoKey) {
[PSCustomObject]@{ FeatureId = $_; RegistryKey = (Resolve-UndoRegFilePath $f.RegistryUndoKey) }
}
} | Where-Object { $_ })
New-RegistrySettingsBackup -ActionableKeys $actionableKeys -ExtraFeatures $undoSyntheticFeatures | Out-Null
}
catch {
throw "Registry backup failed before applying changes. $($_.Exception.Message)"
}
}
}

Expand All @@ -199,9 +208,15 @@ function ExecuteAllChanges {
if ($script:ApplyProgressCallback) {
& $script:ApplyProgressCallback $currentStep $totalSteps "Creating system restore point, this may take a moment..."
}
Write-Host "> Creating a system restore point..."
CreateSystemRestorePoint
Write-Host ""
if ($isWhatIf) {
Write-Host "[WhatIf] Create system restore point" -ForegroundColor Cyan
Write-Host ""
}
else {
Write-Host "> Creating a system restore point..."
CreateSystemRestorePoint
Write-Host ""
}
}

# Execute all parameters
Expand Down
8 changes: 8 additions & 0 deletions Scripts/Features/ImportRegistryFile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ function ImportRegistryFile {
$importScript = {
param($targetRegFilePath, $hiveContext)

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Previewing registry changes for $path:" -ForegroundColor Cyan

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In PowerShell, does using "$path:" inside a double-quoted string produce an InvalidVariableReferenceWithDrive parse error, and is "${path}:" the correct fix?

💡 Result:

Yes, placing a colon (:) immediately after a variable name inside a double-quoted string in PowerShell causes a parse error, typically identified by the error ID InvalidVariableReferenceWithDrive [1][2][3]. This happens because PowerShell's parser interprets the dollar sign, the variable name, and the immediately following colon as a namespace variable reference (or drive-qualified reference), such as the notation used for environment variables (e.g., $env:PATH) [1][4]. When the characters following the colon do not form a valid path or item name in that context, the parser throws an error because it expects a valid drive-qualified reference [1][2][5]. Using curly braces to delimit the variable name, as in "${path}:", is the correct and standard fix [6][1][2]. This syntax explicitly tells the PowerShell parser that the variable name is "$path" and that the colon is a literal character following the variable, rather than part of the variable reference itself [6][1][5]. Top results: [6][1][2]

Citations:


🏁 Script executed:

# First, check if the file exists and examine it
fd -t f "ImportRegistryFile.ps1" --exec cat -n {} \;

Repository: Raphire/Win11Debloat

Length of output: 5268


Fix invalid variable interpolation in string formatting.

Line 26 uses $path: inside a double-quoted string, which PowerShell parses as an invalid drive-qualified variable reference, triggering an InvalidVariableReferenceWithDrive error. Use ${path}: instead to explicitly delimit the variable name from the following literal colon.

Proposed fix
-            Write-Host "[WhatIf] Previewing registry changes for $path:" -ForegroundColor Cyan
+            Write-Host "[WhatIf] Previewing registry changes for ${path}:" -ForegroundColor Cyan
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Write-Host "[WhatIf] Previewing registry changes for $path:" -ForegroundColor Cyan
Write-Host "[WhatIf] Previewing registry changes for ${path}:" -ForegroundColor Cyan
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[error] 26-26: Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to delimit the name.

(InvalidVariableReferenceWithDrive)


[warning] Missing BOM encoding for non-ASCII encoded file 'ImportRegistryFile.ps1'

(PSUseBOMForUnicodeEncodedFile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Scripts/Features/ImportRegistryFile.ps1` at line 26, In the Write-Host
command where the message contains `$path:`, PowerShell is incorrectly
interpreting the colon as part of a drive-qualified variable reference. Fix this
by explicitly delimiting the variable name using curly braces, changing `$path:`
to `${path}:` within the double-quoted string so the colon is treated as a
literal character rather than part of the variable syntax.

Source: Linters/SAST tools

Invoke-RegistryOperationsFromRegFile -RegFilePath $targetRegFilePath
Write-Host ""
return
}

# When the target user's hive is already loaded under their SID, the .reg file's
# HKEY_USERS\Default paths won't match. Use the PowerShell registry writer instead,
# which remaps Default → SID via Split-RegistryPath.
Expand Down
22 changes: 22 additions & 0 deletions Scripts/Features/ReplaceStartMenu.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ function ReplaceStartMenuForAllUsers {
# Also replace the start menu file for the default user profile
$defaultStartMenuPath = GetUserDirectory -userName "Default" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState" -exitIfPathNotFound $false

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Replace Start Menu for Default user profile with template $startMenuTemplate" -ForegroundColor Cyan
return
}

# Create folder if it doesn't exist
if (-not (Test-Path $defaultStartMenuPath)) {
new-item $defaultStartMenuPath -ItemType Directory -Force | Out-Null
Expand Down Expand Up @@ -61,6 +67,12 @@ function ReplaceStartMenu {

$userName = GetStartMenuUserNameFromPath -StartMenuBinFile $startMenuBinFile

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Replace Start Menu for user $userName with template $startMenuTemplate" -ForegroundColor Cyan
return
}

$backupBinFile = $startMenuBinFile + ".bak"

if (Test-Path $startMenuBinFile) {
Expand Down Expand Up @@ -121,6 +133,16 @@ function RestoreStartMenuFromBackup {
}
$currentBinBackup = $StartMenuBinFile + '.restore.bak'

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Restore start menu for user $userName from backup $backupBinFile" -ForegroundColor Cyan
return [PSCustomObject]@{
UserName = $userName
Result = $true
Message = "[WhatIf] Restored start menu for user $userName."
}
}

if (-not (Test-Path -LiteralPath $backupBinFile)) {
return [PSCustomObject]@{
UserName = $userName
Expand Down
6 changes: 6 additions & 0 deletions Scripts/Features/RestartExplorer.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ function RestartExplorer {
return
}

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Restart the Windows Explorer process" -ForegroundColor Cyan
return
}

Write-Host "> Attempting to restart the Windows Explorer process to apply all changes..."

if ($script:Params.ContainsKey("NoRestartExplorer")) {
Expand Down
9 changes: 8 additions & 1 deletion Scripts/Features/RestoreRegistryBackup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ function Restore-RegistryBackupState {

$friendlyTarget = GetFriendlyRegistryBackupTarget -Target ([string]$Backup.Target)

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Restore registry backup for $friendlyTarget" -ForegroundColor Cyan
return [PSCustomObject]@{ Result = $true }
}
Comment on lines +136 to +140

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

WhatIf handler should return a result indicator for consistency.

The function returns void during WhatIf mode, but the caller in Show-RestoreBackupWindow.ps1 unconditionally sets RestoredRegistry = $true after invoking this function (lines 24-34 of the context snippet). This means the GUI will incorrectly report that the registry was restored even when WhatIf prevented the operation.

According to the review stack context, RestoreStartMenuFromBackup returns PSCustomObject with Result=$true during WhatIf to signal preview completion. This function should follow the same pattern for consistency and correct downstream behavior.

Proposed fix to return a result indicator
     $isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
     if ($isWhatIf) {
         Write-Host "[WhatIf] Restore registry backup for $friendlyTarget" -ForegroundColor Cyan
-        return
+        return [PSCustomObject]@{ Result = $true }
     }

Then update the caller in Show-RestoreBackupWindow.ps1 to check the result:

-        Restore-RegistryBackupState -Backup $backup
-        $restoreResult.RestoredRegistry = $true
+        $restoreOpResult = Restore-RegistryBackupState -Backup $backup
+        if ($restoreOpResult -and $restoreOpResult.Result) {
+            $restoreResult.RestoredRegistry = $true
+        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Scripts/Features/RestoreRegistryBackup.ps1` around lines 136 - 140, The
WhatIf handler in the RestoreRegistryBackup function returns void, but the
caller in Show-RestoreBackupWindow.ps1 unconditionally sets RestoredRegistry to
true after invocation, causing incorrect GUI state when WhatIf mode prevents the
actual restore. Modify the WhatIf handler block to return a PSCustomObject with
a Result property set to true (following the same pattern used by
RestoreStartMenuFromBackup) instead of using a bare return statement. This
allows the caller to check the returned result object before updating the
RestoredRegistry flag.


$restoreAction = {
param($normalizedBackup)

Expand All @@ -148,9 +154,10 @@ function Restore-RegistryBackupState {
Write-Host "Restore requires loading target user hive."
Invoke-WithLoadedRestoreHive -Target $Backup.Target -ScriptBlock $restoreAction -ArgumentObject $Backup
Write-Host "Restore completed for $friendlyTarget."
return
return [PSCustomObject]@{ Result = $true }
}

& $restoreAction $Backup
Write-Host "Restore completed for $friendlyTarget."
return [PSCustomObject]@{ Result = $true }
}
6 changes: 6 additions & 0 deletions Scripts/FileIO/SaveCustomAppsListToFile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ function SaveCustomAppsListToFile {
$appsList
)

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Save custom apps list to file" -ForegroundColor Cyan
return
}

$script:SelectedApps = $appsList

# Create file that stores selected apps if it doesn't exist
Expand Down
6 changes: 6 additions & 0 deletions Scripts/FileIO/SaveSettings.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Saves the current settings, excluding control parameters, to 'LastUsedSettings.json' file
function SaveSettings {
$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Save settings to LastUsedSettings.json" -ForegroundColor Cyan
return
}

$settings = @{
"Version" = "1.0"
"Settings" = @()
Expand Down
7 changes: 7 additions & 0 deletions Scripts/GUI/Show-ConfigWindow.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,13 @@ function Export-Configuration {

Write-Host "Exporting configuration to '$($saveDialog.FileName)'... (Categories: $($categories -join ', '))"

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Export configuration to '$($saveDialog.FileName)'" -ForegroundColor Cyan
Show-MessageBox -Message "[WhatIf] Configuration would be exported to this file (no file written)." -Title 'Export Configuration' -Button 'OK' -Icon 'Information' | Out-Null
return
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (SaveToFile -Config $config -FilePath $saveDialog.FileName) {
Write-Host "Configuration exported successfully: $($saveDialog.FileName)"
Show-MessageBox -Message "Configuration exported successfully." -Title 'Export Configuration' -Button 'OK' -Icon 'Information' | Out-Null
Expand Down
14 changes: 11 additions & 3 deletions Scripts/GUI/Show-RestoreBackupWindow.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,17 @@ function Show-RestoreBackupWindow {
}

Write-Host "User confirmed registry restore for $($backup.Target)."
Restore-RegistryBackupState -Backup $backup
$restoreResult.RestoredRegistry = $true
$successMessage = 'Registry backup restored successfully. Some changes may require a restart to take effect.'
$restoreOpResult = Restore-RegistryBackupState -Backup $backup
if ($restoreOpResult -and $restoreOpResult.Result) {
$restoreResult.RestoredRegistry = $true
$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
$successMessage = '[WhatIf] Registry backup would be restored (no changes made).'
}
else {
$successMessage = 'Registry backup restored successfully. Some changes may require a restart to take effect.'
}
}
}
elseif ($dialogResult.Result -eq 'RestoreStartMenu') {
$scope = $dialogResult.StartMenuScope
Expand Down
57 changes: 48 additions & 9 deletions Scripts/Helpers/ApplyRegistryRegFile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,21 @@ function Invoke-RegistryDeleteValueOperation {
$KeyInfo
)

$valueName = Get-NormalizedRegistryValueName -ValueName $Operation.ValueName
$displayValueName = if ([string]::IsNullOrEmpty($valueName)) { '(Default)' } else { $valueName }

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
Write-Host "[WhatIf] Remove Registry Value: $($Operation.KeyPath) \ $displayValueName" -ForegroundColor Cyan
return
}

if ($null -eq $KeyInfo.Key) {
$valueName = Get-NormalizedRegistryValueName -ValueName $Operation.ValueName
$displayValueName = if ([string]::IsNullOrEmpty($valueName)) { '(Default)' } else { $valueName }
Write-Verbose "Unable to find or open key '$($Operation.KeyPath)' and value '$displayValueName'"
return
}

try {
$valueName = Get-NormalizedRegistryValueName -ValueName $Operation.ValueName
$KeyInfo.Key.DeleteValue($valueName, $false)
}
finally {
Expand All @@ -127,12 +133,23 @@ function Invoke-RegistrySetValueOperation {
$KeyInfo
)

$setArgs = Convert-RegOperationToValueKind -Operation $Operation
$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
$displayVal = if ($setArgs.Kind -eq [Microsoft.Win32.RegistryValueKind]::Binary) {
"Binary data ($($setArgs.Value.Length) bytes)"
} else {
$setArgs.Value
}
Write-Host "[WhatIf] Set Registry Value: $($Operation.KeyPath) \ $($setArgs.Name) = $displayVal ($($setArgs.Kind))" -ForegroundColor Cyan
return
}

if ($null -eq $KeyInfo.Key) {
throw [System.UnauthorizedAccessException]::new("Unable to open or create registry key '$($Operation.KeyPath)'")
}

try {
$setArgs = Convert-RegOperationToValueKind -Operation $Operation
$KeyInfo.Key.SetValue($setArgs.Name, $setArgs.Value, $setArgs.Kind)
}
finally {
Expand Down Expand Up @@ -173,6 +190,25 @@ function Invoke-RegistryOperation {
$isSetValueOperation = $operationType -eq 'SetValue'
$isDeleteKeyOperation = $operationType -eq 'DeleteKey'

$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")
if ($isWhatIf) {
switch ($operationType) {
'DeleteKey' {
Write-Host "[WhatIf] Remove Registry Key (Tree): $($Operation.KeyPath)" -ForegroundColor Cyan
}
'DeleteValue' {
Invoke-RegistryDeleteValueOperation -Operation $Operation -KeyInfo $null
}
'SetValue' {
Invoke-RegistrySetValueOperation -Operation $Operation -KeyInfo $null
}
default {
throw "Unsupported reg operation type '$($Operation.OperationType)' in '$RegFilePath'"
}
}
return
}

$keyInfo = Get-RegistryKeyForOperation -RegistryPath $Operation.KeyPath -CreateIfMissing:$isSetValueOperation -OpenKey:(-not $isDeleteKeyOperation)

switch ($operationType) {
Expand Down Expand Up @@ -202,6 +238,7 @@ function Invoke-RegistryOperationsFromRegFile {
$accessDeniedCount = 0
$operations = @(Get-RegFileOperations -regFilePath $RegFilePath)
$totalOperations = $operations.Count
$isWhatIf = $null -ne $script:Params -and $script:Params.ContainsKey("WhatIf")

foreach ($operation in $operations) {
try {
Expand All @@ -213,11 +250,13 @@ function Invoke-RegistryOperationsFromRegFile {
}
}

if ($totalOperations -gt 0 -and $accessDeniedCount -eq $totalOperations) {
throw "Registry fallback import could not apply any operations in '$RegFilePath' because all $accessDeniedCount operation(s) were blocked by access restrictions."
}
if (-not $isWhatIf) {
if ($totalOperations -gt 0 -and $accessDeniedCount -eq $totalOperations) {
throw "Registry fallback import could not apply any operations in '$RegFilePath' because all $accessDeniedCount operation(s) were blocked by access restrictions."
}

if ($accessDeniedCount -gt 0) {
Write-Warning "Registry fallback import completed with $accessDeniedCount access-restricted operation(s) skipped in '$RegFilePath'."
if ($accessDeniedCount -gt 0) {
Write-Warning "Registry fallback import completed with $accessDeniedCount access-restricted operation(s) skipped in '$RegFilePath'."
}
}
}
1 change: 1 addition & 0 deletions Scripts/Helpers/RegistryPathHelpers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ function Get-RegistryFilePathForFeature {

return Join-Path $script:RegfilesPath $RegistryKey
}

11 changes: 11 additions & 0 deletions Win11Debloat.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,17 @@ else {
Start-Transcript -Path $script:DefaultLogPath -Append -IncludeInvocationHeader -Force | Out-Null
}

# Check if the device is domain-joined and warn the user (Group Policy may override changes)
try {
$computerSystem = Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue
if ($null -ne $computerSystem -and $computerSystem.PartOfDomain) {
Write-Warning "This machine is domain-joined. Group Policy may override changes made by Win11Debloat."
}
}
catch {
# Suppress WMI/CIM query and null reference errors to ensure the script does not crash on startup
}
Comment on lines +230 to +232

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

PowerShell best practice for catch blocks in non-critical startup checks: is Write-Verbose in catch preferred over an empty catch?

💡 Result:

In PowerShell, using an empty catch block is considered a poor design practice and is explicitly flagged as a warning by the PSScriptAnalyzer rule AvoidUsingEmptyCatchBlock [1]. Empty catch blocks swallow errors, which makes debugging significantly more difficult because the script provides no feedback that a failure occurred [2][3]. For non-critical startup checks, using Write-Verbose is a significantly better practice than an empty catch block because it provides visibility into the failure without cluttering the output stream for normal users [4][2]. However, the most robust approach for production-grade scripts is to ensure failures are logged properly [2][3]. Best practices for error handling in these scenarios include: 1. Avoid Silent Failures: Never leave a catch block empty [2][3][5]. At a minimum, you should log the error using Write-Verbose, Write-Warning, or an appropriate logging mechanism so that you have a trail if the check fails [4][2]. 2. Distinguish Error Types: Ensure that the cmdlets inside your try block are actually raising terminating errors, as many cmdlets are non-terminating by default [2]. Use -ErrorAction Stop or set $ErrorActionPreference = 'Stop' within the scope to ensure exceptions are caught [4][2][3]. 3. Log Effectively: In the catch block, capture the error information (using $_ or $PSItem) [6][7]. If the error is non-critical, Write-Verbose is appropriate; if it requires attention, consider Write-Warning or Write-Error [1][4][2]. 4. Consider Cleanup: If the startup check involves resources that need to be released (e.g., database connections, file handles), use a finally block to ensure cleanup occurs regardless of whether the check succeeded or failed [4][2][8]. By replacing an empty catch block with a logged event, you ensure that your script fails "loudly enough" to be debugged without creating unnecessary noise during normal operation [3].

Citations:


🏁 Script executed:

#!/bin/bash
# Find and examine the Win11Debloat.ps1 file around lines 230-232
fd -t f "Win11Debloat.ps1" | head -1 | xargs -I {} sh -c 'wc -l "{}" && sed -n "225,235p" "{}" | cat -n'

Repository: Raphire/Win11Debloat

Length of output: 1042


Replace empty catch block with verbose logging to aid diagnostics.

The catch block at lines 230-232 silently suppresses all WMI/CIM query failures without logging, making it harder to diagnose startup issues. While the try block correctly handles non-critical domain-join detection, the catch should emit a Write-Verbose message to provide visibility when failures occur.

Proposed fix
 catch {
-    # Suppress WMI/CIM query and null reference errors to ensure the script does not crash on startup
+    Write-Verbose "Domain-join check skipped: $($_.Exception.Message)"
 }
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] 230-232: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Win11Debloat.ps1` around lines 230 - 232, The catch block is currently empty
and suppresses all WMI/CIM query errors without any logging, making it difficult
to diagnose startup issues. Add a Write-Verbose statement inside the catch block
to emit diagnostic information when errors occur. This will provide visibility
into failures while still allowing the script to continue gracefully. The
verbose message should indicate that a WMI/CIM query or domain-join detection
attempt failed and optionally include error details from the exception.

Source: Linters/SAST tools


# Check if script has all required files
if (-not ((Test-Path $script:DefaultSettingsFilePath) -and (Test-Path $script:AppsListFilePath) -and (Test-Path $script:RegfilesPath) -and (Test-Path $script:AssetsPath) -and (Test-Path $script:AppSelectionSchema) -and (Test-Path $script:ApplyChangesWindowSchema) -and (Test-Path $script:SharedStylesSchema) -and (Test-Path $script:BubbleHintSchema) -and (Test-Path $script:RestoreBackupWindowSchema) -and (Test-Path $script:FeaturesFilePath))) {
Write-Error "Win11Debloat is unable to find required files, please ensure all script files are present"
Expand Down