If you are incorporating some patching process during your OS Deployments, you’ve undoubtedly come across the issue where some patches released by Microsoft cause multiple reboots. These additional reboots are unhandled by the task sequence, which causes it to quit with little explanation. This is documented in the following MS KB article, where MS also maintains the list of patches that are known to cause this: http://support.microsoft.com/kb/2894518.
Depending on how you do your patching, there are different ways to handle this. One way to do it is to update your reference images to include the patches. But it may not be practical to do this every time a new ‘multiple-reboot’ patch is discovered. And let’s face it, it cannot be done quickly.
If you are using the Software Updates feature of ConfigMgr, then you can remove those patches from both the Software Update groups and the Deployment Packages, and make sure your ADRs don’t try to pull in those patches again.
If, like me, you use WSUS to patch your builds, which is nicely documented by Chris Nackers here, then you can use the WUMU_ExcludeKB variable either in your customsettings.ini file, or in the task sequence itself, to block the patches from being installed. However, I have seen some reports that this is not 100% reliable.
My preferred method to do this is simply to ‘decline’ the updates on all our WSUS servers (which are standalone). This prevents them from being installed by WSUS as only ‘approved’ updates can be installed. Obviously to decline all those patches manually would take some time if you have a few WSUS servers as we do, but thankfully Powershell can help us.
Here are a couple of scripts that I use to do this. The first will search each WSUS server for any of those ‘multiple-reboot’ patches by using the KB number. Then it will report on the approval status of each patch so you can identify if it needs to be declined or not.
The next script will then go ahead and decline all those updates on each WSUS server in succession. You could run the first script again afterwards to verify that the updates were declined.
Search for ‘multiple-reboot’ patches in WSUS
Logging is done in brief to the console, and in more detail to a text file which shows you each patch and it’s status on each server.
<# This script will search for all the updates in the $Updates variable on each WSUS server in the $WSUSservers variable, and report their approval status. It logs to the console and more detailed logging to a text file. #> # This update list is the one from http://support.microsoft.com/kb/2894518 which lists software updates that cause multiple reboots and kills the WSUS step in the OSD Task Sequence $Updates = ` "2984976", ` "2981685", ` "2966034", ` "2965788", ` "2920189", ` "2871777", ` "2871690", ` "2862330", ` "2771431", ` "2821895", ` "2545698", ` "2529073" $WsusServers = ` "wsusserver01", ` "wsusserver02", ` "wsusserver03", ` "wsusserver04", ` "wsusserver05" $Log = "$env:USERPROFILE\SearchedUpdates.txt" [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null $approveState = 'Microsoft.UpdateServices.Administration.ApprovedStates' -as [type] foreach ($WsusServer in $WsusServers) { $finalCount = $null write-host "Searching for updates on $WsusServer" -ForegroundColor Green write-output "############################################" | Out-File - FilePath $Log -Append write-output "## Searching for updates on $WsusServer ##" | Out-File -FilePath $Log -Append write-output "############################################" | Out-File -FilePath $Log -Append $new = $null $new = @() foreach ($update in $updates) { $Count = $null write-host " Searching for kb$update" | Out-File -FilePath $Log -Append $wsus = Get-WSUSServer -Name $WsusServer -PortNumber 8530 $updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope -Property @{ TextIncludes = "$update" ApprovedStates = $approveState::Any } $UpdateList = $wsus.GetUpdates($updateScope) If ($UpdateList -ne $null) { $Count = $UpdateList.Count $finalcount += $Count write-host " Update found" -ForegroundColor DarkGray write-output "KB$Update found. There are $count updates with this KB." | Out-File -FilePath $Log -Append $new += $wsus.GetUpdates($updateScope) | Select Title,IsLatestRevision,IsSuperseded,CreationDate,IsApproved,IsDeclined } Else { write-host " Update not found" -ForegroundColor Red write-output "KB$Update NOT found" | Out-File -FilePath $Log -Append } } Write-Output "Found $finalcount updates in total" | Out-File -FilePath $Log -Append Write-Output " " | Out-File -FilePath $Log -Append $new | ft -AutoSize | Out-String -Width 4096 | Out-File -FilePath $Log -Append Write-Output " " | Out-File -FilePath $Log -Append } Invoke-Item $Log
You can also output to html if you prefer:
<# This script will search for all the updates in the $Updates variable on each WSUS server in the $WSUSservers variable, and report their approval status. It logs to the console and more detailed logging in an html page. #> # This update list is the one from http://support.microsoft.com/kb/2894518 which lists software updates that cause multiple reboots and kills the WSUS step in the OSD Task Sequence $Updates = ` "2984976", ` "2981685", ` "2966034", ` "2965788", ` "2920189", ` "2871777", ` "2871690", ` "2862330", ` "2771431", ` "2821895", ` "2545698", ` "2529073" $WsusServers = ` "wsusserver01", ` "wsusserver02", ` "wsusserver03", ` "wsusserver04", ` "wsusserver05" $Log = "$env:USERPROFILE\SearchedUpdates.html" $html = "" [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null $approveState = 'Microsoft.UpdateServices.Administration.ApprovedStates' -as [type] foreach ($WsusServer in $WsusServers) { $finalCount = $null write-host "Searching for updates on $WsusServer" -ForegroundColor Green $html += write-output "#####################################" $html += "<br>" $html += write-output "## Searching for updates on $WsusServer ##" $html += "<br>" $html += write-output "#####################################" $html += "<br><br>" $new = $null $new = @() foreach ($update in $updates) { $Count = $null write-host " Searching for kb$update" | Out-File -FilePath $Log -Append $wsus = Get-WSUSServer -Name $WsusServer -PortNumber 8530 $updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope -Property @{ TextIncludes = "$update" ApprovedStates = $approveState::Any } $UpdateList = $wsus.GetUpdates($updateScope) If ($UpdateList -ne $null) { $Count = $UpdateList.Count $finalcount += $Count write-host " Update found" -ForegroundColor DarkGray $html += write-output "KB$Update found. There are $count updates with this KB." $html += "<br>" $new += $wsus.GetUpdates($updateScope) | Select Title,IsLatestRevision,IsSuperseded,CreationDate,IsApproved,IsDeclined } Else { write-host " Update not found" -ForegroundColor Red $html += write-output "KB$Update NOT found" | ConvertTo-Html | Out-File -FilePath $Log -Append $html += "<br>" } } $html += "<br>" $html += write-output "Found $finalcount updates in total" $html += "<br>" $new = $new | ConvertTo-Html $html += $new $html += "<br><br>" } $html | Out-File $log invoke-item $log
Declining ‘multiple-reboot’ patches in WSUS
Again, logging is done in brief to the console, and in more detail to a log file which shows you each patch that was declined.
<# This script will decline all updates with the KB number listed in the $Updates variable on each WSUS server in the $WSUSservers variable. It logs to the console and more detailed logging to a log file. #> # This update list is the one from http://support.microsoft.com/kb/2894518 which lists software updates that cause multiple reboots and kills the WSUS step in the OSD Task Sequence $Updates = ` "2984976", ` "2981685", ` "2966034", ` "2965788", ` "2920189", ` "2871777", ` "2871690", ` "2862330", ` "2771431", ` "2821895", ` "2545698", ` "2529073" $WsusServers = ` "wsusserver01", ` "wsusserver02", ` "wsusserver03", ` "wsusserver04", ` "wsusserver05" $Log = "$env:USERPROFILE\DeclinedUpdates.log" [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null $approveState = 'Microsoft.UpdateServices.Administration.ApprovedStates' -as [type] foreach ($WsusServer in $WsusServers) { $finalCount = $null $wsus = Get-WSUSServer -Name $WsusServer -PortNumber 8530 write-host "Declining Unwanted Updates on $WsusServer" -ForegroundColor Green write-output "#############################################" | Out-File $Log -Append write-output "## DECLINING UNWANTED UPDATES ON $WsusServer ##" | Out-File $Log -Append write-output "#############################################" | Out-File $Log -Append foreach ($Update in $Updates) { $updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope -Property @{ TextIncludes = "$update" ApprovedStates = $approveState::Any } $UpdateList = $wsus.GetUpdates($updateScope) $Count = $UpdateList.Count write-output "Declining $Count updates for KB$Update" | Out-File $Log -Append write-host " KB$Update" -NoNewline $UpdateList | ForEach { Write-Output (" Declining {0}" -f $_.Title) -Verbose | Out-File $Log -Append Write-host "." -NoNewline $_.Decline() } write-host " " $finalCount += $Count } write-output ">>Declined a total of $finalCount updates for $WsusServer<<" | Out-File $Log -Append write-output " " | Out-File $Log -Append } Invoke-Item $Log