Update to script that runs Windows Update

Articles in the "Programmatically Run Windows Update" series

  1. Programmatically run Windows Update (as part of a broader patch and reboot process)
  2. Update to script that runs Windows Update [This article]

As part of the patch and reboot script I use in my Exchange environment, there is a separate script that runs Windows Update, first covered here.  I have updated the script to allow skipping the installation of any patches by specifying the KB numbers in a companion file.

Starting on the second line of the file (so that the first line can contain instructions), list one KB number per line.  To use this list, there is a new function in the script:

function Get-UpdatesToSkip
	$scriptDirectory = Split-Path $script:MyInvocation.MyCommand.Path
	$KB = @()
	if (Test-Path "$scriptDirectory\updatestoskip.txt")
		$sourceFile = Get-Content "$scriptDirectory\updatestoskip.txt"
		if ($sourceFile.Length -gt 1)
			for ($i = 1;$i -le $sourceFile.Length - 1;$i++)
				$KB += $sourceFile[$i]

The function gets the current path of the script, checks for the existence of the file, and reads each KB into an array. If the file is not present, or is present but no KB numbers are listed, the array will just be empty. This allows the script to still run successfully when you don’t have the file or specify any patches to skip.

When searching for available updates via the Microsoft.Update.Session interface, you can’t filter on a KB number, but you can filter on an Update ID (UID). Unfortunately, to cross reference KB number and UID, you have to first perform a search anyway. So rather than searching twice, I just work with the collection created from the search. The object that contains the list of available hotfixes cannot be manipulated the same way as a standard collection object since it is a COM object, so you can’t use a method like Remove() to remove an item from the collection.

Therefore, in order to have a collection of updates that don’t include certain KBs, you can build a new collection, skipping items in the first collection that match a filter. To do this, I create another Microsoft.Update.UpdateColl object that will be used to specify which updates to download (so that you don’t download all of the updates and then skip certain ones):

$updatesToDownload = New-Object -ComObject 'Microsoft.Update.UpdateColl'

To add to this collection, each item in the first collection is compared to the array that contains the list of KBs:

$updatesToSkip = Get-UpdatesToSkip
$updates | ForEach-Object {
	if ($updatesToSkip -match $_.KBArticleIDs)
		WriteEvent "Skipping download of $($_.KBArticleIDs) since it is listed in the file of updates to skip." 'Information' '1001'
		$updatesToDownload.Add($_) | Out-Null

(The WriteEvent function is defined in the script and is used to write a custom event to the Application log so that you will know that an update was intentionally skipped.) The rest of the script is basically unchanged; just some minor tweaks to accommodate the new code. The download has been updated and includes the companion text file. (If you are wondering why a companion file is used, it is because, as part of the Exchange patch and reboot script, the Windows Update script is remotely called via a scheduled task, and there isn’t a way to dynamically specify command line parameters for a scheduled task (without modifying the task itself)).

  Run-WindowsUpdate.zip (1.8 KiB)

2 thoughts on “Update to script that runs Windows Update

  1. Hey Scott,

    Great code! As I’m not savvy in PS at all this saves me so much effort, I can’t express my gratitude enough for your work. I’ve got things set to use your script but I’m running into this feedback on exec:

    Exception calling “Search” with “1” argument(s): “Exception from HRESULT: 0x8024400A”
    At *****Update.ps1:44 char:56
    + $updates = $upddateSession.CreateupdateSearcher().Search <<< ($criteria).Updates
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ComMethodTargetInvocation

    Since I'm not familiar with PS I'm having trouble debugging the issue here. Is the problem with an outdated version of PS or something else I overlooked?

    Thanks much and keep it up!

  2. The error code indicates it is an issue within Windows Update, not PowerShell. I would verify the version of Windows Update installed on the server.

    Edit: The commenter confirmed that the issue was resolved after updating to the latest version of the Windows Update agent.

Leave a Reply

Your email address will not be published. Required fields are marked *