>neoworkplace_

Search and install Windows security updates using Powershell and Windows Update Agent API

19 Sep 2023

It has become imperative for businesses to prioritize the security of their IT infrastructure. One of the foundational elements of this is ensuring that computers, and servers systems remain updated with the latest security patches. A significant proportion of cyberattacks exploit known vulnerabilities or flaws for which patches or fixes are already available.

For many acceptable reasons, specially in large company IT infrastructure, patch management solution like Microsoft WSUS, SCCM or Qualys for exemple, might not successfully patch all computers/servers in a requested short time (discovery issues, communication issues, client health, etc…). To fill this gap, IT Services might consider to create a parallele solution to patch faulty computers or servers. Windows Powershell can help to design your own patching process where the patch managment solution in place does not succeed.

On a Windows machine, computer or server, Windows OS give the ability to exploit the Windows Update Agent (WUA) through the Win32 WUA API. In PowerShell The WUA API can be accessed using Component Object Model interface (COM Objects) referered in Wuapi.dll.

Search Missing Windows Updates using ComObject

First of all, we need to create an Update Session, to do so let’s create a « Microsoft.Update.Session » COM Object inside a variable, and explore his members :

$objSession = New-Object -ComObject "Microsoft.Update.Session"
$objSession | Get-Member
Name                       MemberType Definition
----                       ---------- ----------
CreateUpdateDownloader     Method     IUpdateDownloader CreateUpdateDownloader ()
CreateUpdateInstaller      Method     IUpdateInstaller CreateUpdateInstaller ()
CreateUpdateSearcher       Method     IUpdateSearcher CreateUpdateSearcher ()
CreateUpdateServiceManager Method     IUpdateServiceManager2 CreateUpdateServiceManager ()
QueryHistory               Method     IUpdateHistoryEntryCollection QueryHistory (string criteria, int startIndex, int Count)
ClientApplicationID        Property   string ClientApplicationID () {get} {set}
ReadOnly                   Property   bool ReadOnly () {get}
UserLocale                 Property   uint UserLocale () {get} {set} 
WebProxy                   Property   IWebProxy WebProxy () {get} {set}

As we can see we get access to diferent usefull methods and properties. 

Create your Update Searcher

We will use our initial Com Object « Microsoft.Update.Session » defined in $objSession, and call the method CreateUpdateSearcher(). This method will create an instance of object of type IUpdateSearcher.

$objSearcher = $objSession.CreateUpdateSearcher()
$objSearcher | Get-Member
Name                                MemberType Definition
----                                ---------- ----------
BeginSearch                         Method     ISearchJob BeginSearch (string criteria, IUnknown onCompleted, Variant state)
EndSearch                           Method     ISearchResult EndSearch (ISearchJob searchJob)
EscapeString                        Method     string EscapeString (string unescaped)
GetTotalHistoryCount                Method     int GetTotalHistoryCount ()
QueryHistory                        Method     IUpdateHistoryEntryCollection QueryHistory (int startIndex, int Count)
Search                              Method     ISearchResult Search (string criteria)
CanAutomaticallyUpgradeService      Property   bool CanAutomaticallyUpgradeService () {get} {set}
ClientApplicationID                 Property   string ClientApplicationID () {get} {set}
IgnoreDownloadPriority              Property   bool IgnoreDownloadPriority () {get} {set}
IncludePotentiallySupersededUpdates Property   bool IncludePotentiallySupersededUpdates () {get} {set}
Online                              Property   bool Online () {get} {set}
SearchScope                         Property   SearchScope SearchScope () {get} {set}
ServerSelection                     Property   ServerSelection ServerSelection () {get} {set}
ServiceID                           Property   string ServiceID () {get} {set}

Set properties to configure the behavior of your searcher

Now that we have an instance of IUpdateSearcher we have to configure diferent properties to control behavior of the searcher. In our case we need to set up the following properties with Int32 values.

ServerSelection => To determine where the updates are sourced from

The ServerSelection enumeration defines values that describe the type of server to use for an update search operation.

Here are the possible values for ServerSelection:

  • 0 (ssDefault): Search the default server for updates.
  • 1 (ssManagedServer):  Search only the managed server for updates, such as a Windows Server Update Services (WSUS) server that’s set up in an enterprise environment to distribute updates.
  • 2 (ssWindowsUpdate): Search the Windows Update service over the Internet.
  • 3 (ssOthers): Search another server, to be specified by other means.

In our case we will set the ServerSelection to 3 (ssOthers) in order to exclude the default server type, managed server type, and Windows Update service, in order to use the Microsoft Update service (which is not defined by a specific value).

$objSearcher.ServerSelection = 3

ServiceID => Represents a unique identifier (GUID) for the update service

