or: How I stopped worrying and learned to live with Automatic Maintenance
As many of you may already know Windows Server 2012 does not play nice with WSUS/Windows Update. It will accept the download and notify or the download and schedule settings, but that's about where it ends. In other words, it will not honor the Windows Update schedule settings.
This is due to a new feature called Automatic Maintenance, which manages software updates in its own special kind of way and prevents us from forcing a reboot immediately after the updates have been installed.
If you are not yet sure how it works, this posts sums it up pretty well:
http://social.technet.microsoft.com/Forums/windowsserver/en-US/9714c384-ffed-4561-8349-32fd1dace9f9/server-2012-windows-update-group-policy
To work around this behavior and make sure that the updates get installed and the servers immediately rebooted I put together this PowerShell script. Together with the Windows Update policy to just download and notify it should at least ensure that the Windows Updates get installed during the proper server maintenance window.
I'm putting this script up here as-is and leave no guarantees that it's either perfect or will work for you. As always, watch for line breaks. If you find any bugs or have ideas on how to improve it, feel free to leave a comment.
[CmdletBinding()]
Param (
[switch]$Reboot
)
$UpdateSession =
New-Object -ComObject
'Microsoft.Update.Session'
$UpdateSearcher =
$UpdateSession.CreateUpdateSearcher()
$SearchResult =
$UpdateSearcher.Search("IsInstalled=0 and IsHidden=0")
$RebootRequired =
$False
$Install =
New-Object -ComObject
'Microsoft.Update.UpdateColl'
for($i=0;$i -lt $SearchResult.Updates.Count;$i++) {
$Update =
$SearchResult.Updates.Item($i)
Write-Verbose $Update.Title
if($Update.InstallationBehavior.CanRequestUserInput)
{
Write-Verbose
'CanRequestUserInput True'
Continue
}
if($Update.EulaAccepted
-eq $False)
{
Write-Verbose
'Accepting EULA'
$Update.AcceptEula()
}
if($Update.IsDownloaded)
{
Write-Verbose
'IsDownloaded'
$Install.Add($Update)
| Out-Null
if($Update.InstallationBehavior.RebootBehavior -gt
0) {
$RebootRequired
= $True
}
} else
{
Write-Verbose
'NotDownloaded'
}
}
if($Install.Count -eq 0) {
Write-Verbose
'No updates to install'
break
}
$UpdateInstaller = $UpdateSession.CreateUpdateInstaller()
$UpdateInstaller.Updates
= $Install
$Results = $UpdateInstaller.Install()
$Date = (Get-Date).ToUniversalTime()
$DateUTC = Get-Date($Date) -Format 'yyyy-MM-dd
HH:mm:ss'
$LastSuccessTime = $DateUTC
$LastError = $Results.HResult
Write-Verbose $Results
$RegPath = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto
Update\Results\Install'
if(Test-Path
-Path $RegPath)
{
$RegKey
= Get-ItemProperty
-Path $RegPath
if($RegKey -match
'LastError') {
Set-ItemProperty
-Path $RegPath
-Name 'LastError'
-Value $LastError
}
if($RegKey -match
'LastSuccessTime') {
Set-ItemProperty
-Path $RegPath
-Name 'LastSuccessTime'
-Value $LastSuccessTime
}
}
if($RebootRequired
-or $Reboot)
{
Write-Verbose
'Rebooting in 10 seconds'
Start-Sleep
-Seconds 10
Restart-Computer
-Force
}
<#
$Results.ResultCode:
Return Value Meaning
0 Not Started
1 In Progress
2 Succeeded
3 Succeeded With Errors
4 Failed
5 Aborted
#>