~/2025/07/16/powershell-windows-11-debloat-a-golden-image-before-sysprep.md
PowerShell: Windows 11 – Debloat a Golden Image Before Sysprep
--- author: Tom Lasswell date: read: 4 min in: [ps, scripts] tags: [powershell, windows-11, debloat] ---
$ grep -n '^#' post.md
Every Windows 11 reference image I build starts the same way: install, patch, then spend twenty minutes ripping out Xbox overlays, Solitaire, the news and weather apps, and the OneDrive setup that otherwise greets every new profile created from the image. Doing that by hand in the Store or per user doesn't stick once you sysprep and capture, because provisioned packages and the Default user profile are exactly what get baked into every new user session. This script removes matching AppX packages for every account on the reference machine and from the provisioned list, sets the consumer-experience and Widgets policies, and (unless you tell it to keep it) removes the OneDrive setup trigger from the Default user hive.
The order matters. Microsoft's support article on Sysprep failures describes exactly what happens if you only deprovision: when a package is removed from provisioning but is still installed for the account doing the image prep, Sysprep /generalize fails with SYSPRP Package <PackageFullName> was installed for a user, but not provisioned for all users followed by 0x80073cf2. The fix Microsoft documents is to remove the package for the user with Remove-AppxPackage and remove the provisioning with Remove-AppxProvisionedPackage, which is what this script does, in that order.
Requirements
- Windows PowerShell 5.1, run elevated. The
AppxandDismmodules ship in the box. - Run on the reference machine itself, in audit mode, before
Sysprep /generalize. - Local administrator rights.
Remove-AppxPackage -AllUsersrequires an elevated session. - Disconnect the reference machine from the internet, or disable Store automatic updates, while you build it. Microsoft notes that a Store app updated during image prep also breaks Sysprep with the same
0x80073cf2error. - The
DisableWindowsConsumerFeaturesandDisableSoftLandingpolicies are only honored on Enterprise and Education editions (per the Experience Policy CSP reference). They are harmless but ignored on Pro.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
-AppNamePattern | string | No | Wildcard patterns matched against package names (Name for installed packages, DisplayName for provisioned ones). Defaults to a built-in list of consumer apps. |
-KeepOneDrive | switch | No | Skip the Default user hive change, so new profiles still run OneDrive setup at first sign-in. |
-WhatIf | switch | No | Built-in ShouldProcess support: preview every removal and registry change without applying it. |
Usage
Preview what the script would remove before touching the reference image:
.\Remove-GoldenImageBloat.ps1 -WhatIf
Found 14 installed and 14 provisioned package(s) matching the pattern list.
What if: Performing the operation "Remove-AppxPackage -AllUsers" on target "Microsoft.BingNews_4.55.62231.0_x64__8wekyb3d8bbwe".
What if: Performing the operation "Remove-AppxProvisionedPackage" on target "Microsoft.BingNews".
...
What if: Performing the operation "Set consumer experience policies" on target "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent".
What if: Performing the operation "Disable Widgets (AllowNewsAndInterests = 0)" on target "HKLM:\SOFTWARE\Policies\Microsoft\Dsh".
What if: Performing the operation "Remove OneDriveSetup Run value from the Default user hive" on target "C:\Users\Default\NTUSER.DAT".
Run it for real, keeping OneDrive because this particular image ships to users who rely on it:
.\Remove-GoldenImageBloat.ps1 -KeepOneDrive
Before generalizing, confirm nothing from the list is still installed for any account. This is the check Microsoft's Sysprep article uses; any package that shows Installed for a user will break the generalize pass:
Get-AppxPackage -AllUsers | Where-Object { $_.PublisherId -eq "8wekyb3d8bbwe" } | Format-List -Property PackageFullName, PackageUserInformation
Then generalize and shut down for capture:
& "$env:SystemRoot\System32\Sysprep\Sysprep.exe" /generalize /oobe /shutdown
Add /mode:vm only if the captured VHD will be deployed back onto the same hypervisor with the same hardware profile. The Sysprep documentation is explicit that VM mode must be run inside a VM and that deploying to a different hardware profile can cause unexpected issues.
Script
<#
.SYNOPSIS
Removes consumer AppX packages from a Windows 11 reference machine for all users and
from provisioning, and sets consumer-experience policies before Sysprep.
.DESCRIPTION
For each name pattern, removes matching installed AppX packages for every user
account (Remove-AppxPackage -AllUsers), then removes the matching provisioned
packages (Remove-AppxProvisionedPackage -Online) so they never install for new
profiles. Removing both avoids the documented Sysprep failure 0x80073cf2 ("installed
for a user, but not provisioned for all users"). Sets the CloudContent consumer
experience policies and the Widgets policy, and unless -KeepOneDrive is supplied,
removes the OneDriveSetup Run value from the Default user hive. Run in audit mode,
before Sysprep /generalize.
.PARAMETER AppNamePattern
Wildcard patterns matched against package names. Defaults to a list of common
consumer apps.
.PARAMETER KeepOneDrive
Skip the Default user hive change so new profiles still run OneDrive setup.
.EXAMPLE
.\Remove-GoldenImageBloat.ps1 -WhatIf
.EXAMPLE
.\Remove-GoldenImageBloat.ps1 -KeepOneDrive
.NOTES
Author : Thomas Lasswell (https://www.techcolumnist.com)
Version : 1.0 (2025-07-16)
Requires: Windows PowerShell 5.1, elevated; Appx and Dism modules (in box)
.LINK
https://learn.microsoft.com/en-us/troubleshoot/windows-client/setup-upgrade-and-drivers/sysprep-fails-remove-or-update-store-apps
#>
#Requires -RunAsAdministrator
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter()]
[string[]]$AppNamePattern = @(
"Clipchamp.Clipchamp",
"Microsoft.BingNews",
"Microsoft.BingWeather",
"Microsoft.GamingApp",
"Microsoft.GetHelp",
"Microsoft.Getstarted",
"Microsoft.MicrosoftSolitaireCollection",
"Microsoft.PowerAutomateDesktop",
"Microsoft.Todos",
"Microsoft.WindowsFeedbackHub",
"Microsoft.Xbox.TCUI",
"Microsoft.XboxGamingOverlay",
"Microsoft.XboxSpeechToTextOverlay",
"Microsoft.YourPhone",
"Microsoft.ZuneMusic",
"Microsoft.ZuneVideo"
),
[Parameter()]
[switch]$KeepOneDrive
)
# Return $true when a package name matches any pattern in the list.
function Test-NameMatch {
param (
[string]$Name,
[string[]]$Pattern
)
foreach ($p in $Pattern) {
if ($Name -like $p) {
return $true
}
}
return $false
}
$installed = @(Get-AppxPackage -AllUsers | Where-Object { Test-NameMatch -Name $_.Name -Pattern $AppNamePattern })
$provisioned = @(Get-AppxProvisionedPackage -Online | Where-Object { Test-NameMatch -Name $_.DisplayName -Pattern $AppNamePattern })
Write-Host "Found $($installed.Count) installed and $($provisioned.Count) provisioned package(s) matching the pattern list."
# Remove the installed copies for every account first, so Sysprep does not find a
# per-user install of a package that is no longer provisioned.
foreach ($package in $installed) {
if ($PSCmdlet.ShouldProcess($package.PackageFullName, "Remove-AppxPackage -AllUsers")) {
try {
Remove-AppxPackage -Package $package.PackageFullName -AllUsers -ErrorAction Stop
Write-Host "Removed installed package: $($package.PackageFullName)"
} catch {
Write-Warning "Could not remove $($package.PackageFullName): $($_.Exception.Message)"
}
}
}
# Then remove the provisioned copy so new profiles never receive it.
foreach ($package in $provisioned) {
if ($PSCmdlet.ShouldProcess($package.DisplayName, "Remove-AppxProvisionedPackage")) {
try {
Remove-AppxProvisionedPackage -Online -PackageName $package.PackageName -AllUsers -ErrorAction Stop | Out-Null
Write-Host "Removed provisioned package: $($package.DisplayName)"
} catch {
Write-Warning "Could not deprovision $($package.DisplayName): $($_.Exception.Message)"
}
}
}
# Consumer experiences and Windows tips (honored on Enterprise and Education only).
$cloudContentPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent"
if ($PSCmdlet.ShouldProcess($cloudContentPath, "Set consumer experience policies")) {
New-Item -Path $cloudContentPath -Force | Out-Null
New-ItemProperty -Path $cloudContentPath -Name "DisableWindowsConsumerFeatures" -Value 1 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path $cloudContentPath -Name "DisableSoftLanding" -Value 1 -PropertyType DWord -Force | Out-Null
}
# Widgets ("Allow widgets" policy, NewsAndInterests.admx).
$dshPath = "HKLM:\SOFTWARE\Policies\Microsoft\Dsh"
if ($PSCmdlet.ShouldProcess($dshPath, "Disable Widgets (AllowNewsAndInterests = 0)")) {
New-Item -Path $dshPath -Force | Out-Null
New-ItemProperty -Path $dshPath -Name "AllowNewsAndInterests" -Value 0 -PropertyType DWord -Force | Out-Null
}
if (-not $KeepOneDrive) {
# New profiles run OneDrive setup from a Run value in the Default user hive.
$defaultHive = "$env:SystemDrive\Users\Default\NTUSER.DAT"
$mountKey = "HKU\DefaultUserTemp"
$runKey = "Registry::HKEY_USERS\DefaultUserTemp\Software\Microsoft\Windows\CurrentVersion\Run"
if ((Test-Path -Path $defaultHive) -and $PSCmdlet.ShouldProcess($defaultHive, "Remove OneDriveSetup Run value from the Default user hive")) {
& reg.exe load $mountKey $defaultHive | Out-Null
try {
if (Get-ItemProperty -Path $runKey -Name "OneDriveSetup" -ErrorAction SilentlyContinue) {
Remove-ItemProperty -Path $runKey -Name "OneDriveSetup"
Write-Host "Removed OneDriveSetup from the Default user Run key."
} else {
Write-Host "No OneDriveSetup value in the Default user Run key."
}
} finally {
# Release any handles PowerShell holds on the hive before unloading it.
[gc]::Collect()
[gc]::WaitForPendingFinalizers()
Start-Sleep -Seconds 1
& reg.exe unload $mountKey | Out-Null
}
}
}
Write-Host "Done. Verify with Get-AppxPackage -AllUsers, then run Sysprep /generalize."
Notes
- Snapshot the reference VM before running this. The patterns are wildcards, and a pattern that's too broad will remove more than you intended. Run
Get-AppxProvisionedPackage -Online | Format-Table DisplayName, PackageName(the command Microsoft's app overview uses) first to see exactly what your build provisions. - If Sysprep still fails, open
setupact.logandsetuperr.login%WINDIR%\System32\Sysprep\Pantherand search forSYSPRP Package. Copy them somewhere else before retrying: Microsoft's "fatal error occurred while trying to sysprep" article notes that each Sysprep run clears thatsetupact.log. The line names the exactPackageFullNamethat's installed for a user but no longer provisioned. Remove it withRemove-AppxPackage -Package <PackageFullName>for that user (or delete the extra test account) and rerun. - Microsoft's article also calls out the reverse trap: letting the Store update an inbox app during image prep produces the same
0x80073cf2failure. Keep the reference VM offline between patching and capture. reg.exe unloadfails if anything still holds a handle on the mounted hive. The script forces garbage collection first; if it still fails interactively, close any Registry Editor windows and rerunreg.exe unload HKU\DefaultUserTemp.- Removing the
OneDriveSetupRun value is a community-documented technique (it appears in the archived Microsoft TechNet forums rather than in product documentation), so verify first sign-in behavior on your build after capture. It only stops the per-user setup from launching; it doesn't block OneDrive by policy. - On Windows 11 24H2 and later Enterprise or Education, the Remove default Microsoft Store packages from the system policy does this removal natively at OOBE and first sign-in, and blocks reinstallation while the app stays selected. If you can use it, it's more durable than baking removals into an image. I cover it in the deployment image companion post.
- A feature update can reintroduce inbox apps on the reference image. Rerun this after any feature update and recapture.
Source
- Sysprep fails with Microsoft Store apps (KB 2769827)
- Remove-AppxProvisionedPackage
- Remove-AppxPackage
- Sysprep command-line options
- A fatal error occurred while trying to sysprep the machine
- Experience Policy CSP (AllowWindowsConsumerFeatures, AllowWindowsTips)
- NewsAndInterests Policy CSP (AllowNewsAndInterests)
- Uninstall OneDrive during a Windows 10 OSD task sequence (archived TechNet thread)