Dealing with ‘multiple-reboot’ patches during OSD

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

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s