With this property we can define which update service we want to use for the search. An update service is defined by his GUID. Basically the commonly used update services are :

  • Windows Update: Windows Update service provide only Windows system patchs. The service will pull updates directly from internet. His GUID is consistent from Microsoft across all systems : 9482f4b4-e343-43b6-b170-9a65bc822c77
  • Microsoft Update: Microsoft Update cover a larger scope whithin Windows system and Microsoft products such as Microsoft Office, Microsoft SQL server, etc… The service trigger the pull from Internet. His GUID is consistent from Microsoft across all systems : 7971f918-a847-4430-9279-4a52d1efe18d
  • Windows Server Update Services (WSUS): If an enterprise has set up its own WSUS server, then the ServiceID would correspond to that WSUS instance. Each WSUS instance would typically have its own unique GUID. 

In our case we will use the Microsoft Update service which provide us the most large scope of updates (Windows system, Office, etc…). 

$objSearcher.ServiceID = '7971f918-a847-4430-9279-4a52d1efe18d'

Start searching missing updates

Now that our searcher is configured, we can start the search of update whith criterias.

As we saw above, the searcher object have a method called « Search ». This Search method accept criteria as filter to define which update you are looking for. Here is an exemple of search criteria to use for searching security updates that are not installed on the system : 

$criteria = "IsInstalled=0 and Type='Software' and CategoryIDs contains '0fa1201d-4330-4fa8-8ae9-b877473b6441'"

The syntax used is similar to SQL, where you can define condition and logical operations. In this example we define not yet installed updates with "IsInstalled=0" of type software "Type='Software'" as opposed to driver updates, which would use Type='Driver'. We filter the type of update with category ID '0fa1201d-4330-4fa8-8ae9-b877473b6441' which correspond to Secrurity category update. You can find more details about types and categories classification with GUID on Microsoft online documentation.

Let’s start the search whitin a new variables that we will call $missingUpdates : 

$missingUpdates = $objSearcher.Search($criteria)

After the search made, if updates are found, you will get a collection of updates returned by the service provider (Microsoft Udpdate in our case).

For example, here is an overview of an update object returned :

Title                           : Intel Corporation - Sensor - 5/10/2016 12:00:00 AM - 3.0.40.3258
AutoSelectOnWebSites            : False
BundledUpdates                  : System.__ComObject
CanRequireSource                : False
Categories                      : System.__ComObject
Deadline                        : 
DeltaCompressedContentAvailable : False
DeltaCompressedContentPreferred : True
Description                     : Intel Corporation Sensor  driver update released in  May 2016
EulaAccepted                    : True
EulaText                        : 
HandlerID                       : http://schemas.microsoft.com/msus/2002/12/UpdateHandlers/WindowsDriver
Identity                        : System.__ComObject
Image                           : 
InstallationBehavior            : System.__ComObject
IsBeta                          : False
IsDownloaded                    : False
IsHidden                        : False
IsInstalled                     : False
IsMandatory                     : False
IsUninstallable                 : False [...]

Install missing updates

Now that we get a collection of missing updates in our system, let’s see how we can install them to patch our computer. To do so, we need to introduce two new type of COM objects which are UpdateCollection and IUpdateInstaller. The UpdateCollection object represents a collection of IUpdate objects, essentially acting as a container to hold updates that you might want to download or install programmatically. The IUpdateInstaller will give the ability to proceed to the installation of a specified updates collection set.

Let’s create an Update Collection inside a new variable and add on it our missing updates : 

$updateCollection = New-Object -ComObject "Microsoft.Update.UpdateColl"
$missingUpdates.Updates | ForEach-Object { $updateCollection.Add($_) }

Our container is now ready…let’s create the installer using the initial session object, and inject it our update collection

$updateInstaller = $objSession.CreateUpdateInstaller()
$updateInstaller.Updates = $updateCollection

We can now start the installation :

$resultInstall = $updateInstaller.Install()

#Control the result of the installation
if ($installResult.ResultCode -eq 2) {
    Write-Output "Updates installed successfully."
} else {
    Write-Output "There was an issue installing the updates."
}

We’ve learned how to use Powershell and the Windows Update API to search an install missing windows updates.

I’ve made a Powershell module easy to use to help you to exploit the Windows Update Agent API, you can download it on github on following project : Powershell WUA

Steeve Dadon

.net automation consultant

I there ! I'm a french IT consultant with a deep-seated passion for .NET Automation and Web technologies. Having dedicated almost 15 years to the industry, I've honed my expertise in specific areas like Powershell, Restfull API,  ASP.NET, C#, MVC, etc...and even WordPress and GPT AI.

Throughout my career, I've had the privilege to collaborate with various talented teams, which has further deepened my understanding of the ever-evolving tech landscape. 

I've created this blog to share my knowledge, which I believe can be beneficial and a valuable resource for various IT needs. Feel free to contact me if needed...