<# .SYNOPSIS Finds and removes the registry settings that lock people (or Windows) out of Windows Update. .DESCRIPTION Checks the handful of policy values that disable Windows Update access or automatic updates, plus the Windows Update and BITS services. Anything set to block is removed (returned to "Not configured"), and disabled services go back to Manual, which is how Windows ships them. Returns one object per check, so you can see what was wrong even when you only run it with -WhatIf. .PARAMETER IncludeCurrentUser Also check the per-user policy values in HKCU. Only useful when run as the signed-in user, not as SYSTEM. .PARAMETER KeepAutoUpdatePolicy Leave NoAutoUpdate alone. Some ConfigMgr and WSUS setups turn automatic updates off on purpose. .PARAMETER SkipServices Leave service startup types alone. .EXAMPLE .\Repair-WindowsUpdateAccess.ps1 -WhatIf .EXAMPLE .\Repair-WindowsUpdateAccess.ps1 -IncludeCurrentUser -Verbose #> [CmdletBinding(SupportsShouldProcess)] param( [switch]$IncludeCurrentUser, [switch]$KeepAutoUpdatePolicy, [switch]$SkipServices ) $wuPolicy = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' $checks = @( @{ Path = $wuPolicy; Name = 'DisableWindowsUpdateAccess'; Setting = 'Turn off access to all Windows Update features' } @{ Path = $wuPolicy; Name = 'SetDisableUXWUAccess'; Setting = 'Remove access to use all Windows Update features' } @{ Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer'; Name = 'NoWindowsUpdate'; Setting = 'Windows Update removed from Start/Settings (machine)' } @{ Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\WindowsUpdate'; Name = 'DisableWindowsUpdateAccess'; Setting = 'Windows Update access disabled (machine)' } ) if (-not $KeepAutoUpdatePolicy) { $checks += @{ Path = "$wuPolicy\AU"; Name = 'NoAutoUpdate'; Setting = 'Automatic Updates turned off' } } if ($IncludeCurrentUser) { $checks += @{ Path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer'; Name = 'NoWindowsUpdate'; Setting = 'Windows Update removed (user)' } $checks += @{ Path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\WindowsUpdate'; Name = 'DisableWindowsUpdateAccess'; Setting = 'Windows Update access disabled (user)' } } $changed = $false foreach ($check in $checks) { $value = (Get-ItemProperty -Path $check.Path -Name $check.Name -ErrorAction SilentlyContinue).($check.Name) $row = [pscustomobject]@{ Check = $check.Setting Location = "$($check.Path)\$($check.Name)" Value = $value Blocking = ($value -eq 1) Action = 'None' } if ($row.Blocking) { if ($PSCmdlet.ShouldProcess($row.Location, 'Remove blocking policy value')) { try { Remove-ItemProperty -Path $check.Path -Name $check.Name -ErrorAction Stop $row.Action = 'Removed' $changed = $true } catch { $row.Action = 'Failed' Write-Warning "Couldn't remove $($row.Location): $($_.Exception.Message)" } } else { $row.Action = 'WhatIf' } } $row } if (-not $SkipServices) { foreach ($name in 'wuauserv', 'BITS') { $service = Get-Service -Name $name -ErrorAction SilentlyContinue $row = [pscustomobject]@{ Check = "$name service startup" Location = "Service:$name" Value = if ($service) { "$($service.StartType)" } else { 'Missing' } Blocking = ($service -and "$($service.StartType)" -eq 'Disabled') Action = 'None' } if ($row.Blocking) { if ($PSCmdlet.ShouldProcess($name, 'Set startup type to Manual')) { try { Set-Service -Name $name -StartupType Manual -ErrorAction Stop $row.Action = 'SetToManual' $changed = $true } catch { $row.Action = 'Failed' Write-Warning "Couldn't change $name`: $($_.Exception.Message)" } } else { $row.Action = 'WhatIf' } } $row } } # Windows Update reads policy when it starts a scan, so give it a clean start. if ($changed) { $wu = Get-Service -Name wuauserv -ErrorAction SilentlyContinue if ($wu -and $wu.Status -eq 'Running' -and $PSCmdlet.ShouldProcess('wuauserv', 'Restart service')) { Restart-Service -Name wuauserv -Force -ErrorAction SilentlyContinue } Write-Verbose 'Changes made. If a GPO or MDM policy set these values, it will set them again at the next refresh.' }