diff --git a/.gitignore b/.gitignore index 02f795d..8418a12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ LastSettings SavedSettings +LastUsedSettings.json CustomAppsList Win11Debloat.log \ No newline at end of file diff --git a/Assets/Menus/DefaultSettings b/Assets/Menus/DefaultSettings deleted file mode 100644 index 7ad9c78..0000000 --- a/Assets/Menus/DefaultSettings +++ /dev/null @@ -1,11 +0,0 @@ -- Disable & remove Microsoft Copilot -- Disable telemetry, diagnostic data, activity history, app-launch tracking & targeted ads -- Disable tips, tricks, suggestions and ads in start, settings, notifications and File Explorer -- Disable ads, suggestions and the MSN news feed in Microsoft Edge -- Disable tips & tricks on the lockscreen (This may change your lockscreen wallpaper) -- Disable & remove Bing web search, Bing AI and Cortana from Windows search -- Show file extensions for known file types -- Disable widgets on the taskbar & lockscreen -- Disable Fast Start-up - - diff --git a/DefaultSettings.json b/DefaultSettings.json new file mode 100644 index 0000000..046f5db --- /dev/null +++ b/DefaultSettings.json @@ -0,0 +1,257 @@ +{ + "Version": "1.0", + "Settings": [ + { + "Name": "CreateRestorePoint", + "Value": true + }, + { + "Name": "DisableTelemetry", + "Value": true + }, + { + "Name": "DisableSuggestions", + "Value": true + }, + { + "Name": "DisableEdgeAds", + "Value": true + }, + { + "Name": "DisableLockscreenTips", + "Value": true + }, + { + "Name": "DisableBing", + "Value": true + }, + { + "Name": "DisableCopilot", + "Value": true + }, + { + "Name": "DisableRecall", + "Value": true + }, + { + "Name": "DisableClickToDo", + "Value": true + }, + { + "Name": "DisableWidgets", + "Value": true + }, + { + "Name": "HideChat", + "Value": true + }, + { + "Name": "ShowKnownFileExt", + "Value": true + }, + { + "Name": "DisableFastStartup", + "Value": true + }, + { + "Name": "Hide3dObjects", + "Value": true + }, + { + "Name": "DisableModernStandbyNetworking", + "Value": true + }, + { + "Name": "DisableDVR", + "Value": false + }, + { + "Name": "DisableGameBarIntegration", + "Value": false + }, + { + "Name": "ClearStart", + "Value": false + }, + { + "Name": "ClearStartAllUsers", + "Value": false + }, + { + "Name": "DisableStartRecommended", + "Value": false + }, + { + "Name": "DisableStartPhoneLink", + "Value": false + }, + { + "Name": "DisableSettings365Ads", + "Value": false + }, + { + "Name": "DisableEdgeAI", + "Value": false + }, + { + "Name": "DisablePaintAI", + "Value": false + }, + { + "Name": "DisableNotepadAI", + "Value": false + }, + { + "Name": "EnableDarkMode", + "Value": false + }, + { + "Name": "RevertContextMenu", + "Value": false + }, + { + "Name": "DisableMouseAcceleration", + "Value": false + }, + { + "Name": "DisableStickyKeys", + "Value": false + }, + { + "Name": "DisableDesktopSpotlight", + "Value": false + }, + { + "Name": "TaskbarAlignLeft", + "Value": false + }, + { + "Name": "CombineTaskbarAlways", + "Value": false + }, + { + "Name": "CombineMMTaskbarAlways", + "Value": false + }, + { + "Name": "CombineTaskbarWhenFull", + "Value": false + }, + { + "Name": "CombineMMTaskbarWhenFull", + "Value": false + }, + { + "Name": "CombineTaskbarNever", + "Value": false + }, + { + "Name": "CombineMMTaskbarNever", + "Value": false + }, + { + "Name": "MMTaskbarModeAll", + "Value": false + }, + { + "Name": "MMTaskbarModeMainActive", + "Value": false + }, + { + "Name": "MMTaskbarModeActive", + "Value": false + }, + { + "Name": "HideSearchTb", + "Value": false + }, + { + "Name": "ShowSearchIconTb", + "Value": false + }, + { + "Name": "ShowSearchLabelTb", + "Value": false + }, + { + "Name": "ShowSearchBoxTb", + "Value": false + }, + { + "Name": "HideTaskview", + "Value": false + }, + { + "Name": "EnableEndTask", + "Value": false + }, + { + "Name": "EnableLastActiveClick", + "Value": false + }, + { + "Name": "ShowHiddenFolders", + "Value": false + }, + { + "Name": "ExplorerToHome", + "Value": false + }, + { + "Name": "ExplorerToThisPC", + "Value": false + }, + { + "Name": "ExplorerToDownloads", + "Value": false + }, + { + "Name": "ExplorerToOneDrive", + "Value": false + }, + { + "Name": "HideHome", + "Value": false + }, + { + "Name": "HideGallery", + "Value": false + }, + { + "Name": "HideDupliDrive", + "Value": false + }, + { + "Name": "DisableTransparency", + "Value": false + }, + { + "Name": "DisableAnimations", + "Value": false + }, + { + "Name": "ForceRemoveEdge", + "Value": false + }, + { + "Name": "HideIncludeInLibrary", + "Value": false + }, + { + "Name": "HideGiveAccessTo", + "Value": false + }, + { + "Name": "HideShare", + "Value": false + }, + { + "Name": "HideOnedrive", + "Value": false + }, + { + "Name": "HideMusic", + "Value": false + } + ] +} \ No newline at end of file diff --git a/Get.ps1 b/Get.ps1 index 2165bc8..c48bc5b 100644 --- a/Get.ps1 +++ b/Get.ps1 @@ -9,7 +9,8 @@ param ( [switch]$RunDefaults, [switch]$RunDefaultsLite, [switch]$RunSavedSettings, - [switch]$RemoveApps, + [string]$Apps, + [switch]$RemoveApps, [switch]$RemoveAppsCustom, [switch]$RemoveGamingApps, [switch]$RemoveCommApps, @@ -103,11 +104,11 @@ catch { Exit } -# Remove old script folder if it exists, except for CustomAppsList and SavedSettings files +# Remove old script folder if it exists, except for CustomAppsList and LastUsedSettings.json files if (Test-Path "$env:TEMP/Win11Debloat") { Write-Output "" Write-Output "> Cleaning up old Win11Debloat folder..." - Get-ChildItem -Path "$env:TEMP/Win11Debloat" -Exclude CustomAppsList,SavedSettings,Win11Debloat.log | Remove-Item -Recurse -Force + Get-ChildItem -Path "$env:TEMP/Win11Debloat" -Exclude CustomAppsList,LastUsedSettings.json,Win11Debloat.log | Remove-Item -Recurse -Force } Write-Output "" @@ -143,13 +144,13 @@ if ($null -ne $debloatProcess) { $debloatProcess.WaitForExit() } -# Remove all remaining script files, except for CustomAppsList and SavedSettings files +# Remove all remaining script files, except for CustomAppsList and LastUsedSettings.json files if (Test-Path "$env:TEMP/Win11Debloat") { Write-Output "" Write-Output "> Cleaning up..." # Cleanup, remove Win11Debloat directory - Get-ChildItem -Path "$env:TEMP/Win11Debloat" -Exclude CustomAppsList,SavedSettings,Win11Debloat.log | Remove-Item -Recurse -Force + Get-ChildItem -Path "$env:TEMP/Win11Debloat" -Exclude CustomAppsList,LastUsedSettings.json,Win11Debloat.log | Remove-Item -Recurse -Force } Write-Output "" diff --git a/README.md b/README.md index ed3069e..266725c 100755 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Win11Debloat is a lightweight, easy to use PowerShell script that allows you to quickly declutter and improve your Windows experience. It can remove pre-installed bloatware apps, disable telemetry, remove intrusive interface elements and much more. No need to painstakingly go through all the settings yourself or remove apps one by one. Win11Debloat makes the process quick and easy! -The script also includes many features that system administrators will enjoy. Such as support for Windows Audit mode, the option to make changes to other Windows users and the ability to run the script without requiring user input during runtime. Please refer to our [wiki](https://github.com/Raphire/Win11Debloat/wiki/) for more details. +The script also includes many features that system administrators and power users will enjoy. Such as support for Windows Audit mode, the option to make changes to other Windows users and the ability to access all of Win11Debloat's features right from the command-line. Please refer to our [wiki](https://github.com/Raphire/Win11Debloat/wiki/) for more details. ![Win11Debloat Menu](/Assets/menu.png) diff --git a/Win11Debloat.ps1 b/Win11Debloat.ps1 index 2df75bb..7291f8a 100755 --- a/Win11Debloat.ps1 +++ b/Win11Debloat.ps1 @@ -11,7 +11,8 @@ param ( [switch]$RunDefaults, [switch]$RunDefaultsLite, [switch]$RunSavedSettings, - [switch]$RemoveApps, + [string]$Apps, + [switch]$RemoveApps, [switch]$RemoveAppsCustom, [switch]$RemoveGamingApps, [switch]$RemoveCommApps, @@ -23,10 +24,10 @@ param ( [switch]$DisableTelemetry, [switch]$DisableFastStartup, [switch]$DisableModernStandbyNetworking, - [switch]$DisableBingSearches, [switch]$DisableBing, + [switch]$DisableBing, [switch]$DisableBingSearches, [switch]$DisableDesktopSpotlight, - [switch]$DisableLockscrTips, [switch]$DisableLockscreenTips, - [switch]$DisableWindowsSuggestions, [switch]$DisableSuggestions, + [switch]$DisableLockscreenTips, [switch]$DisableLockscrTips, + [switch]$DisableSuggestions, [switch]$DisableWindowsSuggestions, [switch]$DisableEdgeAds, [switch]$DisableSettings365Ads, [switch]$DisableSettingsHome, @@ -51,7 +52,7 @@ param ( [switch]$DisableNotepadAI, [switch]$DisableEdgeAI, [switch]$DisableWidgets, [switch]$HideWidgets, - [switch]$DisableChat, [switch]$HideChat, + [switch]$HideChat, [switch]$DisableChat, [switch]$EnableEndTask, [switch]$EnableLastActiveClick, [switch]$ClearStart, @@ -68,20 +69,116 @@ param ( [switch]$ExplorerToDownloads, [switch]$ExplorerToOneDrive, [switch]$NoRestartExplorer, - [switch]$DisableOnedrive, [switch]$HideOnedrive, - [switch]$Disable3dObjects, [switch]$Hide3dObjects, - [switch]$DisableMusic, [switch]$HideMusic, - [switch]$DisableIncludeInLibrary, [switch]$HideIncludeInLibrary, - [switch]$DisableGiveAccessTo, [switch]$HideGiveAccessTo, - [switch]$DisableShare, [switch]$HideShare + [switch]$HideOnedrive, [switch]$DisableOnedrive, + [switch]$Hide3dObjects, [switch]$Disable3dObjects, + [switch]$HideMusic, [switch]$DisableMusic, + [switch]$HideIncludeInLibrary, [switch]$DisableIncludeInLibrary, + [switch]$HideGiveAccessTo, [switch]$DisableGiveAccessTo, + [switch]$HideShare, [switch]$DisableShare ) -# Show error if current powershell environment is limited by security policies +# Define script-level variables & paths +$script:DefaultSettingsFilePath = "$PSScriptRoot/DefaultSettings.json" +$script:AppsListFilePath = "$PSScriptRoot/Appslist.txt" +$script:SavedSettingsFilePath = "$PSScriptRoot/LastUsedSettings.json" +$script:CustomAppsListFilePath = "$PSScriptRoot/CustomAppsList" +$script:DefaultLogPath = "$PSScriptRoot/Win11Debloat.log" +$script:RegfilesPath = "$PSScriptRoot/Regfiles" +$script:AssetsPath = "$PSScriptRoot/Assets" + +$script:ControlParams = 'WhatIf', 'Confirm', 'Verbose', 'Debug', 'LogPath', 'Silent', 'Sysprep', 'User', 'NoRestartExplorer', 'RunDefaults', 'RunDefaultsLite', 'RunSavedSettings', 'RunAppsListGenerator' +$script:Features = @{ + "RemoveApps" = "Remove the apps specified in the 'Apps' parameter" + "Apps" = "The selection of apps to remove, specified as a comma separated list. Use 'Default' (or omit) to use the default apps list" + "RemoveAppsCustom" = "Remove custom selection of apps" + "RemoveCommApps" = "Remove the Mail, Calendar, and People apps" + "RemoveW11Outlook" = "Remove the new Outlook for Windows app" + "RemoveGamingApps" = "Remove the Xbox App and Xbox Gamebar" + "RemoveHPApps" = "Remove HP OEM applications" + "CreateRestorePoint" = "Create a system restore point" + "DisableTelemetry" = "Disable telemetry, diagnostic data, activity history, app-launch tracking & targeted ads" + "DisableSuggestions" = "Disable tips, tricks, suggestions and ads in start, settings, notifications and File Explorer" + "DisableEdgeAds" = "Disable ads, suggestions and the MSN news feed in Microsoft Edge" + "DisableLockscreenTips" = "Disable tips & tricks on the lockscreen" + "DisableBing" = "Disable & remove Bing web search, Bing AI and Cortana from Windows search" + "DisableCopilot" = "Disable & remove Microsoft Copilot" + "DisableRecall" = "Disable Windows Recall (Windows 11 only)" + "DisableClickToDo" = "Disable Click to Do, AI text & image analysis (Windows 11 only)" + "DisableWidgets" = "Disable widgets on the taskbar & lockscreen" + "HideChat" = "Hide the chat (meet now) icon from the taskbar (Windows 10 only)" + "ShowKnownFileExt" = "Show file extensions for known file types" + "DisableFastStartup" = "Disable Fast Start-up" + "Hide3dObjects" = "Hide the 3D objects folder under 'This pc' in File Explorer (Windows 10 only)" + "DisableModernStandbyNetworking" = "Disable network connectivity during Modern Standby (If supported)" + "DisableDVR" = "Disable Xbox game/screen recording" + "DisableGameBarIntegration" = "Disable Game Bar integration" + "ClearStart" = "Remove all pinned apps from the start menu for this user only" + "ClearStartAllUsers" = "Remove all pinned apps from the start menu for all existing and new users" + "ReplaceStart" = "Replace the start menu layout for this user only with the provided template file" + "ReplaceStartAllUsers" = "Replace the start menu layout for all existing and new users with the provided template file" + "DisableStartRecommended" = "Disable the recommended section in the start menu (Windows 11 only)" + "DisableStartPhoneLink" = "Disable the Phone Link mobile devices integration in the start menu" + "DisableSettings365Ads" = "Disable Microsoft 365 ads in Settings Home (Windows 11 only)" + "DisableSettingsHome" = "Completely hide the Settings 'Home' page (Windows 11 only)" + "DisableEdgeAI" = "Disable AI features in Microsoft Edge (Windows 11 only)" + "DisablePaintAI" = "Disable AI features in Paint (Windows 11 only)" + "DisableNotepadAI" = "Disable AI features in Notepad (Windows 11 only)" + "EnableDarkMode" = "Enable dark mode for system and apps" + "RevertContextMenu" = "Restore the old Windows 10 style context menu (Windows 11 only)" + "DisableMouseAcceleration" = "Turn off Enhance Pointer Precision (mouse acceleration)" + "DisableStickyKeys" = "Disable the Sticky Keys keyboard shortcut (Windows 11 only)" + "DisableDesktopSpotlight" = "Disable the Windows Spotlight desktop background option" + "TaskbarAlignLeft" = "Align taskbar icons to the left (Windows 11 only)" + "CombineTaskbarAlways" = "Always combine taskbar buttons and hide labels for the main display (Windows 11 only)" + "CombineMMTaskbarAlways" = "Always combine taskbar buttons and hide labels for secondary displays (Windows 11 only)" + "CombineTaskbarWhenFull" = "Combine taskbar buttons and hide labels when taskbar is full for the main display (Windows 11 only)" + "CombineMMTaskbarWhenFull" = "Combine taskbar buttons and hide labels when taskbar is full for secondary displays (Windows 11 only)" + "CombineTaskbarNever" = "Never combine taskbar buttons and show labels for the main display (Windows 11 only)" + "CombineMMTaskbarNever" = "Never combine taskbar buttons and show labels for secondary displays (Windows 11 only)" + "MMTaskbarModeAll" = "Show app icons on all taskbars (Windows 11 only)" + "MMTaskbarModeMainActive" = "Show app icons on main taskbar and on taskbar where the windows is open (Windows 11 only)" + "MMTaskbarModeActive" = "Show app icons only on taskbar where the window is open (Windows 11 only)" + "HideSearchTb" = "Hide search icon from the taskbar (Windows 11 only)" + "ShowSearchIconTb" = "Show search icon on the taskbar (Windows 11 only)" + "ShowSearchLabelTb" = "Show search icon with label on the taskbar (Windows 11 only)" + "ShowSearchBoxTb" = "Show search box on the taskbar (Windows 11 only)" + "HideTaskview" = "Hide the taskview button from the taskbar (Windows 11 only)" + "EnableEndTask" = "Enable the 'End Task' option in the taskbar right click menu (Windows 11 only)" + "EnableLastActiveClick" = "Enable the 'Last Active Click' behavior in the taskbar app area" + "ShowHiddenFolders" = "Show hidden files, folders and drives" + "ExplorerToHome" = "Change the default location that File Explorer opens to 'Home'" + "ExplorerToThisPC" = "Change the default location that File Explorer opens to 'This PC'" + "ExplorerToDownloads" = "Change the default location that File Explorer opens to 'Downloads'" + "ExplorerToOneDrive" = "Change the default location that File Explorer opens to 'OneDrive'" + "HideHome" = "Hide the Home section from the File Explorer sidepanel (Windows 11 only)" + "HideGallery" = "Hide the Gallery section from the File Explorer sidepanel (Windows 11 only)" + "HideDupliDrive" = "Hide duplicate removable drive entries from the File Explorer sidepanel" + "DisableTransparency" = "Disable transparency effects" + "DisableAnimations" = "Disable animations and visual effects" + "ForceRemoveEdge" = "Forcefully uninstall Microsoft Edge. NOT RECOMMENDED!" + "HideIncludeInLibrary" = "Hide the 'Include in library' option in the context menu (Windows 10 only)" + "HideGiveAccessTo" = "Hide the 'Give access to' option in the context menu (Windows 10 only)" + "HideShare" = "Hide the 'Share' option in the context menu (Windows 10 only)" + "HideOnedrive" = "Hide the OneDrive folder in the File Explorer sidepanel (Windows 10 only)" + "HideMusic" = "Hide the music folder under 'This pc' in File Explorer (Windows 10 only)" +} + +# Check if current powershell environment is limited by security policies if ($ExecutionContext.SessionState.LanguageMode -ne "FullLanguage") { - Write-Host "Error: Win11Debloat is unable to run on your system, powershell execution is restricted by security policies" -ForegroundColor Red - AwaitKeyToExit + Write-Error "Win11Debloat is unable to run on your system, powershell execution is restricted by security policies" + Write-Output "Press any key to exit..." + $null = [System.Console]::ReadKey() + Exit +} + +# Check if script does not see file dependencies +if (-not ((Test-Path $script:DefaultSettingsFilePath) -and (Test-Path $script:AppsListFilePath) -and (Test-Path $script:RegfilesPath) -and (Test-Path $script:AssetsPath))) { + Write-Error "Win11Debloat is unable to find required files, please ensure all script files are present" + Write-Output "Press any key to exit..." + $null = [System.Console]::ReadKey() + Exit } # Log script output to 'Win11Debloat.log' at the specified path @@ -89,7 +186,7 @@ if ($LogPath -and (Test-Path $LogPath)) { Start-Transcript -Path "$LogPath/Win11Debloat.log" -Append -IncludeInvocationHeader -Force | Out-Null } else { - Start-Transcript -Path "$PSScriptRoot/Win11Debloat.log" -Append -IncludeInvocationHeader -Force | Out-Null + Start-Transcript -Path $script:DefaultLogPath -Append -IncludeInvocationHeader -Force | Out-Null } @@ -112,20 +209,20 @@ function ShowAppSelectionForm { $label = New-Object System.Windows.Forms.Label $button1 = New-Object System.Windows.Forms.Button $button2 = New-Object System.Windows.Forms.Button - $selectionBox = New-Object System.Windows.Forms.CheckedListBox + $selectionBox = New-Object System.Windows.Forms.CheckedListBox $loadingLabel = New-Object System.Windows.Forms.Label $onlyInstalledCheckBox = New-Object System.Windows.Forms.CheckBox $checkUncheckCheckBox = New-Object System.Windows.Forms.CheckBox $initialFormWindowState = New-Object System.Windows.Forms.FormWindowState - $script:selectionBoxIndex = -1 + $script:SelectionBoxIndex = -1 # saveButton eventHandler - $handler_saveButton_Click= + $handler_saveButton_Click= { if ($selectionBox.CheckedItems -contains "Microsoft.WindowsStore" -and -not $Silent) { $warningSelection = [System.Windows.Forms.Messagebox]::Show('Are you sure you wish to uninstall the Microsoft Store? This app cannot easily be reinstalled.', 'Are you sure?', 'YesNo', 'Warning') - + if ($warningSelection -eq 'No') { return } @@ -133,34 +230,40 @@ function ShowAppSelectionForm { $script:SelectedApps = $selectionBox.CheckedItems - # Create file that stores selected apps if it doesn't exist - if (-not (Test-Path "$PSScriptRoot/CustomAppsList")) { - $null = New-Item "$PSScriptRoot/CustomAppsList" + # Close form without saving if no apps were selected + if ($script:SelectedApps.Count -eq 0) { + $form.Close() + return } - Set-Content -Path "$PSScriptRoot/CustomAppsList" -Value $script:SelectedApps + # Create file that stores selected apps if it doesn't exist + if (-not (Test-Path $script:CustomAppsListFilePath)) { + $null = New-Item $script:CustomAppsListFilePath + } + + Set-Content -Path $script:CustomAppsListFilePath -Value $script:SelectedApps $form.DialogResult = [System.Windows.Forms.DialogResult]::OK $form.Close() } # cancelButton eventHandler - $handler_cancelButton_Click= + $handler_cancelButton_Click= { $form.Close() } - $selectionBox_SelectedIndexChanged= + $selectionBox_SelectedIndexChanged= { - $script:selectionBoxIndex = $selectionBox.SelectedIndex + $script:SelectionBoxIndex = $selectionBox.SelectedIndex } $selectionBox_MouseDown= { if ($_.Button -eq [System.Windows.Forms.MouseButtons]::Left) { if ([System.Windows.Forms.Control]::ModifierKeys -eq [System.Windows.Forms.Keys]::Shift) { - if ($script:selectionBoxIndex -ne -1) { - $topIndex = $script:selectionBoxIndex + if ($script:SelectionBoxIndex -ne -1) { + $topIndex = $script:SelectionBoxIndex if ($selectionBox.SelectedIndex -gt $topIndex) { for (($i = ($topIndex)); $i -le $selectionBox.SelectedIndex; $i++) { @@ -174,7 +277,7 @@ function ShowAppSelectionForm { } } } - elseif ($script:selectionBoxIndex -ne $selectionBox.SelectedIndex) { + elseif ($script:SelectionBoxIndex -ne $selectionBox.SelectedIndex) { $selectionBox.SetItemChecked($selectionBox.SelectedIndex, -not $selectionBox.GetItemChecked($selectionBox.SelectedIndex)) } } @@ -193,7 +296,7 @@ function ShowAppSelectionForm { $form.WindowState = $initialFormWindowState # Reset state to default before loading appslist again - $script:selectionBoxIndex = -1 + $script:SelectionBoxIndex = -1 $checkUncheckCheckBox.Checked = $False # Show loading indicator @@ -203,11 +306,9 @@ function ShowAppSelectionForm { # Clear selectionBox before adding any new items $selectionBox.Items.Clear() - # Set filePath where Appslist can be found - $appsFile = "$PSScriptRoot/Appslist.txt" $listOfApps = "" - if ($onlyInstalledCheckBox.Checked -and ($script:wingetInstalled -eq $true)) { + if ($onlyInstalledCheckBox.Checked -and ($script:WingetInstalled -eq $true)) { # Attempt to get a list of installed apps via winget, times out after 10 seconds $job = Start-Job { return winget list --accept-source-agreements --disable-interactivity } $jobDone = $job | Wait-Job -TimeOut 10 @@ -223,7 +324,7 @@ function ShowAppSelectionForm { } # Go through appslist and add items one by one to the selectionBox - Foreach ($app in (Get-Content -Path $appsFile | Where-Object { $_ -notmatch '^\s*$' -and $_ -notmatch '^# .*' -and $_ -notmatch '^# -* #' } )) { + Foreach ($app in (Get-Content -Path $script:AppsListFilePath | Where-Object { $_ -notmatch '^\s*$' -and $_ -notmatch '^# .*' -and $_ -notmatch '^# -* #' } )) { $appChecked = $true # Remove first # if it exists and set appChecked to false @@ -236,7 +337,7 @@ function ShowAppSelectionForm { if (-not ($app.IndexOf('#') -eq -1)) { $app = $app.Substring(0, $app.IndexOf('#')) } - + # Remove leading and trailing spaces and `*` characters from Appname $app = $app.Trim() $appString = $app.Trim('*') @@ -255,11 +356,11 @@ function ShowAppSelectionForm { } } - # Add the app to the selectionBox and set it's checked status + # Add the app to the selectionBox and set its checked status $selectionBox.Items.Add($appString, $appChecked) | Out-Null } } - + # Hide loading indicator $loadingLabel.Visible = $False @@ -353,6 +454,45 @@ function ShowAppSelectionForm { } +# Returns a validated list of apps based on the provided appsList and the supported apps from Appslist.txt +function ValidateAppslist { + param ( + $appsList + ) + + $supportedAppsList = @() + $validatedAppsList = @() + + # Generate a list of supported apps from AppsList.txt + Foreach ($app in (Get-Content -Path $script:AppsListFilePath | Where-Object { $_ -notmatch '^\s*$' -and $_ -notmatch '^# .*' -and $_ -notmatch '^# -* #' } )) { + $app = $app.TrimStart("#") + + if (-not ($app.IndexOf('#') -eq -1)) { + $app = $app.Substring(0, $app.IndexOf('#')) + } + + $app = $app.Trim() + $appString = $app.Trim('*') + $supportedAppsList += $appString + } + + # Validate provided appsList against supportedAppsList + Foreach ($app in $appsList) { + $app = $app.Trim() + $appString = $app.Trim('*') + + if ($supportedAppsList -notcontains $appString) { + Write-Host "Removal of app '$appString' is not supported and will be skipped" -ForegroundColor Yellow + continue + } + + $validatedAppsList += $appString + } + + return $validatedAppsList +} + + # Returns list of apps from the specified file, it trims the app names and removes any comments function ReadAppslistFromFile { param ( @@ -361,16 +501,16 @@ function ReadAppslistFromFile { $appsList = @() - # Get list of apps from file at the path provided, and remove them one by one + if (-not (Test-Path $appsFilePath)) { + return $appsList + } + Foreach ($app in (Get-Content -Path $appsFilePath | Where-Object { $_ -notmatch '^#.*' -and $_ -notmatch '^\s*$' } )) { - # Remove any comments from the Appname if (-not ($app.IndexOf('#') -eq -1)) { $app = $app.Substring(0, $app.IndexOf('#')) } - # Remove any spaces before and after the Appname $app = $app.Trim() - $appString = $app.Trim('*') $appsList += $appString } @@ -385,13 +525,13 @@ function RemoveApps { $appslist ) - Foreach ($app in $appsList) { + Foreach ($app in $appsList) { Write-Output "Attempting to remove $app..." # Use winget only to remove OneDrive and Edge if (($app -eq "Microsoft.OneDrive") -or ($app -eq "Microsoft.Edge")) { - if ($script:wingetInstalled -eq $false) { - Write-Host "Error: WinGet is either not installed or is outdated, $app could not be removed" -ForegroundColor Red + if ($script:WingetInstalled -eq $false) { + Write-Host "WinGet is either not installed or is outdated, $app could not be removed" -ForegroundColor Red continue } @@ -405,16 +545,17 @@ function RemoveApps { RegImport "Adding scheduled task to uninstall $app after new users log in..." "Uninstall_$($appName).reg" } else { - Strip-Progress -ScriptBlock { winget uninstall --accept-source-agreements --disable-interactivity --id $app } | Tee-Object -Variable wingetOutput - } + # Uninstall app via winget, with any progress indicators removed from the output + StripProgress -ScriptBlock { winget uninstall --accept-source-agreements --disable-interactivity --id $app } | Tee-Object -Variable wingetOutput - If (($app -eq "Microsoft.Edge") -and (Select-String -InputObject $wingetOutput -Pattern "Uninstall failed with exit code")) { - Write-Host "Unable to uninstall Microsoft Edge via Winget" -ForegroundColor Red - Write-Output "" - - if ($( Read-Host -Prompt "Would you like to forcefully uninstall Edge? NOT RECOMMENDED! (y/n)" ) -eq 'y') { + If (($app -eq "Microsoft.Edge") -and (Select-String -InputObject $wingetOutput -Pattern "Uninstall failed with exit code")) { + Write-Host "Unable to uninstall Microsoft Edge via Winget" -ForegroundColor Red Write-Output "" - ForceRemoveEdge + + if ($( Read-Host -Prompt "Would you like to forcefully uninstall Microsoft Edge? NOT RECOMMENDED! (y/n)" ) -eq 'y') { + Write-Output "" + ForceRemoveEdge + } } } @@ -448,14 +589,14 @@ function RemoveApps { Write-Host $psitem.Exception.StackTrace -ForegroundColor Gray } } - + Write-Output "" } -# Forcefully removes Microsoft Edge using it's uninstaller +# Forcefully removes Microsoft Edge using its uninstaller +# Credit: Based on work from loadstring1 & ave9858 function ForceRemoveEdge { - # Based on work from loadstring1 & ave9858 Write-Output "> Forcefully uninstalling Microsoft Edge..." $regView = [Microsoft.Win32.RegistryView]::Registry32 @@ -507,13 +648,13 @@ function ForceRemoveEdge { Write-Output "" Write-Host "Error: Unable to forcefully uninstall Microsoft Edge, uninstaller could not be found" -ForegroundColor Red } - + Write-Output "" } # Execute provided command and strips progress spinners/bars from console output -function Strip-Progress { +function StripProgress { param( [ScriptBlock]$ScriptBlock ) @@ -526,7 +667,7 @@ function Strip-Progress { & $ScriptBlock 2>&1 | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { - "ERROR: $($_.Exception.Message)" + "Error: $($_.Exception.Message)" } else { $line = $_ -replace $progressPattern, '' -replace $sizePattern, '' @@ -578,24 +719,24 @@ function GetUserDirectory { try { $userDirectoryExists = Test-Path "$env:SystemDrive\Users\$userName" $userPath = "$env:SystemDrive\Users\$userName\$fileName" - + if ((Test-Path $userPath) -or ($userDirectoryExists -and (-not $exitIfPathNotFound))) { return $userPath } - + $userDirectoryExists = Test-Path ($env:USERPROFILE -Replace ('\\' + $env:USERNAME + '$'), "\$userName") $userPath = $env:USERPROFILE -Replace ('\\' + $env:USERNAME + '$'), "\$userName\$fileName" - + if ((Test-Path $userPath) -or ($userDirectoryExists -and (-not $exitIfPathNotFound))) { return $userPath } } catch { - Write-Host "Error: Something went wrong when trying to find the user directory path for user $userName. Please ensure the user exists on this system." -ForegroundColor Red + Write-Error "Something went wrong when trying to find the user directory path for user $userName. Please ensure the user exists on this system" AwaitKeyToExit } - Write-Host "Error: Unable to find user directory path for user $userName" -ForegroundColor Red + Write-Error "Unable to find user directory path for user $userName" AwaitKeyToExit } @@ -611,21 +752,21 @@ function RegImport { if ($script:Params.ContainsKey("Sysprep")) { $defaultUserPath = GetUserDirectory -userName "Default" -fileName "NTUSER.DAT" - + reg load "HKU\Default" $defaultUserPath | Out-Null - reg import "$PSScriptRoot\Regfiles\Sysprep\$path" + reg import "$script:RegfilesPath\Sysprep\$path" reg unload "HKU\Default" | Out-Null } elseif ($script:Params.ContainsKey("User")) { $userPath = GetUserDirectory -userName $script:Params.Item("User") -fileName "NTUSER.DAT" - + reg load "HKU\Default" $userPath | Out-Null - reg import "$PSScriptRoot\Regfiles\Sysprep\$path" + reg import "$script:RegfilesPath\Sysprep\$path" reg unload "HKU\Default" | Out-Null - + } else { - reg import "$PSScriptRoot\Regfiles\$path" + reg import "$script:RegfilesPath\$path" } Write-Output "" @@ -634,18 +775,19 @@ function RegImport { # Restart the Windows Explorer process function RestartExplorer { + Write-Output "> Attempting to restart the Windows Explorer process to apply all changes..." + if ($script:Params.ContainsKey("Sysprep") -or $script:Params.ContainsKey("User") -or $script:Params.ContainsKey("NoRestartExplorer")) { + Write-Host "Process restart was skipped, please manually reboot your PC to apply all changes" -ForegroundColor Yellow return } - Write-Output "> Restarting Windows Explorer process to apply all changes... (This may cause some flickering)" - if ($script:Params.ContainsKey("DisableMouseAcceleration")) { - Write-Host "Warning: The Enhance Pointer Precision setting changes will only take effect after a reboot" -ForegroundColor Yellow + Write-Host "Warning: Changes to the Enhance Pointer Precision setting will only take effect after a reboot" -ForegroundColor Yellow } if ($script:Params.ContainsKey("DisableStickyKeys")) { - Write-Host "Warning: The Sticky Keys setting changes will only take effect after a reboot" -ForegroundColor Yellow + Write-Host "Warning: Changes to the Sticky Keys setting will only take effect after a reboot" -ForegroundColor Yellow } if ($script:Params.ContainsKey("DisableAnimations")) { @@ -655,10 +797,11 @@ function RestartExplorer { # Only restart if the powershell process matches the OS architecture. # Restarting explorer from a 32bit PowerShell window will fail on a 64bit OS if ([Environment]::Is64BitProcess -eq [Environment]::Is64BitOperatingSystem) { + Write-Output "Restarting the Windows Explorer process... (This may cause your screen to flicker)" Stop-Process -processName: Explorer -Force } else { - Write-Warning "Unable to restart Windows Explorer process, please manually reboot your PC to apply all changes." + Write-Host "Unable to restart Windows Explorer process, please manually reboot your PC to apply all changes" -ForegroundColor Yellow } } @@ -667,12 +810,12 @@ function RestartExplorer { # Credit: https://lazyadmin.nl/win-11/customize-windows-11-start-menu-layout/ function ReplaceStartMenuForAllUsers { param ( - $startMenuTemplate = "$PSScriptRoot/Assets/Start/start2.bin" + $startMenuTemplate = "$script:AssetsPath/Start/start2.bin" ) Write-Output "> Removing all pinned apps from the start menu for all users..." - # Check if template bin file exists, return early if it doesn't + # Check if template bin file exists if (-not (Test-Path $startMenuTemplate)) { Write-Host "Error: Unable to clear start menu, start2.bin file missing from script folder" -ForegroundColor Red Write-Output "" @@ -708,7 +851,7 @@ function ReplaceStartMenuForAllUsers { # Credit: https://lazyadmin.nl/win-11/customize-windows-11-start-menu-layout/ function ReplaceStartMenu { param ( - $startMenuTemplate = "$PSScriptRoot/Assets/Start/start2.bin", + $startMenuTemplate = "$script:AssetsPath/Start/start2.bin", $startMenuBinFile = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" ) @@ -717,7 +860,7 @@ function ReplaceStartMenu { $startMenuBinFile = GetUserDirectory -userName "$(GetUserName)" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" -exitIfPathNotFound $false } - # Check if template bin file exists, return early if it doesn't + # Check if template bin file exists if (-not (Test-Path $startMenuTemplate)) { Write-Host "Error: Unable to replace start menu, template file not found" -ForegroundColor Red return @@ -737,7 +880,7 @@ function ReplaceStartMenu { Move-Item -Path $startMenuBinFile -Destination $backupBinFile -Force } else { - Write-Host "Warning: Unable to find original start2.bin file for user $userName. No backup was created for this user!" -ForegroundColor Yellow + Write-Host "Unable to find original start2.bin file for user $userName, no backup was created for this user" -ForegroundColor Yellow New-Item -ItemType File -Path $startMenuBinFile -Force } @@ -752,36 +895,48 @@ function ReplaceStartMenu { function AddParameter { param ( $parameterName, - $message, - $addToFile = $true + $value = $true ) - # Add key if it doesn't already exist + # Add parameter or update its value if key already exists if (-not $script:Params.ContainsKey($parameterName)) { - $script:Params.Add($parameterName, $true) + $script:Params.Add($parameterName, $value) } - - if (-not $addToFile) { - Write-Output "- $message" - return + else { + $script:Params[$parameterName] = $value } - - # Create or clear file that stores last used settings - if (-not (Test-Path "$PSScriptRoot/SavedSettings")) { - $null = New-Item "$PSScriptRoot/SavedSettings" - } - elseif ($script:FirstSelection) { - $null = Clear-Content "$PSScriptRoot/SavedSettings" - } - - $script:FirstSelection = $false - - # Create entry and add it to the file - $entry = "$parameterName#- $message" - Add-Content -Path "$PSScriptRoot/SavedSettings" -Value $entry } +# Saves the current settings, excluding control parameters, to a JSON file +function SaveSettings { + $settings = @{ + "Version" = "1.0" + "Settings" = @() + } + + foreach ($param in $script:Params.Keys) { + if ($script:ControlParams -notcontains $param) { + $value = $script:Params[$param] + + $settings.Settings += @{ + "Name" = $param + "Value" = $value + } + } + } + + try { + $settings | ConvertTo-Json -Depth 10 | Set-Content $script:SavedSettingsFilePath + } + catch { + Write-Output "" + Write-Host "Error: Failed to save settings to LastUsedSettings.json file" -ForegroundColor Red + } +} + + +# Prints the header for the script function PrintHeader { param ( $title @@ -803,6 +958,7 @@ function PrintHeader { } +# Prints the contents of a file to the console function PrintFromFile { param ( $path, @@ -823,23 +979,92 @@ function PrintFromFile { } -function PrintAppsList { - param ( - $path, - $printCount = $false - ) +# Prints all pending changes that will be made by the script +function PrintPendingChanges { + Write-Output "Win11Debloat will make the following changes:" - if (-not (Test-Path $path)) { - return + if ($script:Params['CreateRestorePoint']) { + Write-Output "- $($script:Features['CreateRestorePoint'])" } - - $appsList = ReadAppslistFromFile $path + foreach ($parameterName in $script:Params.Keys) { + if ($script:ControlParams -contains $parameterName) { + continue + } - if ($printCount) { - Write-Output "- Remove $($appsList.Count) apps:" + # Print parameter description + switch ($parameterName) { + 'Apps' { + continue + } + 'CreateRestorePoint' { + continue + } + 'RemoveApps' { + $appsList = GenerateAppsList + + if ($appsList.Count -eq 0) { + Write-Host "No valid apps were selected for removal" -ForegroundColor Yellow + Write-Output "" + continue + } + + Write-Output "- Remove $($appsList.Count) apps:" + Write-Host $appsList -ForegroundColor DarkGray + continue + } + 'RemoveAppsCustom' { + $appsList = ReadAppslistFromFile $script:CustomAppsListFilePath + + if ($appsList.Count -eq 0) { + Write-Host "No valid apps were selected for removal" -ForegroundColor Yellow + Write-Output "" + continue + } + + Write-Output "- Remove $($appsList.Count) apps:" + Write-Host $appsList -ForegroundColor DarkGray + continue + } + default { + if ($script:Features -and $script:Features.ContainsKey($parameterName)) { + $message = $script:Features[$parameterName] + Write-Output "- $message" + } + else { + # Fallback: show the parameter name if no feature description is available + Write-Output "- $parameterName" + } + continue + } + } } - Write-Host $appsList -ForegroundColor DarkGray + Write-Output "" + Write-Output "" + Write-Output "Press enter to execute the script or press CTRL+C to quit..." + Read-Host | Out-Null +} + + +# Generates a list of apps to remove based on the Apps parameter +function GenerateAppsList { + if (-not ($script:Params["Apps"] -and $script:Params["Apps"] -is [string])) { + return @() + } + + $appMode = $script:Params["Apps"].toLower() + + switch ($appMode) { + 'default' { + $appsList = ReadAppslistFromFile $script:AppsListFilePath + return $appsList + } + default { + $appsList = $script:Params["Apps"].Split(',') | ForEach-Object { $_.Trim() } + $validatedAppsList = ValidateAppslist $appsList + return $validatedAppsList + } + } } @@ -857,22 +1082,22 @@ function AwaitKeyToExit { function GetUserName { - if ($script:Params.ContainsKey("User")) { - return $script:Params.Item("User") + if ($script:Params.ContainsKey("User")) { + return $script:Params.Item("User") } - + return $env:USERNAME } function CreateSystemRestorePoint { Write-Output "> Attempting to create a system restore point..." - + $SysRestore = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore" -Name "RPSessionInterval" if ($SysRestore.RPSessionInterval -eq 0) { if ($Silent -or $( Read-Host -Prompt "System restore is disabled, would you like to enable it and create a restore point? (y/n)") -eq 'y') { - $enableSystemRestoreJob = Start-Job { + $enableSystemRestoreJob = Start-Job { try { Enable-ComputerRestore -Drive "$env:SystemDrive" } @@ -881,7 +1106,7 @@ function CreateSystemRestorePoint { return } } - + $enableSystemRestoreJobDone = $enableSystemRestoreJob | Wait-Job -TimeOut 20 if (-not $enableSystemRestoreJobDone) { @@ -898,7 +1123,7 @@ function CreateSystemRestorePoint { } } - $createRestorePointJob = Start-Job { + $createRestorePointJob = Start-Job { # Find existing restore points that are less than 24 hours old try { $recentRestorePoints = Get-ComputerRestorePoint | Where-Object { (Get-Date) - [System.Management.ManagementDateTimeConverter]::ToDateTime($_.CreationTime) -le (New-TimeSpan -Hours 24) } @@ -907,7 +1132,7 @@ function CreateSystemRestorePoint { Write-Host "Error: Unable to retrieve existing restore points: $_" -ForegroundColor Red return } - + if ($recentRestorePoints.Count -eq 0) { try { Checkpoint-Computer -Description "Restore point created by Win11Debloat" -RestorePointType "MODIFY_SETTINGS" @@ -918,10 +1143,10 @@ function CreateSystemRestorePoint { } } else { - Write-Host "A recent restore point already exists, no new restore point was created." -ForegroundColor Yellow + Write-Host "A recent restore point already exists, no new restore point was created" -ForegroundColor Yellow } } - + $createRestorePointJobDone = $createRestorePointJob | Wait-Job -TimeOut 20 if (-not $createRestorePointJobDone) { @@ -946,8 +1171,8 @@ function ShowScriptMenuOptions { Write-Host "(3) App removal mode: Select & remove apps, without making other changes" # Only show this option if SavedSettings file exists - if (Test-Path "$PSScriptRoot/SavedSettings") { - Write-Host "(4) Apply saved custom settings from last time" + if (Test-Path $script:SavedSettingsFilePath) { + Write-Host "(4) Apply the settings you used last time" $ModeSelectionMessage = "Please select an option (1/2/3/4/0)" } @@ -961,12 +1186,12 @@ function ShowScriptMenuOptions { if ($Mode -eq '0') { # Print information screen from file - PrintFromFile "$PSScriptRoot/Assets/Menus/Info" "Information" + PrintFromFile "$script:AssetsPath/Menus/Info" "Information" Write-Host "Press any key to go back..." $null = [System.Console]::ReadKey() } - elseif (($Mode -eq '4') -and -not (Test-Path "$PSScriptRoot/SavedSettings")) { + elseif (($Mode -eq '4') -and -not (Test-Path $script:SavedSettingsFilePath)) { $Mode = $null } } @@ -976,9 +1201,7 @@ function ShowScriptMenuOptions { } -function ShowDefaultMode { - AddParameter 'CreateRestorePoint' 'Create a system restore point' $false - +function ShowDefaultModeOptions { # Show options for removing apps, or set selection if RunDefaults or RunDefaultsLite parameter was passed if ($RunDefaults) { $RemoveAppsInput = '1' @@ -989,74 +1212,66 @@ function ShowDefaultMode { else { $RemoveAppsInput = ShowDefaultModeAppRemovalOptions - if (($script:selectedApps.contains('Microsoft.XboxGameOverlay') -or $script:selectedApps.contains('Microsoft.XboxGamingOverlay')) -and + if ($RemoveAppsInput -eq '2' -and ($script:SelectedApps.contains('Microsoft.XboxGameOverlay') -or $script:SelectedApps.contains('Microsoft.XboxGamingOverlay')) -and $( Read-Host -Prompt "Disable Game Bar integration and game/screen recording? This also stops ms-gamingoverlay and ms-gamebar popups (y/n)" ) -eq 'y') { $DisableGameBarIntegrationInput = $true; } } - PrintHeader 'Default Mode' - - Write-Output "Win11Debloat will make the following changes:" - - # Select correct option based on user input - switch ($RemoveAppsInput) { - '1' { - AddParameter 'RemoveApps' 'Remove the default selection of apps:' $false - PrintAppsList "$PSScriptRoot/Appslist.txt" - } - '2' { - AddParameter 'RemoveAppsCustom' "Remove $($script:SelectedApps.Count) apps:" $false - PrintAppsList "$PSScriptRoot/CustomAppsList" - } - } - - if ($DisableGameBarIntegrationInput) { - AddParameter 'DisableDVR' 'Disable Xbox game/screen recording' $false - AddParameter 'DisableGameBarIntegration' 'Disable Game Bar integration' $false - } - - # Only add this option for Windows 10 users - if (get-ciminstance -query "select caption from win32_operatingsystem where caption like '%Windows 10%'") { - AddParameter 'Hide3dObjects' "Hide the 3D objects folder under 'This pc' in File Explorer" $false - AddParameter 'HideChat' 'Hide the chat (meet now) icon from the taskbar' $false - } - - # Only add these options for Windows 11 users (build 22000+) - if ($WinVersion -ge 22000) { - if ($script:ModernStandbySupported) { - AddParameter 'DisableModernStandbyNetworking' 'Disable network connectivity during Modern Standby' $false + # Add default settings based on user input + try { + $defaultSettings = (Get-Content -Path $script:DefaultSettingsFilePath -Raw | ConvertFrom-Json) + if (-not $defaultSettings.Version -or $defaultSettings.Version -ne "1.0") { + Write-Warning "DefaultSettings.json version mismatch (expected 1.0, found $($defaultSettings.Version))" } - AddParameter 'DisableRecall' 'Disable Windows Recall' $false - AddParameter 'DisableClickToDo' 'Disable Click to Do (AI text & image analysis)' $false + # Select app removal options based on user input + switch ($RemoveAppsInput) { + '1' { + AddParameter 'RemoveApps' + AddParameter 'Apps' 'Default' + } + '2' { + AddParameter 'RemoveAppsCustom' + + if ($DisableGameBarIntegrationInput) { + AddParameter 'DisableDVR' + AddParameter 'DisableGameBarIntegration' + } + } + } + + Foreach ($setting in $defaultSettings.Settings) { + if ($setting.Value -eq $false) { + continue + } + + AddParameter $setting.Name $setting.Value + } + } + catch { + Write-Error "Failed to load settings from DefaultSettings.json file" + AwaitKeyToExit } - PrintFromFile "$PSScriptRoot/Assets/Menus/DefaultSettings" "Default Mode" $false - - # Suppress prompt if Silent parameter was passed - if (-not $Silent) { - Write-Output "Press enter to execute the script or press CTRL+C to quit..." - Read-Host | Out-Null - } - - $DefaultParameterNames = 'DisableCopilot','DisableTelemetry','DisableSuggestions','DisableEdgeAds','DisableLockscreenTips','DisableBing','ShowKnownFileExt','DisableWidgets','DisableFastStartup' + SaveSettings PrintHeader 'Default Mode' - # Add default parameters, if they don't already exist - foreach ($ParameterName in $DefaultParameterNames) { - if (-not $script:Params.ContainsKey($ParameterName)) { - $script:Params.Add($ParameterName, $true) - } + # Skip change summary if Silent parameter was passed + if ($Silent) { + return } + + PrintPendingChanges + PrintHeader 'Default Mode' } function ShowDefaultModeAppRemovalOptions { PrintHeader 'Default Mode' - Write-Host "Please note: The default selection of apps includes Microsoft Teams, Spotify, Sticky Notes and more. Select option 2 to verify and change what apps are removed by the script." -ForegroundColor DarkGray + Write-Host "Please note: The default selection of apps includes Microsoft Teams, Spotify, Sticky Notes and more. Select option 2 to verify and change what apps are removed by the script" -ForegroundColor DarkGray Write-Host "" Do { @@ -1071,7 +1286,7 @@ function ShowDefaultModeAppRemovalOptions { $result = ShowAppSelectionForm if ($result -ne [System.Windows.Forms.DialogResult]::OK) { - # User cancelled or closed app selection, show error and change RemoveAppsInput so the menu will be shown again + # User cancelled or closed app selection, change RemoveAppsInput so the menu will be shown again Write-Host "" Write-Host "Cancelled application selection, please try again" -ForegroundColor Red @@ -1090,10 +1305,10 @@ function ShowDefaultModeAppRemovalOptions { function ShowCustomModeOptions { # Get current Windows build version to compare against features $WinVersion = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' CurrentBuild - + PrintHeader 'Custom Mode' - AddParameter 'CreateRestorePoint' 'Create a system restore point' + AddParameter 'CreateRestorePoint' # Show options for removing apps, only continue on valid input Do { @@ -1109,41 +1324,48 @@ function ShowCustomModeOptions { $result = ShowAppSelectionForm if ($result -ne [System.Windows.Forms.DialogResult]::OK) { - # User cancelled or closed app selection, show error and change RemoveAppsInput so the menu will be shown again + # User cancelled or closed app selection, change RemoveAppsInput so the menu will be shown again Write-Output "" Write-Host "Cancelled application selection, please try again" -ForegroundColor Red $RemoveAppsInput = 'c' } - + Write-Output "" } } - while ($RemoveAppsInput -ne 'n' -and $RemoveAppsInput -ne '0' -and $RemoveAppsInput -ne '1' -and $RemoveAppsInput -ne '2' -and $RemoveAppsInput -ne '3') + while ($RemoveAppsInput -ne 'n' -and $RemoveAppsInput -ne '0' -and $RemoveAppsInput -ne '1' -and $RemoveAppsInput -ne '2' -and $RemoveAppsInput -ne '3') # Select correct option based on user input switch ($RemoveAppsInput) { '1' { - AddParameter 'RemoveApps' 'Remove the default selection of apps' + AddParameter 'RemoveApps' + AddParameter 'Apps' 'Default' } '2' { - AddParameter 'RemoveApps' 'Remove the default selection of apps' - AddParameter 'RemoveCommApps' 'Remove the Mail, Calendar, and People apps' - AddParameter 'RemoveW11Outlook' 'Remove the new Outlook for Windows app' - AddParameter 'RemoveGamingApps' 'Remove the Xbox App and Xbox Gamebar' - AddParameter 'DisableDVR' 'Disable Xbox game/screen recording' - AddParameter 'DisableGameBarIntegration' 'Disable Game Bar integration' + AddParameter 'RemoveApps' + AddParameter 'Apps' 'Default' + AddParameter 'RemoveCommApps' + AddParameter 'RemoveW11Outlook' + AddParameter 'RemoveGamingApps' + + Write-Output "" + + if ($(Read-Host -Prompt "Disable Game Bar integration and game/screen recording? This also stops ms-gamingoverlay and ms-gamebar popups (y/n)" ) -eq 'y') { + AddParameter 'DisableDVR' + AddParameter 'DisableGameBarIntegration' + } } '3' { Write-Output "You have selected $($script:SelectedApps.Count) apps for removal" - AddParameter 'RemoveAppsCustom' "Remove $($script:SelectedApps.Count) apps:" + AddParameter 'RemoveAppsCustom' Write-Output "" - if ($( Read-Host -Prompt "Disable Game Bar integration and game/screen recording? This also stops ms-gamingoverlay and ms-gamebar popups (y/n)" ) -eq 'y') { - AddParameter 'DisableDVR' 'Disable Xbox game/screen recording' - AddParameter 'DisableGameBarIntegration' 'Disable Game Bar integration' + if ($(Read-Host -Prompt "Disable Game Bar integration and game/screen recording? This also stops ms-gamingoverlay and ms-gamebar popups (y/n)" ) -eq 'y') { + AddParameter 'DisableDVR' + AddParameter 'DisableGameBarIntegration' } } } @@ -1151,22 +1373,22 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt "Disable telemetry, diagnostic data, activity history, app-launch tracking and targeted ads? (y/n)" ) -eq 'y') { - AddParameter 'DisableTelemetry' 'Disable telemetry, diagnostic data, activity history, app-launch tracking & targeted ads' + AddParameter 'DisableTelemetry' } Write-Output "" if ($( Read-Host -Prompt "Disable tips, tricks, suggestions and ads in start, settings, notifications, explorer, lockscreen and Edge? (y/n)" ) -eq 'y') { - AddParameter 'DisableSuggestions' 'Disable tips, tricks, suggestions and ads in start, settings, notifications and File Explorer' - AddParameter 'DisableEdgeAds' 'Disable ads, suggestions and the MSN news feed in Microsoft Edge' - AddParameter 'DisableSettings365Ads' 'Disable Microsoft 365 ads in Settings Home' - AddParameter 'DisableLockscreenTips' 'Disable tips & tricks on the lockscreen' + AddParameter 'DisableSuggestions' + AddParameter 'DisableEdgeAds' + AddParameter 'DisableSettings365Ads' + AddParameter 'DisableLockscreenTips' } Write-Output "" if ($( Read-Host -Prompt "Disable & remove Bing web search, Bing AI and Cortana from Windows search? (y/n)" ) -eq 'y') { - AddParameter 'DisableBing' 'Disable & remove Bing web search, Bing AI and Cortana from Windows search' + AddParameter 'DisableBing' } # Only show this option for Windows 11 users running build 22621 or later @@ -1181,22 +1403,22 @@ function ShowCustomModeOptions { Write-Host " (2) Disable Microsoft Copilot, Windows Recall, Click to Do and AI features in Microsoft Edge, Paint and Notepad" -ForegroundColor Yellow $DisableAIInput = Read-Host "Do you want to disable any AI features? This applies to all users (n/1/2)" } - while ($DisableAIInput -ne 'n' -and $DisableAIInput -ne '0' -and $DisableAIInput -ne '1' -and $DisableAIInput -ne '2') + while ($DisableAIInput -ne 'n' -and $DisableAIInput -ne '0' -and $DisableAIInput -ne '1' -and $DisableAIInput -ne '2') # Select correct option based on user input switch ($DisableAIInput) { '1' { - AddParameter 'DisableCopilot' 'Disable & remove Microsoft Copilot' - AddParameter 'DisableRecall' 'Disable Windows Recall' - AddParameter 'DisableClickToDo' 'Disable Click to Do (AI text & image analysis)' + AddParameter 'DisableCopilot' + AddParameter 'DisableRecall' + AddParameter 'DisableClickToDo' } '2' { - AddParameter 'DisableCopilot' 'Disable & remove Microsoft Copilot' - AddParameter 'DisableRecall' 'Disable Windows Recall' - AddParameter 'DisableClickToDo' 'Disable Click to Do (AI text & image analysis)' - AddParameter 'DisableEdgeAI' 'Disable AI features in Edge' - AddParameter 'DisablePaintAI' 'Disable AI features in Paint' - AddParameter 'DisableNotepadAI' 'Disable AI features in Notepad' + AddParameter 'DisableCopilot' + AddParameter 'DisableRecall' + AddParameter 'DisableClickToDo' + AddParameter 'DisableEdgeAI' + AddParameter 'DisablePaintAI' + AddParameter 'DisableNotepadAI' } } } @@ -1204,20 +1426,20 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt "Disable Windows Spotlight background on desktop? (y/n)" ) -eq 'y') { - AddParameter 'DisableDesktopSpotlight' 'Disable the Windows Spotlight desktop background option.' + AddParameter 'DisableDesktopSpotlight' } Write-Output "" if ($( Read-Host -Prompt "Enable dark mode for system and apps? (y/n)" ) -eq 'y') { - AddParameter 'EnableDarkMode' 'Enable dark mode for system and apps' + AddParameter 'EnableDarkMode' } Write-Output "" if ($( Read-Host -Prompt "Disable transparency, animations and visual effects? (y/n)" ) -eq 'y') { - AddParameter 'DisableTransparency' 'Disable transparency effects' - AddParameter 'DisableAnimations' 'Disable animations and visual effects' + AddParameter 'DisableTransparency' + AddParameter 'DisableAnimations' } # Only show this option for Windows 11 users running build 22000 or later @@ -1225,14 +1447,14 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt "Restore the old Windows 10 style context menu? (y/n)" ) -eq 'y') { - AddParameter 'RevertContextMenu' 'Restore the old Windows 10 style context menu' + AddParameter 'RevertContextMenu' } } Write-Output "" if ($( Read-Host -Prompt "Turn off Enhance Pointer Precision, also known as mouse acceleration? (y/n)" ) -eq 'y') { - AddParameter 'DisableMouseAcceleration' 'Turn off Enhance Pointer Precision (mouse acceleration)' + AddParameter 'DisableMouseAcceleration' } # Only show this option for Windows 11 users running build 26100 or later @@ -1240,14 +1462,14 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt "Disable the Sticky Keys keyboard shortcut? (y/n)" ) -eq 'y') { - AddParameter 'DisableStickyKeys' 'Disable the Sticky Keys keyboard shortcut' + AddParameter 'DisableStickyKeys' } } Write-Output "" if ($( Read-Host -Prompt "Disable Fast Start-up? This applies to all users (y/n)" ) -eq 'y') { - AddParameter 'DisableFastStartup' 'Disable Fast Start-up' + AddParameter 'DisableFastStartup' } # Only show this option for Windows 11 users running build 22000 or later, and if the machine has at least one battery @@ -1255,7 +1477,7 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt "Disable network connectivity during Modern Standby? This applies to all users (y/n)" ) -eq 'y') { - AddParameter 'DisableModernStandbyNetworking' 'Disable network connectivity during Modern Standby' + AddParameter 'DisableModernStandbyNetworking' } } @@ -1267,19 +1489,19 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt " Hide the 'Include in library' option in the context menu? (y/n)" ) -eq 'y') { - AddParameter 'HideIncludeInLibrary' "Hide the 'Include in library' option in the context menu" + AddParameter 'HideIncludeInLibrary' } Write-Output "" if ($( Read-Host -Prompt " Hide the 'Give access to' option in the context menu? (y/n)" ) -eq 'y') { - AddParameter 'HideGiveAccessTo' "Hide the 'Give access to' option in the context menu" + AddParameter 'HideGiveAccessTo' } Write-Output "" if ($( Read-Host -Prompt " Hide the 'Share' option in the context menu? (y/n)" ) -eq 'y') { - AddParameter 'HideShare' "Hide the 'Share' option in the context menu" + AddParameter 'HideShare' } } } @@ -1293,7 +1515,7 @@ function ShowCustomModeOptions { if ($script:Params.ContainsKey("Sysprep")) { if ($( Read-Host -Prompt "Remove all pinned apps from the start menu for all existing and new users? (y/n)" ) -eq 'y') { - AddParameter 'ClearStartAllUsers' 'Remove all pinned apps from the start menu for existing and new users' + AddParameter 'ClearStartAllUsers' } } else { @@ -1302,17 +1524,17 @@ function ShowCustomModeOptions { Write-Host " (n) Don't remove any pinned apps from the start menu" -ForegroundColor Yellow Write-Host " (1) Remove all pinned apps from the start menu for this user only ($(GetUserName))" -ForegroundColor Yellow Write-Host " (2) Remove all pinned apps from the start menu for all existing and new users" -ForegroundColor Yellow - $ClearStartInput = Read-Host " Remove all pinned apps from the start menu? (n/1/2)" + $ClearStartInput = Read-Host " Remove all pinned apps from the start menu? (n/1/2)" } - while ($ClearStartInput -ne 'n' -and $ClearStartInput -ne '0' -and $ClearStartInput -ne '1' -and $ClearStartInput -ne '2') + while ($ClearStartInput -ne 'n' -and $ClearStartInput -ne '0' -and $ClearStartInput -ne '1' -and $ClearStartInput -ne '2') # Select correct option based on user input switch ($ClearStartInput) { '1' { - AddParameter 'ClearStart' "Remove all pinned apps from the start menu for this user only" + AddParameter 'ClearStart' } '2' { - AddParameter 'ClearStartAllUsers' "Remove all pinned apps from the start menu for all existing and new users" + AddParameter 'ClearStartAllUsers' } } } @@ -1322,14 +1544,14 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt " Disable the recommended section in the start menu? This applies to all users (y/n)" ) -eq 'y') { - AddParameter 'DisableStartRecommended' 'Disable the recommended section in the start menu.' + AddParameter 'DisableStartRecommended' } } Write-Output "" if ($( Read-Host -Prompt " Disable the Phone Link mobile devices integration in the start menu? (y/n)" ) -eq 'y') { - AddParameter 'DisableStartPhoneLink' 'Disable the Phone Link mobile devices integration in the start menu.' + AddParameter 'DisableStartPhoneLink' } } } @@ -1342,7 +1564,7 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt " Align taskbar buttons to the left side? (y/n)" ) -eq 'y') { - AddParameter 'TaskbarAlignLeft' 'Align taskbar icons to the left' + AddParameter 'TaskbarAlignLeft' } # Show options for combine icon on taskbar, only continue on valid input @@ -1353,23 +1575,23 @@ function ShowCustomModeOptions { Write-Host " (1) Always" -ForegroundColor Yellow Write-Host " (2) When taskbar is full" -ForegroundColor Yellow Write-Host " (3) Never" -ForegroundColor Yellow - $TbCombineTaskbar = Read-Host " Combine taskbar buttons and hide labels? (n/1/2/3)" + $TbCombineTaskbar = Read-Host " Combine taskbar buttons and hide labels? (n/1/2/3)" } - while ($TbCombineTaskbar -ne 'n' -and $TbCombineTaskbar -ne '0' -and $TbCombineTaskbar -ne '1' -and $TbCombineTaskbar -ne '2' -and $TbCombineTaskbar -ne '3') + while ($TbCombineTaskbar -ne 'n' -and $TbCombineTaskbar -ne '0' -and $TbCombineTaskbar -ne '1' -and $TbCombineTaskbar -ne '2' -and $TbCombineTaskbar -ne '3') # Select correct taskbar goup option based on user input switch ($TbCombineTaskbar) { '1' { - AddParameter 'CombineTaskbarAlways' 'Always combine taskbar buttons and hide labels for the main display' - AddParameter 'CombineMMTaskbarAlways' 'Always combine taskbar buttons and hide labels for secondary displays' + AddParameter 'CombineTaskbarAlways' + AddParameter 'CombineMMTaskbarAlways' } '2' { - AddParameter 'CombineTaskbarWhenFull' 'Combine taskbar buttons and hide labels when taskbar is full for the main display' - AddParameter 'CombineMMTaskbarWhenFull' 'Combine taskbar buttons and hide labels when taskbar is full for secondary displays' + AddParameter 'CombineTaskbarWhenFull' + AddParameter 'CombineMMTaskbarWhenFull' } '3' { - AddParameter 'CombineTaskbarNever' 'Never combine taskbar buttons and show labels for the main display' - AddParameter 'CombineMMTaskbarNever' 'Never combine taskbar buttons and show labels for secondary displays' + AddParameter 'CombineTaskbarNever' + AddParameter 'CombineMMTaskbarNever' } } @@ -1381,20 +1603,20 @@ function ShowCustomModeOptions { Write-Host " (1) Show app icons on all taskbars" -ForegroundColor Yellow Write-Host " (2) Show app icons on main taskbar and on taskbar where the windows is open" -ForegroundColor Yellow Write-Host " (3) Show app icons only on taskbar where the window is open" -ForegroundColor Yellow - $TbCombineTaskbar = Read-Host " Change how to show app icons on the taskbar when using multiple monitors? (n/1/2/3)" + $TbCombineTaskbar = Read-Host " Change how to show app icons on the taskbar when using multiple monitors? (n/1/2/3)" } - while ($TbCombineTaskbar -ne 'n' -and $TbCombineTaskbar -ne '0' -and $TbCombineTaskbar -ne '1' -and $TbCombineTaskbar -ne '2' -and $TbCombineTaskbar -ne '3') + while ($TbCombineTaskbar -ne 'n' -and $TbCombineTaskbar -ne '0' -and $TbCombineTaskbar -ne '1' -and $TbCombineTaskbar -ne '2' -and $TbCombineTaskbar -ne '3') # Select correct taskbar goup option based on user input switch ($TbCombineTaskbar) { '1' { - AddParameter 'MMTaskbarModeAll' 'Show app icons on all taskbars' + AddParameter 'MMTaskbarModeAll' } '2' { - AddParameter 'MMTaskbarModeMainActive' 'Show app icons on main taskbar and on taskbar where the windows is open' + AddParameter 'MMTaskbarModeMainActive' } '3' { - AddParameter 'MMTaskbarModeActive' 'Show app icons only on taskbar where the window is open' + AddParameter 'MMTaskbarModeActive' } } @@ -1407,37 +1629,37 @@ function ShowCustomModeOptions { Write-Host " (2) Show search icon on the taskbar" -ForegroundColor Yellow Write-Host " (3) Show search icon with label on the taskbar" -ForegroundColor Yellow Write-Host " (4) Show search box on the taskbar" -ForegroundColor Yellow - $TbSearchInput = Read-Host " Hide or change the search icon on the taskbar? (n/1/2/3/4)" + $TbSearchInput = Read-Host " Hide or change the search icon on the taskbar? (n/1/2/3/4)" } - while ($TbSearchInput -ne 'n' -and $TbSearchInput -ne '0' -and $TbSearchInput -ne '1' -and $TbSearchInput -ne '2' -and $TbSearchInput -ne '3' -and $TbSearchInput -ne '4') + while ($TbSearchInput -ne 'n' -and $TbSearchInput -ne '0' -and $TbSearchInput -ne '1' -and $TbSearchInput -ne '2' -and $TbSearchInput -ne '3' -and $TbSearchInput -ne '4') # Select correct taskbar search option based on user input switch ($TbSearchInput) { '1' { - AddParameter 'HideSearchTb' 'Hide search icon from the taskbar' + AddParameter 'HideSearchTb' } '2' { - AddParameter 'ShowSearchIconTb' 'Show search icon on the taskbar' + AddParameter 'ShowSearchIconTb' } '3' { - AddParameter 'ShowSearchLabelTb' 'Show search icon with label on the taskbar' + AddParameter 'ShowSearchLabelTb' } '4' { - AddParameter 'ShowSearchBoxTb' 'Show search box on the taskbar' + AddParameter 'ShowSearchBoxTb' } } Write-Output "" if ($( Read-Host -Prompt " Hide the taskview button from the taskbar? (y/n)" ) -eq 'y') { - AddParameter 'HideTaskview' 'Hide the taskview button from the taskbar' + AddParameter 'HideTaskview' } } Write-Output "" if ($( Read-Host -Prompt " Disable the widgets service to remove widgets on the taskbar & lockscreen? (y/n)" ) -eq 'y') { - AddParameter 'DisableWidgets' 'Disable widgets on the taskbar & lockscreen' + AddParameter 'DisableWidgets' } # Only show this options for Windows users running build 22621 or earlier @@ -1445,22 +1667,22 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt " Hide the chat (meet now) icon from the taskbar? (y/n)" ) -eq 'y') { - AddParameter 'HideChat' 'Hide the chat (meet now) icon from the taskbar' + AddParameter 'HideChat' } } - + # Only show this options for Windows users running build 22631 or later if ($WinVersion -ge 22631) { Write-Output "" if ($( Read-Host -Prompt " Enable the 'End Task' option in the taskbar right click menu? (y/n)" ) -eq 'y') { - AddParameter 'EnableEndTask' "Enable the 'End Task' option in the taskbar right click menu" + AddParameter 'EnableEndTask' } } - + Write-Output "" if ($( Read-Host -Prompt " Enable the 'Last Active Click' behavior in the taskbar app area? (y/n)" ) -eq 'y') { - AddParameter 'EnableLastActiveClick' "Enable the 'Last Active Click' behavior in the taskbar app area" + AddParameter 'EnableLastActiveClick' } } @@ -1476,36 +1698,36 @@ function ShowCustomModeOptions { Write-Host " (2) Open File Explorer to 'This PC'" -ForegroundColor Yellow Write-Host " (3) Open File Explorer to 'Downloads'" -ForegroundColor Yellow Write-Host " (4) Open File Explorer to 'OneDrive'" -ForegroundColor Yellow - $ExplSearchInput = Read-Host " Change the default location that File Explorer opens to? (n/1/2/3/4)" + $ExplSearchInput = Read-Host " Change the default location that File Explorer opens to? (n/1/2/3/4)" } - while ($ExplSearchInput -ne 'n' -and $ExplSearchInput -ne '0' -and $ExplSearchInput -ne '1' -and $ExplSearchInput -ne '2' -and $ExplSearchInput -ne '3' -and $ExplSearchInput -ne '4') + while ($ExplSearchInput -ne 'n' -and $ExplSearchInput -ne '0' -and $ExplSearchInput -ne '1' -and $ExplSearchInput -ne '2' -and $ExplSearchInput -ne '3' -and $ExplSearchInput -ne '4') # Select correct taskbar search option based on user input switch ($ExplSearchInput) { '1' { - AddParameter 'ExplorerToHome' "Change the default location that File Explorer opens to 'Home'" + AddParameter 'ExplorerToHome' } '2' { - AddParameter 'ExplorerToThisPC' "Change the default location that File Explorer opens to 'This PC'" + AddParameter 'ExplorerToThisPC' } '3' { - AddParameter 'ExplorerToDownloads' "Change the default location that File Explorer opens to 'Downloads'" + AddParameter 'ExplorerToDownloads' } '4' { - AddParameter 'ExplorerToOneDrive' "Change the default location that File Explorer opens to 'OneDrive'" + AddParameter 'ExplorerToOneDrive' } } Write-Output "" if ($( Read-Host -Prompt " Show hidden files, folders and drives? (y/n)" ) -eq 'y') { - AddParameter 'ShowHiddenFolders' 'Show hidden files, folders and drives' + AddParameter 'ShowHiddenFolders' } Write-Output "" if ($( Read-Host -Prompt " Show file extensions for known file types? (y/n)" ) -eq 'y') { - AddParameter 'ShowKnownFileExt' 'Show file extensions for known file types' + AddParameter 'ShowKnownFileExt' } # Only show this option for Windows 11 users running build 22000 or later @@ -1513,20 +1735,20 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt " Hide the Home section from the File Explorer sidepanel? (y/n)" ) -eq 'y') { - AddParameter 'HideHome' 'Hide the Home section from the File Explorer sidepanel' + AddParameter 'HideHome' } Write-Output "" if ($( Read-Host -Prompt " Hide the Gallery section from the File Explorer sidepanel? (y/n)" ) -eq 'y') { - AddParameter 'HideGallery' 'Hide the Gallery section from the File Explorer sidepanel' + AddParameter 'HideGallery' } } Write-Output "" if ($( Read-Host -Prompt " Hide duplicate removable drive entries from the File Explorer sidepanel so they only show under This PC? (y/n)" ) -eq 'y') { - AddParameter 'HideDupliDrive' 'Hide duplicate removable drive entries from the File Explorer sidepanel' + AddParameter 'HideDupliDrive' } # Only show option for disabling these specific folders for Windows 10 users @@ -1537,23 +1759,28 @@ function ShowCustomModeOptions { Write-Output "" if ($( Read-Host -Prompt " Hide the OneDrive folder from the File Explorer sidepanel? (y/n)" ) -eq 'y') { - AddParameter 'HideOnedrive' 'Hide the OneDrive folder in the File Explorer sidepanel' + AddParameter 'HideOnedrive' } Write-Output "" - + if ($( Read-Host -Prompt " Hide the 3D objects folder from the File Explorer sidepanel? (y/n)" ) -eq 'y') { - AddParameter 'Hide3dObjects' "Hide the 3D objects folder under 'This pc' in File Explorer" + AddParameter 'Hide3dObjects' } - + Write-Output "" if ($( Read-Host -Prompt " Hide the music folder from the File Explorer sidepanel? (y/n)" ) -eq 'y') { - AddParameter 'HideMusic' "Hide the music folder under 'This pc' in File Explorer" + AddParameter 'HideMusic' } } } } + + # Only save settings if any changes were selected by the user + if ($script:Params.Keys.Count -gt 1) { + SaveSettings + } # Suppress prompt if Silent parameter was passed if (-not $Silent) { @@ -1571,11 +1798,15 @@ function ShowCustomModeOptions { function ShowAppRemoval { PrintHeader "App Removal" + Write-Output "> Opening app selection form..." + $result = ShowAppSelectionForm if ($result -eq [System.Windows.Forms.DialogResult]::OK) { Write-Output "You have selected $($script:SelectedApps.Count) apps for removal" - AddParameter 'RemoveAppsCustom' "Remove $($script:SelectedApps.Count) apps:" + AddParameter 'RemoveAppsCustom' + + SaveSettings # Suppress prompt if Silent parameter was passed if (-not $Silent) { @@ -1593,46 +1824,44 @@ function ShowAppRemoval { } -function LoadAndShowSavedSettings { +function LoadAndShowLastUsedSettings { PrintHeader 'Custom Mode' - Write-Output "Win11Debloat will make the following changes:" - # Print the saved settings info from file - Foreach ($line in (Get-Content -Path "$PSScriptRoot/SavedSettings" )) { - # Remove any spaces before and after the line - $line = $line.Trim() + try { + $savedSettings = (Get-Content -Path $script:SavedSettingsFilePath -Raw | ConvertFrom-Json) + if ($savedSettings.Version -and $savedSettings.Version -ne "1.0") { + Write-Warning "LastUsedSettings.json version mismatch (expected 1.0, found $($savedSettings.Version))" + } + + if (-not $savedSettings.Settings) { + throw + } + + # Add settings from LastUsedSettings.json to Params + Foreach ($parameter in $savedSettings.Settings) { + $parameterName = $parameter.Name + $value = $parameter.Value - # Check if the line contains a comment - if (-not ($line.IndexOf('#') -eq -1)) { - $parameterName = $line.Substring(0, $line.IndexOf('#')) - - # Print parameter description and add parameter to Params list - switch ($parameterName) { - 'RemoveApps' { - PrintAppsList "$PSScriptRoot/Appslist.txt" $true - } - 'RemoveAppsCustom' { - PrintAppsList "$PSScriptRoot/CustomAppsList" $true - } - default { - Write-Output $line.Substring(($line.IndexOf('#') + 1), ($line.Length - $line.IndexOf('#') - 1)) - } + # Skip parameters that are set to false in the config + if ($value -eq $false) { + continue } - + + # Add parameter to Params if (-not $script:Params.ContainsKey($parameterName)) { - $script:Params.Add($parameterName, $true) + $script:Params.Add($parameterName, $value) + } + else { + $script:Params[$parameterName] = $value } } } - - # Suppress prompt if Silent parameter was passed - if (-not $Silent) { - Write-Output "" - Write-Output "" - Write-Output "Press enter to execute the script or press CTRL+C to quit..." - Read-Host | Out-Null + catch { + Write-Error "Failed to load settings from LastUsedSettings.json file" + AwaitKeyToExit } + PrintPendingChanges PrintHeader 'Custom Mode' } @@ -1648,14 +1877,14 @@ function LoadAndShowSavedSettings { # Check if winget is installed & if it is, check if the version is at least v1.4 if ((Get-AppxPackage -Name "*Microsoft.DesktopAppInstaller*") -and ([int](((winget -v) -replace 'v','').split('.')[0..1] -join '') -gt 14)) { - $script:wingetInstalled = $true + $script:WingetInstalled = $true } else { - $script:wingetInstalled = $false + $script:WingetInstalled = $false # Show warning that requires user confirmation, Suppress confirmation if Silent parameter was passed if (-not $Silent) { - Write-Warning "Winget is not installed or outdated. This may prevent Win11Debloat from removing certain apps." + Write-Warning "Winget is not installed or outdated, this may prevent Win11Debloat from removing certain apps" Write-Output "" Write-Output "Press any key to continue anyway..." $null = [System.Console]::ReadKey() @@ -1669,15 +1898,18 @@ $WinVersion = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\Current $script:ModernStandbySupported = CheckModernStandbySupport $script:Params = $PSBoundParameters -$script:FirstSelection = $true -$SPParams = 'WhatIf', 'Confirm', 'Verbose', 'Silent', 'Sysprep', 'Debug', 'User', 'CreateRestorePoint', 'LogPath' -$SPParamCount = 0 -# Count how many SPParams exist within Params -# This is later used to check if any options were selected -foreach ($Param in $SPParams) { +# Add default Apps parameter when RemoveApps is requested and Apps was not explicitly provided +if ((-not $script:Params.ContainsKey("Apps")) -and $script:Params.ContainsKey("RemoveApps")) { + $script:Params.Add('Apps', 'Default') +} + +$controlParamsCount = 0 + +# Count how many control parameters are set, to determine if any changes were selected by the user during runtime +foreach ($Param in $script:ControlParams) { if ($script:Params.ContainsKey($Param)) { - $SPParamCount++ + $controlParamsCount++ } } @@ -1699,7 +1931,7 @@ if ($script:Params.ContainsKey("Sysprep")) { # Exit script if run in Sysprep mode on Windows 10 if ($WinVersion -lt 22000) { - Write-Host "Error: Win11Debloat Sysprep mode is not supported on Windows 10" -ForegroundColor Red + Write-Error "Win11Debloat Sysprep mode is not supported on Windows 10" AwaitKeyToExit } } @@ -1709,9 +1941,9 @@ if ($script:Params.ContainsKey("User")) { $userPath = GetUserDirectory -userName $script:Params.Item("User") } -# Remove SavedSettings file if it exists and is empty -if ((Test-Path "$PSScriptRoot/SavedSettings") -and ([String]::IsNullOrWhiteSpace((Get-content "$PSScriptRoot/SavedSettings")))) { - Remove-Item -Path "$PSScriptRoot/SavedSettings" -recurse +# Remove LastUsedSettings.json file if it exists and is empty +if ((Test-Path $script:SavedSettingsFilePath) -and ([String]::IsNullOrWhiteSpace((Get-content $script:SavedSettingsFilePath)))) { + Remove-Item -Path $script:SavedSettingsFilePath -recurse } # Only run the app selection form if the 'RunAppsListGenerator' parameter was passed to the script @@ -1733,14 +1965,14 @@ if ($RunAppConfigurator -or $RunAppsListGenerator) { } # Change script execution based on provided parameters or user input -if ((-not $script:Params.Count) -or $RunDefaults -or $RunDefaultsLite -or $RunSavedSettings -or ($SPParamCount -eq $script:Params.Count)) { +if ((-not $script:Params.Count) -or $RunDefaults -or $RunDefaultsLite -or $RunSavedSettings -or ($controlParamsCount -eq $script:Params.Count)) { if ($RunDefaults -or $RunDefaultsLite) { $Mode = '1' } elseif ($RunSavedSettings) { - if (-not (Test-Path "$PSScriptRoot/SavedSettings")) { + if (-not (Test-Path $script:SavedSettingsFilePath)) { PrintHeader 'Custom Mode' - Write-Host "Error: No saved settings found, no changes were made" -ForegroundColor Red + Write-Error "Unable to find LastUsedSettings.json file, no changes were made" AwaitKeyToExit } @@ -1754,7 +1986,7 @@ if ((-not $script:Params.Count) -or $RunDefaults -or $RunDefaultsLite -or $RunSa switch ($Mode) { # Default mode, loads defaults and app removal options '1' { - ShowDefaultMode + ShowDefaultModeOptions } # Custom mode, shows all available options for user selection @@ -1767,9 +1999,9 @@ if ((-not $script:Params.Count) -or $RunDefaults -or $RunDefaultsLite -or $RunSa ShowAppRemoval } - # Load last used custom options from the "SavedSettings" file + # Load last used options from the "LastUsedSettings.json" file '4' { - LoadAndShowSavedSettings + LoadAndShowLastUsedSettings } } } @@ -1777,11 +2009,10 @@ else { PrintHeader 'Custom Mode' } -# If the number of keys in SPParams equals the number of keys in Params then no modifications/changes were selected +# If the number of keys in ControlParams equals the number of keys in Params then no modifications/changes were selected # or added by the user, and the script can exit without making any changes. -if (($SPParamCount -eq $script:Params.Keys.Count) -or (($script:Params.Keys.Count -eq 1) -and ($script:Params.Keys -contains 'CreateRestorePoint'))) { +if (($controlParamsCount -eq $script:Params.Keys.Count) -or ($script:Params.Keys.Count -eq 1 -and ($script:Params.Keys -contains 'CreateRestorePoint' -or $script:Params.Keys -contains 'Apps'))) { Write-Output "The script completed without making any changes." - AwaitKeyToExit } @@ -1793,20 +2024,30 @@ if ($script:Params.ContainsKey("CreateRestorePoint")) { # Execute all selected/provided parameters switch ($script:Params.Keys) { 'RemoveApps' { - $appsList = ReadAppslistFromFile "$PSScriptRoot/Appslist.txt" - Write-Output "> Removing default selection of $($appsList.Count) apps..." + Write-Output "> Removing selected apps..." + $appsList = GenerateAppsList + + if ($appsList.Count -eq 0) { + Write-Host "No valid apps were selected for removal" -ForegroundColor Yellow + Write-Output "" + continue + } + + Write-Output "$($appsList.Count) apps selected for removal" RemoveApps $appsList continue } 'RemoveAppsCustom' { - if (-not (Test-Path "$PSScriptRoot/CustomAppsList")) { - Write-Host "> Error: Could not load custom apps list from file, no apps were removed" -ForegroundColor Red + Write-Output "> Removing selected apps..." + $appsList = ReadAppslistFromFile $script:CustomAppsListFilePath + + if ($appsList.Count -eq 0) { + Write-Host "No valid apps were selected for removal" -ForegroundColor Yellow Write-Output "" continue } - - $appsList = ReadAppslistFromFile "$PSScriptRoot/CustomAppsList" - Write-Output "> Removing $($appsList.Count) apps..." + + Write-Output "$($appsList.Count) apps selected for removal" RemoveApps $appsList continue } @@ -1876,7 +2117,7 @@ switch ($script:Params.Keys) { } {$_ -in "DisableBingSearches", "DisableBing"} { RegImport "> Disabling Bing web search, Bing AI and Cortana from Windows search..." "Disable_Bing_Cortana_In_Search.reg" - + # Also remove the app package for Bing search $appsList = 'Microsoft.BingSearch' RemoveApps $appsList @@ -1891,10 +2132,24 @@ switch ($script:Params.Keys) { continue } 'DisableRecall' { + if ($WinVersion -lt 22000) { + Write-Output "> Disabling Windows Recall..." + Write-Host "Feature is not available on Windows 10" -ForegroundColor Yellow + Write-Output "" + continue + } + RegImport "> Disabling Windows Recall..." "Disable_AI_Recall.reg" continue } 'DisableClickToDo' { + if ($WinVersion -lt 22000) { + Write-Output "> Disabling Click to Do..." + Write-Host "Feature is not available on Windows 10" -ForegroundColor Yellow + Write-Output "" + continue + } + RegImport "> Disabling Click to Do..." "Disable_Click_to_Do.reg" continue } @@ -1927,6 +2182,13 @@ switch ($script:Params.Keys) { continue } 'DisableModernStandbyNetworking' { + if (-not $script:ModernStandbySupported) { + Write-Output "> Disabling network connectivity during Modern Standby..." + Write-Host "Device does not support modern standby" -ForegroundColor Yellow + Write-Output "" + continue + } + RegImport "> Disabling network connectivity during Modern Standby..." "Disable_Modern_Standby_Networking.reg" continue } @@ -2039,6 +2301,13 @@ switch ($script:Params.Keys) { continue } {$_ -in "HideChat", "DisableChat"} { + if ($WinVersion -ge 22000) { + Write-Output "> Hiding the chat icon from the taskbar..." + Write-Host "Feature is not available on Windows 11" -ForegroundColor Yellow + Write-Output "" + continue + } + RegImport "> Hiding the chat icon from the taskbar..." "Disable_Chat_Taskbar.reg" continue } @@ -2091,6 +2360,13 @@ switch ($script:Params.Keys) { continue } {$_ -in "Hide3dObjects", "Disable3dObjects"} { + if ($WinVersion -ge 22000) { + Write-Output "> Hiding the 3D objects folder from the File Explorer navigation pane..." + Write-Host "Feature is not available on Windows 11" -ForegroundColor Yellow + Write-Output "" + continue + } + RegImport "> Hiding the 3D objects folder from the File Explorer navigation pane..." "Hide_3D_Objects_Folder.reg" continue }