Managing Windows Update with PowerShell

I mentioned some of the server management and config tools I’ve been creating with Server 2008 R2 core in mind, and I’m going to put the major bits here. I’ll post the whole script at the end of the set of posts but for now here is the code to look after Windows update

First I created two hash tables to map the numbers used in the functions to some meaningful text

 $AutoUpdateNotificationLevels= @{0="Not configured"; 1="Disabled" ; 2="Notify before download"; 
                                 3="Notify before installation"; 4="Scheduled installation"}

$AutoUpdateDays=@{0="Every Day"; 1="Every Sunday"; 2="Every Monday"; 3="Every Tuesday"; 4="Every Wednesday";
                  5="Every Thursday"; 6="Every Friday"; 7="EverySaturday"}

Next, there is a COM object for everything relating to auto-update. It has a settings property which contains the notification level and update days, the hour at which updates are fetched and how recommended updates are processed.  Setting the properties is pretty easy, and there is is a save method to commit them  - I’ve been lazy here and haven’t got a hash table mapping names to numbers for the notification level so or multiple switches so the user would need to know that notification levels in the hash table (or enter  $AutoUpdateNotificationLevels at the prompt to see what is in the table) – I might fix that for the final script.

  Function Set-WindowsUpdateConfig

{Param ($NotificationLevel , $Day, $hour, $IncludeRecommended)

 $AUSettings = (New-Object -com "Microsoft.Update.AutoUpdate").Settings

 if ($NotificationLevel)  {$AUSettings.NotificationLevel        =$NotificationLevel}

 if ($Day)                {$AUSettings.ScheduledInstallationDay =$Day}

 if ($hour)               {$AUSettings.ScheduledInstallationTime=$hour}

 if ($IncludeRecommended) {$AUSettings.IncludeRecommendedUpdates=$IncludeRecommended}

 $AUSettings.Save

} 

To show what the settings are, I decode them and return a custom object with the decoded properties.

 Function Get-WindowsUpdateConfig

{$AUSettings = (New-Object -com "Microsoft.Update.AutoUpdate").Settings

 $AUObj = New-Object -TypeName System.Object
  Add-Member -inputObject $AuObj -MemberType NoteProperty -Name "NotificationLevel"   `
    -Value $AutoUpdateNotificationLevels[$AUSettings.NotificationLevel]

 Add-Member -inputObject $AuObj -MemberType NoteProperty -Name "UpdateDays"      `
       -Value $AutoUpdateDays[$AUSettings.ScheduledInstallationDay]

 Add-Member -inputObject $AuObj -MemberType NoteProperty -Name "UpdateHour"        `
   -Value $AUSettings.ScheduledInstallationTime 
 Add-Member -inputObject $AuObj -MemberType NoteProperty -Name "Recommended updates" `
            -Value $(IF ($AUSettings.IncludeRecommendedUpdates) {"Included."}  else {"Excluded."})
 $AuObj
 } 

Checking on MSDN I found there is another object used in a script WUA_SearchDownloadInstall , which does what it says – it searches Windows update for updates, downloads them and installs them I added the logic to my function to over-ride the selection criteria, and auto-Restart if a restart is needed. Since it is sometimes useful to patch Virtual Machines, then shut them down, then Patch the host and then reboot it and bring the VMs back again , I’ve also put in a shutdown after Update switch.

The logic is simple enough, create a Session object which has CreateSearcher, CreateDownloader and CreateInstaller Methods. Then create a searcher and use it to get updates matching the default or specified criteria. If there are any updates, create a downloader object, hand it the list of updates found by the searcher and start a download. If the download completes successfully, filter out the successfully downloaded items, and pass those into a newly created installer object. Run the installation process and afterwards output a table showing the state of the updates. Finally reboot if needed.

 Function Add-WindowsUpdate

{param ($Criteria="IsInstalled=0 and Type='Software'" , [switch]$AutoRestart, [Switch]$ShutdownAfterUpdate) 

 $resultcode= @{0="Not Started"; 1="In Progress"; 2="Succeeded"; 3="Succeeded With Errors"; 4="Failed" ; 5="Aborted" }

 $updateSession = new-object -com "Microsoft.Update.Session"
  
 write-progress -Activity "Updating" -Status "Checking available updates"  
 $updates=$updateSession.CreateupdateSearcher().Search($criteria).Updates

 if ($Updates.Count -eq 0)  { "There are no applicable updates."}   
 else { 

       $downloader = $updateSession.CreateUpdateDownloader()   
       $downloader.Updates = $Updates  
         write-progress -Activity 'Updating' -Status "Downloading $($downloader.Updates.count) updates" 
       $Result= $downloader.Download()  
        if (($Result.Hresult -eq 0) –and (($result.resultCode –eq 2) -or ($result.resultCode –eq 3)) ) {

       $updatesToInstall = New-object -com "Microsoft.Update.UpdateColl"

       $Updates | where {$_.isdownloaded} | foreach-Object {$updatesToInstall.Add($_) | out-null }
       $installer = $updateSession.CreateUpdateInstaller()
       $installer.Updates = $updatesToInstall


       write-progress -Activity 'Updating' -Status "Installing $($Installer.Updates.count) updates'
        $installationResult = $installer.Install()
        $Global:counter=-1
        $installer.updates | Format-Table -autosize -property Title,EulaAccepted,@{label='Result';
                               expression={$ResultCode[$installationResult.GetUpdateResult($Global:Counter++).resultCode ] }} 
       if ($autoRestart -and $installationResult.rebootRequired) { shutdown.exe /t 0 /r }
       if ($ShutdownAfterUpdate) {shutdown.exe /t 0 /s }  
} 
}
}

So now I can run
Add-WindowsUpdate –Auto to download updates and reboot if needed,
Set-WindowsUpdateConfig –n 4 –i to schedule updates (Including the merely recommended)  and
Get-WindowsUpdateConfig

So next up it’s Get-RemoteDesktopConfig and Set-RemoteDesktopConfig.