SCRIPT LIBRARY · POWERSHELL
WSUS, WUfB or Windows Update? Check (and Reset) Where a PC Gets Its Updates
One script that reads the WSUS, scan-source and Windows Update for Business settings on a device and tells you where it's really getting updates from, with a safe reset for leftovers.
- What it does
- Reports the WSUS server, per-class scan source, WUfB deferrals, Intune update policy count and default Windows Update service on a device, then makes a best guess at its update source. With -ResetPolicy it backs up and clears the local WindowsUpdate policy key.
- Requires
- Windows PowerShell 5.1 (PowerShell 7 works too)
- No modules
- Permissions
- Any user can run the report. -ResetPolicy needs local administrator (or SYSTEM).
- Runs on
- Windows 10/11, Windows Server 2016+
- Tested
- Parse-checked and dry-run with mocked registry, service and COM calls in PowerShell 7.4
Sooner or later you'll get a machine that won't patch, and the first question is always the same: where is this thing even looking for updates? A WSUS server that was decommissioned two years ago? Windows Update for Business? Both, depending on the update type? The Settings app won't tell you, and "Some settings are managed by your organization" isn't an answer.
The answer is scattered across a few registry keys, the Intune policy store and the Windows Update Agent's list of services. This script reads all of them and puts them in one object, with a plain-English guess at the effective source on top.
It started life as a near copy of the post that registers Microsoft Update. That one is about adding a service. This one is about figuring out which service a device is pointed at, and clearing out stale policy when a machine has been moved from one management tool to another and dragged the old settings along.
<#
.SYNOPSIS
Shows which update source a Windows device is actually pointed at, and can reset it.
.DESCRIPTION
Reads the Windows Update policy keys (WSUS server, scan source, WUfB deferrals, the
Intune/MDM policy store) plus the Windows Update Agent's default service, and makes a
best guess at where the device gets its updates: WSUS, Windows Update for Business,
a mix of the two, or plain unmanaged Windows Update.
With -ResetPolicy it backs up and deletes the local WindowsUpdate policy key, then
restarts the Windows Update service. Group Policy or ConfigMgr will put back whatever
they own at the next refresh, so this is for clearing out leftovers, not for fighting
an active policy.
.PARAMETER ResetPolicy
Back up and remove HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate. Honors -WhatIf.
.PARAMETER BackupPath
Folder for the .reg backup taken before a reset.
.EXAMPLE
.\Get-WindowsUpdateSource.ps1
.EXAMPLE
.\Get-WindowsUpdateSource.ps1 -ResetPolicy -WhatIf
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[switch]$ResetPolicy,
[ValidateNotNullOrEmpty()]
[string]$BackupPath = (Join-Path $env:ProgramData 'WindowsUpdatePolicyBackup')
)
$policyKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'
$mdmKey = 'HKLM:\SOFTWARE\Microsoft\PolicyManager\current\device\Update'
function Get-RegValues([string]$Path) {
$item = Get-ItemProperty -Path $Path -ErrorAction SilentlyContinue
if ($item) { $item } else { [pscustomobject]@{} }
}
function Get-UpdateSourceReport {
$wu = Get-RegValues $policyKey
$au = Get-RegValues "$policyKey\AU"
$mdm = Get-RegValues $mdmKey
# Windows 10 2004 and later: per-class scan source. 1 = WSUS, 0 = Windows Update.
$scanSource = [ordered]@{}
foreach ($class in 'Feature', 'Quality', 'Driver', 'Other') {
$value = $wu."SetPolicyDrivenUpdateSourceFor${class}Updates"
$scanSource[$class] = switch ($value) { 1 { 'WSUS' } 0 { 'WindowsUpdate' } default { 'NotSet' } }
}
$defaultService = $null
try {
$manager = New-Object -ComObject Microsoft.Update.ServiceManager
$defaultService = @($manager.Services) | Where-Object { $_.IsDefaultAUService } | Select-Object -First 1 -ExpandProperty Name
}
catch { Write-Verbose "Couldn't query the Windows Update Agent: $($_.Exception.Message)" }
$usesWsus = ($au.UseWUServer -eq 1) -and [bool]$wu.WUServer
$mdmSettings = @($mdm.PSObject.Properties | Where-Object { $_.Name -notlike 'PS*' -and $_.Name -notlike '*_ProviderSet' -and $_.Name -notlike '*_WinningProvider' })
$hasWufb = ($null -ne $wu.DeferQualityUpdatesPeriodInDays) -or ($null -ne $wu.DeferFeatureUpdatesPeriodInDays) -or ($null -ne $wu.TargetReleaseVersionInfo) -or ($mdmSettings.Count -gt 0)
$source = if ($usesWsus -and ($scanSource.Values -contains 'WindowsUpdate')) { 'Mixed (WSUS + Windows Update)' }
elseif ($usesWsus) { 'WSUS' }
elseif ($hasWufb) { 'Windows Update for Business' }
else { 'Windows Update (unmanaged)' }
$target = (@($wu.ProductVersion, $wu.TargetReleaseVersionInfo) | Where-Object { $_ }) -join ' '
[pscustomobject]@{
ComputerName = $env:COMPUTERNAME
LikelySource = $source
WUServer = $wu.WUServer
WUStatusServer = $wu.WUStatusServer
UseWUServer = $au.UseWUServer
BlockInternetWU = $wu.DoNotConnectToWindowsUpdateInternetLocations
ScanSourceFeature = $scanSource.Feature
ScanSourceQuality = $scanSource.Quality
ScanSourceDriver = $scanSource.Driver
ScanSourceOther = $scanSource.Other
DeferQualityDays = $wu.DeferQualityUpdatesPeriodInDays
DeferFeatureDays = $wu.DeferFeatureUpdatesPeriodInDays
TargetVersion = if ($target) { $target } else { $null }
MdmUpdateSettings = $mdmSettings.Count
NoAutoUpdate = $au.NoAutoUpdate
DefaultAUService = $defaultService
ConfigMgrClient = [bool](Get-Service -Name CcmExec -ErrorAction SilentlyContinue)
}
}
$report = Get-UpdateSourceReport
if (-not $ResetPolicy) { return $report }
if (-not (Get-Item -Path $policyKey -ErrorAction SilentlyContinue)) {
Write-Verbose 'No local WindowsUpdate policy key. Nothing to reset.'
return $report
}
if ($report.ConfigMgrClient) {
Write-Warning 'This device has a ConfigMgr client. Its software update point will rewrite the WSUS settings at the next policy cycle.'
}
if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Back up and remove $policyKey, then restart wuauserv")) {
try {
if (-not (Test-Path -Path $BackupPath)) { New-Item -Path $BackupPath -ItemType Directory -Force | Out-Null }
$backupFile = Join-Path $BackupPath ("WindowsUpdate-{0:yyyyMMdd-HHmmss}.reg" -f (Get-Date))
& reg.exe export 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' $backupFile /y | Out-Null
if ($LASTEXITCODE -ne 0) { throw "reg export failed with exit code $LASTEXITCODE. Not removing anything." }
Write-Verbose "Backed up policy to $backupFile"
Remove-Item -Path $policyKey -Recurse -Force -ErrorAction Stop
Restart-Service -Name wuauserv -Force -ErrorAction Stop
Write-Verbose 'Policy key removed and Windows Update service restarted.'
}
catch {
Write-Error "Reset failed: $($_.Exception.Message)"
exit 1
}
Get-UpdateSourceReport
}
else {
$report
}
Parameters
| Parameter | Type | Default | What it's for |
|---|---|---|---|
-ResetPolicy | switch | — | Back up HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate to a .reg file, delete it, and restart the Windows Update service. Works with -WhatIf. |
-BackupPath | string | $env:ProgramData\WindowsUpdatePolicyBackup | Where the .reg backup goes before a reset. Double-click the file to put everything back. |
Run it
Just tell me what this machine is doing.
.\Get-WindowsUpdateSource.ps1Check a handful of machines at once over PowerShell remoting.
Invoke-Command -ComputerName PC-0142, PC-0187 -FilePath .\Get-WindowsUpdateSource.ps1 | Select-Object ComputerName, LikelySource, WUServerPreview a reset on a device that was moved off WSUS.
.\Get-WindowsUpdateSource.ps1 -ResetPolicy -WhatIfDo the reset, and see the before and after in the verbose output.
.\Get-WindowsUpdateSource.ps1 -ResetPolicy -VerboseWhat you'll see
ComputerName : PC-0142
LikelySource : Mixed (WSUS + Windows Update)
WUServer : http://wsus01.contoso.com:8530
WUStatusServer : http://wsus01.contoso.com:8530
UseWUServer : 1
BlockInternetWU : 1
ScanSourceFeature : WindowsUpdate
ScanSourceQuality : WSUS
ScanSourceDriver : WSUS
ScanSourceOther : WSUS
DeferQualityDays :
DeferFeatureDays :
TargetVersion : Windows 11 24H2
MdmUpdateSettings : 0
NoAutoUpdate : 0
DefaultAUService : Windows Server Update Service
ConfigMgrClient : True
How it works
- Read the WSUS settings.
WUServerandWUStatusServerlive in the WindowsUpdate policy key, andUseWUServersits in theAUsubkey under it. You need both for WSUS to be in play. A server URL withUseWUServerset to 0 does nothing. - Read the scan source per class. On Windows 10 2004 and later, the
SetPolicyDrivenUpdateSourceFor*Updatesvalues decide whether feature, quality, driver and other updates come from WSUS (1) or Windows Update (0). This is the setting that replaced the old "dual scan" confusion. - Look for Windows Update for Business. Deferral days and a target version in the policy key, or any update settings in the Intune policy store, point toward WUfB.
- Ask the agent. The
Microsoft.Update.ServiceManagerCOM object reports which service Automatic Updates uses by default: Windows Update, Microsoft Update, or Windows Server Update Service. - Guess, then report. WSUS with some classes sent to Windows Update is "Mixed", WSUS alone is "WSUS", deferrals with no WSUS is "Windows Update for Business", and nothing at all is unmanaged Windows Update.
- Reset, if you asked. It exports the key with
reg.exe exportfirst and won't delete anything if the export fails. Then it removes the key and restartswuauservso the next scan starts clean.
Because the report is read-only, it's a nice fit for ConfigMgr's Run Scripts feature: approve it once, run it against a collection, and read the output right in the console. Save the -ResetPolicy version for a package you deploy on purpose.
Take it further
- Build an inventory. Run it across a collection, export to CSV, and sort by
WUServer. You'll find the machines still pointed at a server that doesn't exist anymore. - Watch for drift. Turn the
LikelySourcecheck into a configuration item that flags anything that isn't what you expect for that collection. - Check the scan itself. Pair it with the log collection script and read the decoded WindowsUpdate.log to confirm where the last scan actually went.
Things that'll trip you up
- Whatever owns the policy will put it back. A GPO rewrites these values at the next refresh, and a ConfigMgr client rewrites the WSUS settings on its next software update scan. Reset is for leftovers from a tool that's gone, not for arguing with one that's still there.
- Intune settings live somewhere else. MDM update policy sits under HKLM\SOFTWARE\Microsoft\PolicyManager, and the script only counts it. Don't hand-edit that key. Remove or change the assignment in the Intune admin center instead.
- Mixed is often on purpose. Since Windows 10 2004 you can pick the scan source per update class, so quality updates from WSUS and feature updates from Windows Update is a perfectly normal setup. It's only a problem when nobody meant to do it.
- Don't reset during a maintenance window. The reset restarts wuauserv. If an update is mid-install, you've just made your afternoon more interesting.
- LikelySource is a guess. A good one, but still a guess from policy values. When in doubt, read the raw fields, or run Get-WindowsUpdateLog and look for which service the scan actually hit.