<# LXS - Debloat and privacy (Windows) Description: Remove preinstalled apps you pick, and turn off telemetry, Cortana, ads, tracking tasks, Copilot and Recall. Safety rules this script follows: - a restore point is created before the first change of a session - no app is removed unless you explicitly select it - the registry/service/task tweaks can be undone from the menu (option 8) - apps you remove come back only by reinstalling them from the Store Repo: https://git.hyko.cx/hykocx/lxs #> # Load LXS common library (colors, UI helpers, spinner, loggers, guards). # Prefers the sibling ..\lib\common.ps1 (repo checkout or installed layout) and # only hits the network when this script is run standalone. $LxsRawBase = if ($env:LXS_RAW_BASE) { $env:LXS_RAW_BASE } else { 'https://git.hyko.cx/hykocx/lxs/raw/branch/main' } if (-not $env:LXS_RAW_PLATFORM_BASE) { $env:LXS_RAW_PLATFORM_BASE = "$LxsRawBase/windows" } $LxsLibPath = if ($PSScriptRoot) { Join-Path $PSScriptRoot '..\lib\common.ps1' } else { $null } if ($LxsLibPath -and (Test-Path $LxsLibPath)) { . $LxsLibPath } else { try { $LxsLibSource = Invoke-RestMethod -Uri "$env:LXS_RAW_PLATFORM_BASE/lib/common.ps1" -UseBasicParsing -ErrorAction Stop } catch { Write-Error 'Failed to fetch lib/common.ps1' exit 1 } . ([scriptblock]::Create($LxsLibSource)) } $env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_debloat.log' if (-not (Assert-LxsWindows)) { exit 1 } Assert-LxsAdmin -ScriptPath $PSCommandPath -Arguments $args $script:LxsCheckpointDone = $false # One restore point per session, created lazily before the first real change. function Assert-LxsCheckpoint { if ($script:LxsCheckpointDone) { return $true } Write-Host '' Write-LxsInfo 'Creating a restore point before changing anything...' $ok = New-LxsRestorePoint -Description 'LXS before debloat' if (-not $ok) { Write-Host '' Write-LxsWarn 'The restore point could not be created.' if (-not (Confirm-LxsAction -Question 'Continue anyway, without a rollback point?')) { return $false } } $script:LxsCheckpointDone = $true return $true } # ═══════════════════════════════════════════════════════════════════════════ # Preinstalled apps # # Only packages on this list are ever offered. Anything not listed here — # including the Store, App Installer (winget), Terminal, Defender UI, the # VCLibs/.NET runtime frameworks and every driver package — is left alone, # because removing those breaks Windows or LXS itself. # ═══════════════════════════════════════════════════════════════════════════ $LxsRemovableApps = @( @{ Pattern = 'Microsoft.3DBuilder'; Label = '3D Builder' } @{ Pattern = 'Microsoft.549981C3F5F10'; Label = 'Cortana' } @{ Pattern = 'Microsoft.BingFinance'; Label = 'Bing Finance' } @{ Pattern = 'Microsoft.BingNews'; Label = 'Bing News' } @{ Pattern = 'Microsoft.BingSports'; Label = 'Bing Sports' } @{ Pattern = 'Microsoft.BingWeather'; Label = 'Weather' } @{ Pattern = 'Microsoft.BingSearch'; Label = 'Web Search (Bing)' } @{ Pattern = 'Microsoft.GetHelp'; Label = 'Get Help' } @{ Pattern = 'Microsoft.Getstarted'; Label = 'Tips / Get Started' } @{ Pattern = 'Microsoft.Messaging'; Label = 'Messaging' } @{ Pattern = 'Microsoft.Microsoft3DViewer'; Label = '3D Viewer' } @{ Pattern = 'Microsoft.MicrosoftJournal'; Label = 'Journal' } @{ Pattern = 'Microsoft.MicrosoftOfficeHub'; Label = 'Office Hub' } @{ Pattern = 'Microsoft.MicrosoftSolitaireCollection'; Label = 'Solitaire Collection' } @{ Pattern = 'Microsoft.MixedReality.Portal'; Label = 'Mixed Reality Portal' } @{ Pattern = 'Microsoft.NetworkSpeedTest'; Label = 'Network Speed Test' } @{ Pattern = 'Microsoft.News'; Label = 'News' } @{ Pattern = 'Microsoft.Office.OneNote'; Label = 'OneNote (Store version)' } @{ Pattern = 'Microsoft.Office.Sway'; Label = 'Sway' } @{ Pattern = 'Microsoft.OneConnect'; Label = 'Mobile Plans' } @{ Pattern = 'Microsoft.People'; Label = 'People' } @{ Pattern = 'Microsoft.PowerAutomateDesktop'; Label = 'Power Automate Desktop' } @{ Pattern = 'Microsoft.Print3D'; Label = 'Print 3D' } @{ Pattern = 'Microsoft.SkypeApp'; Label = 'Skype' } @{ Pattern = 'Microsoft.Todos'; Label = 'Microsoft To Do' } @{ Pattern = 'Microsoft.Wallet'; Label = 'Wallet' } @{ Pattern = 'Microsoft.WindowsAlarms'; Label = 'Alarms & Clock' } @{ Pattern = 'Microsoft.WindowsFeedbackHub'; Label = 'Feedback Hub' } @{ Pattern = 'Microsoft.WindowsMaps'; Label = 'Maps' } @{ Pattern = 'Microsoft.WindowsSoundRecorder'; Label = 'Sound Recorder' } @{ Pattern = 'Microsoft.Xbox.TCUI'; Label = 'Xbox TCUI' } @{ Pattern = 'Microsoft.XboxApp'; Label = 'Xbox Console Companion' } @{ Pattern = 'Microsoft.XboxGameOverlay'; Label = 'Xbox Game Overlay' } @{ Pattern = 'Microsoft.XboxGamingOverlay'; Label = 'Xbox Game Bar' } @{ Pattern = 'Microsoft.XboxIdentityProvider'; Label = 'Xbox Identity Provider' } @{ Pattern = 'Microsoft.XboxSpeechToTextOverlay'; Label = 'Xbox Speech To Text' } @{ Pattern = 'Microsoft.YourPhone'; Label = 'Phone Link' } @{ Pattern = 'Microsoft.ZuneMusic'; Label = 'Media Player / Groove Music' } @{ Pattern = 'Microsoft.ZuneVideo'; Label = 'Movies & TV' } @{ Pattern = 'MicrosoftTeams'; Label = 'Teams (personal / chat)' } @{ Pattern = 'MSTeams'; Label = 'Teams (new client)' } @{ Pattern = 'Clipchamp.Clipchamp'; Label = 'Clipchamp' } @{ Pattern = 'Microsoft.Copilot'; Label = 'Copilot app' } @{ Pattern = 'Microsoft.WindowsFamilySafety'; Label = 'Family Safety' } @{ Pattern = 'SpotifyAB.SpotifyMusic'; Label = 'Spotify (OEM stub)' } @{ Pattern = 'Disney.37853FC22B2CE'; Label = 'Disney+ (OEM stub)' } ) function Get-LxsInstalledBloat { $found = @() foreach ($entry in $LxsRemovableApps) { try { $pkgs = @(Get-AppxPackage -Name $entry.Pattern -ErrorAction SilentlyContinue) } catch { $pkgs = @() } if ($pkgs.Count -gt 0) { $found += [pscustomobject]@{ Label = $entry.Label Pattern = $entry.Pattern Name = $pkgs[0].Name Version = $pkgs[0].Version } } } return $found } function Remove-LxsSelectedApps { Clear-Host Show-LxsBoxTop -Title 'REMOVE PREINSTALLED APPS' Write-Host '' Write-LxsInfo 'Scanning installed packages...' $found = @(Get-LxsInstalledBloat) Write-Host '' if ($found.Count -eq 0) { Write-LxsOk 'None of the known preinstalled apps are present.' return } for ($i = 0; $i -lt $found.Count; $i++) { Write-Host (" $($script:Cyan)[{0,2}]$($script:NC) $($script:White){1,-32}$($script:NC) $($script:Gray){2}$($script:NC)" -f ` ($i + 1), $found[$i].Label, $found[$i].Name) } Write-Host '' Write-Host "$($script:Gray)Enter the numbers to remove, separated by commas (e.g. 1,4,7),$($script:NC)" Write-Host "$($script:Gray)or 'all' for every entry above. Empty cancels — nothing is removed$($script:NC)" Write-Host "$($script:Gray)unless you name it here.$($script:NC)" Write-Host '' $selection = Read-Host -Prompt 'Selection' if ([string]::IsNullOrWhiteSpace($selection)) { Write-LxsInfo 'Cancelled — nothing removed.' return } $targets = @() if ($selection.Trim() -ieq 'all') { $targets = $found } else { foreach ($part in ($selection -split ',')) { $part = $part.Trim() if ($part -notmatch '^\d+$') { Write-LxsErr "Not a number: $part" return } $idx = [int]$part - 1 if ($idx -lt 0 -or $idx -ge $found.Count) { Write-LxsErr "Out of range: $part" return } $targets += $found[$idx] } } $targets = @($targets | Sort-Object Name -Unique) Write-Host '' Write-Host 'These will be removed for the current user:' foreach ($t in $targets) { Write-Host " - $($t.Label)" } Write-Host '' Write-Host "$($script:Gray)They can be reinstalled later from the Microsoft Store.$($script:NC)" Write-Host '' if (-not (Confirm-LxsAction -Question "Remove $($targets.Count) app(s)?")) { Write-LxsInfo 'Cancelled.' return } if (-not (Assert-LxsCheckpoint)) { return } $alsoProvisioned = Confirm-LxsAction -Question 'Also remove them from the image, so new user accounts do not get them?' Write-Host '' foreach ($t in $targets) { try { Get-AppxPackage -Name $t.Pattern -ErrorAction Stop | Remove-AppxPackage -ErrorAction Stop Write-LxsOk "$($t.Label) removed" } catch { Write-LxsWarn "$($t.Label): $($_.Exception.Message)" } if ($alsoProvisioned) { try { $prov = @(Get-AppxProvisionedPackage -Online -ErrorAction Stop | Where-Object { $_.DisplayName -like $t.Pattern }) foreach ($p in $prov) { Remove-AppxProvisionedPackage -Online -PackageName $p.PackageName -ErrorAction Stop | Out-Null Write-Host "$($script:Gray) also removed from the image$($script:NC)" } } catch { Write-LxsWarn "$($t.Label): could not remove the provisioned package." } } } } # ═══════════════════════════════════════════════════════════════════════════ # Privacy tweaks # # Every entry carries the value to set and the value to restore, so option 8 # can undo exactly what option 2-6 did. Default = $null means "delete the # value", which is what returns a policy key to its Windows default. # ═══════════════════════════════════════════════════════════════════════════ $LxsTweaks = @( # --- telemetry --- @{ Group = 'telemetry'; Label = 'Diagnostic data set to the minimum'; Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection'; Name = 'AllowTelemetry'; Value = 0; Default = $null } @{ Group = 'telemetry'; Label = 'Do not send device names with telemetry'; Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection'; Name = 'AllowDeviceNameInTelemetry'; Value = 0; Default = $null } @{ Group = 'telemetry'; Label = 'Advertising ID disabled'; Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\AdvertisingInfo'; Name = 'Enabled'; Value = 0; Default = 1 } @{ Group = 'telemetry'; Label = 'App launch tracking disabled'; Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced'; Name = 'Start_TrackProgs'; Value = 0; Default = 1 } @{ Group = 'telemetry'; Label = 'Feedback requests disabled'; Path = 'HKCU:\SOFTWARE\Microsoft\Siuf\Rules'; Name = 'NumberOfSIUFInPeriod'; Value = 0; Default = $null } # --- cortana / search --- @{ Group = 'cortana'; Label = 'Cortana disabled'; Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search'; Name = 'AllowCortana'; Value = 0; Default = $null } @{ Group = 'cortana'; Label = 'Web results in Start disabled'; Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search'; Name = 'DisableWebSearch'; Value = 1; Default = $null } @{ Group = 'cortana'; Label = 'Connected web search disabled'; Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search'; Name = 'ConnectedSearchUseWeb'; Value = 0; Default = $null } @{ Group = 'cortana'; Label = 'Search highlights disabled'; Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\SearchSettings'; Name = 'IsDynamicSearchBoxEnabled'; Value = 0; Default = 1 } # --- ads and suggestions --- @{ Group = 'ads'; Label = 'Start menu app suggestions off'; Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager'; Name = 'SystemPaneSuggestionsEnabled'; Value = 0; Default = 1 } @{ Group = 'ads'; Label = 'Silent install of promoted apps off'; Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager'; Name = 'SilentInstalledAppsEnabled'; Value = 0; Default = 1 } @{ Group = 'ads'; Label = 'Lock screen spotlight ads off'; Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager'; Name = 'RotatingLockScreenOverlayEnabled'; Value = 0; Default = 1 } @{ Group = 'ads'; Label = 'Windows tips and tricks off'; Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager'; Name = 'SubscribedContent-338389Enabled'; Value = 0; Default = 1 } @{ Group = 'ads'; Label = 'Suggested content in Settings off'; Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager'; Name = 'SubscribedContent-338393Enabled'; Value = 0; Default = 1 } @{ Group = 'ads'; Label = 'Welcome experience after updates off'; Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager'; Name = 'SubscribedContent-310093Enabled'; Value = 0; Default = 1 } # --- copilot / recall --- @{ Group = 'ai'; Label = 'Windows Copilot turned off'; Path = 'HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot'; Name = 'TurnOffWindowsCopilot'; Value = 1; Default = $null } @{ Group = 'ai'; Label = 'Windows Copilot turned off (machine-wide)'; Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot'; Name = 'TurnOffWindowsCopilot'; Value = 1; Default = $null } @{ Group = 'ai'; Label = 'Recall snapshots disabled'; Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI'; Name = 'DisableAIDataAnalysis'; Value = 1; Default = $null } @{ Group = 'ai'; Label = 'Recall snapshots disabled (per user)'; Path = 'HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI'; Name = 'DisableAIDataAnalysis'; Value = 1; Default = $null } ) $LxsTrackingTasks = @( '\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraisal', '\Microsoft\Windows\Application Experience\ProgramDataUpdater', '\Microsoft\Windows\Application Experience\StartupAppTask', '\Microsoft\Windows\Customer Experience Improvement Program\Consolidator', '\Microsoft\Windows\Customer Experience Improvement Program\UsbCeip', '\Microsoft\Windows\Autochk\Proxy', '\Microsoft\Windows\Feedback\Siuf\DmClient', '\Microsoft\Windows\Feedback\Siuf\DmClientOnScenarioDownload' ) $LxsTrackingServices = @( @{ Name = 'DiagTrack'; Label = 'Connected User Experiences and Telemetry'; Default = 'Automatic' }, @{ Name = 'dmwappushservice'; Label = 'WAP Push Message Routing'; Default = 'Manual' } ) function Invoke-LxsTweakGroup { param( [Parameter(Mandatory = $true)][string]$Group, [Parameter(Mandatory = $true)][string]$Title ) $entries = @($LxsTweaks | Where-Object { $_.Group -eq $Group }) Clear-Host Show-LxsBoxTop -Title $Title Write-Host '' Write-Host 'The following settings will be changed:' foreach ($e in $entries) { Write-Host " - $($e.Label)" } if ($Group -eq 'telemetry') { Write-Host ' - Telemetry services disabled (DiagTrack, dmwappushservice)' Write-Host ' - Tracking scheduled tasks disabled' } Write-Host '' Write-Host "$($script:Gray)All of this is reversible from menu option 8.$($script:NC)" Write-Host '' if (-not (Confirm-LxsAction -Question 'Apply?')) { Write-LxsInfo 'Cancelled.' return } if (-not (Assert-LxsCheckpoint)) { return } Write-Host '' foreach ($e in $entries) { if (Set-LxsRegistryValue -Path $e.Path -Name $e.Name -Value $e.Value) { Write-LxsOk $e.Label } } if ($Group -ne 'telemetry') { return } Write-Host '' foreach ($svc in $LxsTrackingServices) { try { $s = Get-Service -Name $svc.Name -ErrorAction Stop if ($s.Status -eq 'Running') { Stop-Service -Name $svc.Name -Force -ErrorAction SilentlyContinue } Set-Service -Name $svc.Name -StartupType Disabled -ErrorAction Stop Write-LxsOk "$($svc.Label) service disabled" } catch { Write-LxsWarn "$($svc.Label): $($_.Exception.Message)" } } Write-Host '' $disabled = 0 foreach ($taskPath in $LxsTrackingTasks) { $leaf = Split-Path $taskPath -Leaf $parent = (Split-Path $taskPath -Parent) + '\' try { Disable-ScheduledTask -TaskName $leaf -TaskPath $parent -ErrorAction Stop | Out-Null $disabled++ } catch { # Absent on this edition/build — nothing to disable. } } Write-LxsOk "$disabled tracking scheduled task(s) disabled" } function Restore-LxsTweaks { Clear-Host Show-LxsBoxTop -Title 'REVERT PRIVACY TWEAKS' Write-Host '' Write-Host 'This restores every registry value, service and scheduled task that' Write-Host 'this script changes, back to the Windows default.' Write-Host '' Write-Host "$($script:Gray)Apps you removed are NOT restored — reinstall them from the Store.$($script:NC)" Write-Host '' if (-not (Confirm-LxsAction -Question 'Revert the privacy tweaks?')) { Write-LxsInfo 'Cancelled.' return } Write-Host '' foreach ($e in $LxsTweaks) { if ($null -eq $e.Default) { try { if (Test-Path $e.Path) { Remove-ItemProperty -Path $e.Path -Name $e.Name -ErrorAction Stop } Write-LxsOk "$($e.Label) — policy removed" } catch { # Already absent: that is the default state we wanted. } } else { if (Set-LxsRegistryValue -Path $e.Path -Name $e.Name -Value $e.Default) { Write-LxsOk "$($e.Label) — restored to default" } } } Write-Host '' foreach ($svc in $LxsTrackingServices) { try { Set-Service -Name $svc.Name -StartupType $svc.Default -ErrorAction Stop Write-LxsOk "$($svc.Label) service restored to $($svc.Default)" } catch { Write-LxsWarn "$($svc.Label): $($_.Exception.Message)" } } Write-Host '' $enabled = 0 foreach ($taskPath in $LxsTrackingTasks) { $leaf = Split-Path $taskPath -Leaf $parent = (Split-Path $taskPath -Parent) + '\' try { Enable-ScheduledTask -TaskName $leaf -TaskPath $parent -ErrorAction Stop | Out-Null $enabled++ } catch { # Not present on this build. } } Write-LxsOk "$enabled scheduled task(s) re-enabled" Write-Host '' Write-LxsWarn 'Sign out and back in for the per-user settings to take effect.' } function Show-LxsDebloatStatus { Clear-Host Show-LxsBoxTop -Title 'CURRENT STATE' Write-Host '' $installed = @(Get-LxsInstalledBloat) Write-Host " Known preinstalled apps still present : $($installed.Count) / $($LxsRemovableApps.Count)" $applied = 0 foreach ($e in $LxsTweaks) { try { $current = (Get-ItemProperty -Path $e.Path -Name $e.Name -ErrorAction Stop).($e.Name) if ($current -eq $e.Value) { $applied++ } } catch { # Value absent -> tweak not applied. } } Write-Host " Privacy tweaks applied : $applied / $($LxsTweaks.Count)" foreach ($svc in $LxsTrackingServices) { try { $s = Get-Service -Name $svc.Name -ErrorAction Stop Write-Host " $($svc.Name.PadRight(38)): $($s.Status) / $($s.StartType)" } catch { Write-Host " $($svc.Name.PadRight(38)): not present" } } Write-Host '' } function Show-LxsDebloatMenu { while ($true) { Clear-Host Show-LxsBoxTop -Title 'DEBLOAT & PRIVACY' -Right 'ADMIN' Write-Host '' Show-LxsMenuItem '1' 'Show current state' Show-LxsMenuItem '2' 'Remove preinstalled apps' 'you pick each one' Show-LxsMenuItem '3' 'Disable telemetry' 'policy, services, tasks' Show-LxsMenuItem '4' 'Disable Cortana & web search' Show-LxsMenuItem '5' 'Disable ads & suggestions' Show-LxsMenuItem '6' 'Disable Copilot & Recall' Show-LxsMenuItem '7' 'Create a restore point' 'before doing anything' Show-LxsMenuItem '8' 'Revert privacy tweaks' 'undo 3-6' Show-LxsMenuItem '0' 'Back' '' -Exit Write-Host '' Show-LxsBoxBottom Write-Host '' $choice = Read-LxsChoice Write-Host '' switch ($choice) { '1' { Show-LxsDebloatStatus; Read-LxsEnter } '2' { Remove-LxsSelectedApps; Read-LxsEnter } '3' { Invoke-LxsTweakGroup -Group 'telemetry' -Title 'DISABLE TELEMETRY'; Read-LxsEnter } '4' { Invoke-LxsTweakGroup -Group 'cortana' -Title 'DISABLE CORTANA & WEB SEARCH'; Read-LxsEnter } '5' { Invoke-LxsTweakGroup -Group 'ads' -Title 'DISABLE ADS & SUGGESTIONS'; Read-LxsEnter } '6' { Invoke-LxsTweakGroup -Group 'ai' -Title 'DISABLE COPILOT & RECALL'; Read-LxsEnter } '7' { if (New-LxsRestorePoint -Description 'LXS manual checkpoint') { $script:LxsCheckpointDone = $true } Read-LxsEnter } '8' { Restore-LxsTweaks; Read-LxsEnter } '0' { return } default { Write-LxsErr 'Invalid protocol. Select 0-8.'; Start-Sleep -Seconds 1 } } } } Show-LxsDebloatMenu exit 75