Use PowerShell to Audit and Install Windows Patches

Use PowerShell to Audit and Install Windows Patches

  • Comments 42
  • Likes

Summary: Learn how to use a free Windows PowerShell module to audit and install patches on Windows systems.


Microsoft Scripting Guy Ed Wilson here. Today, I am proud to present a guest blog post written by Boe Prox. Boe has written a really cool module to audit and install software patches on Windows systems.

Boe is currently a senior systems administrator with BAE Systems. He has been in the IT industry since 2003 and has spent the past three years working with VBScript and Windows PowerShell. He looks to script whatever he can, whenever he can. He is also a moderator on the Hey, Scripting Guy! Forum. You can check out his blog and his projects: PoshWSUS and PoshPAIG.


<BoeProxDisclaimer>This post does not contain any code in it. It does, however, show examples of what I have been able to put together using Windows PowerShell to create an application to manage the patching of servers in an enterprise environment. Tomorrow’s post will contain more code in it as I discuss some issues I ran into and how I resolved them.</BoeProxDisclamer>

Something that I have been working on for a little while as a work project is a way for other users in my shop to be able to patch our servers during a downtime without actually having to log into each server remotely to install the patches that have been downloaded from the local WSUS server. 

Initially, I wrote two sets of Windows PowerShell scripts: one to audit for patches that were downloaded to each server and waiting to be installed, and another that would install those patches on the server remotely. Though this is a great solution for a couple other folks and me who are familiar with Windows PowerShell, it is not the “complete solution” that we were looking to use with everyone else in my shop. We needed something that would make it easy for anyone to use (a GUI) and still perform the same tasks as the original scripts I wrote.

Enter the PowerShell Patch Audit/Installation GUI (PoshPAIG).

Image of PoshPAIG interface

Rather than go into a lot about how I built PoshPAIG and why I made it the way I did, I am going to go more into some of the new features that I implemented for the latest version, 1.6. If you wish to learn more about what I did to initially build this utility, go to this blog post. My next post tomorrow will also go into some issues I ran into during the build and what I was able to do to resolve them.

Running the utility

First download PoshPAIG. From there, unzip the file to wherever you wish. You can then open a Windows PowerShell console, navigate to the directory structure, and run the Start-PoshPAIG.ps1 script to start the utility.

No more double-clicking a system to run an operation

One of the first changes I made was the removal of double-clicking on a system in the server list to perform the specified operation. Instead of doing that, you can now right-click a system and bring up a shortcut menu to select a few different items, such as removing a server, viewing the WindowsUpdatelog.log, viewing installed updates and performing other operations against the remote system. Clicking Run in the shortcut menu will perform the specified operation that is designated below the server list (Audit, Install, Test Network Connection, or Reboot).

Image of shortcut menu that appears when right-clicking a system

Adding multiple computers using the Add Server menu

Originally, the Add Server menu only let you add one system at a time. While this is okay, I felt that it needed the ability to add more than one system at a time. So with that, I’ve made it so you can add more than one computer by separating the names with a comma. Simply right-click the server list window, click Add Server, type each system name, and then click OK.

The following figure shows the UI before clicking OK.

Image of UI before clicking OK

After clicking OK, the servers are then added to the server list, as shown in the following figure.

Image of UI after clicking OK

Support for operations against multiple systems at a time

One of my most requested features was the ability to perform the operations (audit, install, etc.) against multiple systems at a time instead of against only one system at a time. Doing this allows for a much quicker process of completing whichever operation that you decide to do; otherwise, the operation could take a much longer time than you would expect.

As you can see from another new feature—the Notes column—all the systems are being audited for patches that have been downloaded from the WSUS server. As each system is finished, it will be updated accordingly on the server list with the number of patches found. Depending on the operation you choose, the Notes column and other columns will be affected as well based on the decision. During these operations, if the system is not reachable on the network, the Notes column will report it as being offline.

Image of Notes column with Completed and Offline statuses

By default, the number of systems that will be run against at a time is 20. You can adjust this in the Start-PoshPAIG.ps1 script by modifying the $maxconcurrentjobs variable on line 37 to whatever you feel is an appropriate value.

Sort columns when clicked

Something that should have been in the first version but unfortunately was not as easy to implement as I would have thought (more on this in tomorrow’s post) was the ability to sort a column when clicked. So in this latest version, you can now sort a column by clicking it. The following figure shows sorting the Audited column so that the system with the most patches required is first.

Image of Audited column sorted by systems needing most patches at top


Tracked reboot of systems

Another option I added was the ability to send a reboot command to the remote systems; it will continue to monitor the system until it is back online. To avoid having too many systems being rebooted at one time, I have hard-coded a limit of five systems at a time to be rebooted. If a machine has not been reported as being back online within five minutes, it will be registered as being offline and will need more investigation into the system to see why it has not came back online.

To do this, first select the Reboot option below the server list, and you can choose to run the command against every system by clicking Run. Or you can run the command against a specific computer or computers by selecting them from the server list, right-clicking the server list, and then clicking Run.

Image of running command against specific computer

A warning is first presented advising the user that the computer will be rebooted if the user chooses to continue.

Image of reboot warning

Clicking Yes will continue the reboot process. When completed, you will see the Completed note in the Notes column or the Offline note.

Ping sweep

Another option implemented in version 1.6 is performing a “ping sweep” of all the systems in the server list. This is done by selecting the Test Network Connection check box, clicking Run or selecting the systems, and clicking Run from the menu when right-clicking the server list.

Image of testing network connection

The Notes column shows that the network check is occurring.

Image of results of network connection check

As you can see, two systems are online and the rest are offline. Note that the total time to perform this was just less than 13 seconds.

View WindowsUpdate.log for troubleshooting

I thought that this would be a nice and obvious addition to this utility. One big caveat is that this can only be run against one system at a time. Parsing and using Out-GridView to display the output against a file that could possibly contain several thousand lines of information would slow things down quite a bit. Just right-click a system, click WindowsUpdateLog in the shortcut menu, and then click one of the four options (Last 25, Last 50, Last 100, and Entire Log) to have the utility grab the remote log and display it.

Image of options for viewing WindowsUpdateLog

In this instance, I selected the last 50 lines from the WindowsUpdatelog.log to view.

Show currently installed updates

While not necessarily needed, I figured I would add an option to view the currently installed updates on one or more remote systems. Just select the systems, right-click Installed Updates, and then click View Installed Updates in the shortcut menu.

Image of View Installed Updates option

After all of the updates have been gathered, you can then view the installed updates on each system.

Image of viewing installed updates on each system


I hope everyone enjoyed this post displaying some of the new features of my latest project. This is a work in progress and will have more releases in the future. If you have any feature requests or any bugs that you find, be sure to log them in the Issue Tracker on CodePlex.

Tomorrow, I will address some issues I ran into while creating this utility and the steps I took to resolve them. I promise you will see some code in that post.


I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy



Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • That really nice Ed.  It is sort of a manual version of WSUS. There are a lot of admin out there who will go nuts or this thing.

  • Now try it this way.  It does not require admin role to run queries.  You only need to elevate to run updates.  We can pick an individual update and download it from WU or WSUS or anywhere else.

    $wua=New-Object -ComObject Microsoft.Update.Session




    $results.Updates | select title

    Microsft.Update does not require PsExec to install patches or to execute remotely.

  • Thanks, JRV! Glad you like the utility I wrote. It's been a fun learning process to work on something like this.

    The code you posted is actually very similar to what I use on the back-end to perform the queries for updates. I have just a couple of small changes as I want to be able to query for updates on remote systems.

    $c = "remoteserver"

    $updatesession =  [activator]::CreateInstance([type]::GetTypeFromProgID("Microsoft.Update.Session",$c))

    $updatesearcher = $updatesession.CreateUpdateSearcher()

    $searchresult = $updatesearcher.Search("IsInstalled=0")

    Your last statement has my attention:

    "Microsoft.Update does not require PsExec to install patches or to execute remotely."

    I've spent a bit of time researching this and some trial and error to make the installation work remotely with the COM object, but it is stated on the msdn site that remote execution of patch installations via the COM object is not allowed.

    I've tried using PowerShell remoting, creating a process remotely using WMI and a couple other ways that all ended in failure as the COM object can still see that a remote attempt is being used to try and install updates. I would be very interested if you found a way to utilize the COM object remotely without the need of an outside dependency to remotely install the updates.


  • It would nice to have a column that said "Pending Reboot".  That way when you were running a post patch audit, you could pick up any systems you forgot to reboot.  Awesome work!

  • I understand the not installing a patch remotely. However, if you specify it to be downloaded and installed from WU/MU or WSUS you can trigger it remotely I believe.  It’s been a while since I did this but I seem to remember that that was the trick.

    Yes - you cannot just point at a package and install it. It has to be downloaded into the patch distribution folder and registered.  Only WU can do this.  If you do not have WSUS or the patch is not distributed by WU/MU then you would have to use either PsExec or WMI. I prefer WMI.

    I had some issues running the code but need to look at it more closely. I will post to Codeplex.

    It’s an amazing piece of scripting. You must have sweat bullets.  You also introduce a bunch of interesting techniques for coding Forms and complex programs. I will spend more time looking through you code. Thanks again.

  • Nice, Is there a way to query updates based on the os and just download them so it would speed up the install process?

  • @aWp Thanks for the input and suggestion! I can certainly look into adding that into the UI.

    @John Currently PoshPAIG does not support the downloading of updates for a specific OS. I believe that feature was requested on the codeplex site, but if not, you can certainly use the issue tracker to request that feature and I will look into adding it.

    @jrv I appreciate the kind words regarding PoshPAIG. There was a lot of sweat, blood and tears spent working on this. But I am very happy with the results thus far.

    I'll keep my eyes open for your comments on codeplex, especially regarding the issue running the utility.

    I see what you are saying now regarding the updates. I would also recommend using WMI to install a patch if not coming from WSUS or another location.  Something I should have mentioned in this article and will be sure to update on the codeplex page is that the optimal environment for using this utility is that the systems should have their WU settings set to "download but do not install" either through Group Policy or Local Policy and have an internal WSUS server to approve specific patches for their environment. It will fine outside this environment, but this way a sys admin has complete control over what is being installed on their network.

  • Hello Boe

    -thanx for a great utility that will make instant querying much easier. Just a quick question?

    I have tested the Powershell GUI on one of my servers and it runs fine, but as soon as I run it against the remote servers, I get and 8007005 error? Anyway to get my login credentials changed in the script, when I attach to these remote servers?


  • @Ashleyh Glad you like the utility! Are the servers on a domain or are they standalone systems? If domain, you can use an administrative account that has access to those systems to run the utility. Because of the way I currently have the utility coded, adding alternate credentials will take some time to work. But I do encourage you to log it in the Issue Tracker on the codeplex page.

    Also, if you wish we can continue this discussion on the discussion page on the site as well.

    Thanks again for comments regarding PoshPAIG!


  • Any way you can add these options to control the Windows Update service by right-clicking on the server?

    (gwmi win32_service | ?{$ -match 'wuauserv'}).Stop()

    (gwmi win32_service | ?{$ -match 'wuauserv'}).Start()

    (gwmi win32_service | ?{$ -match 'wuauserv'}).Restart()

  • @arposh Absolutely! That is something that should have been in PoshPAIG already! :) Thanks for the feature request!

  • Oops.  Forgot to include the computername from listview.

    (gwmi win32_service -computername ($listview.selecteditems | %{$}) | ?{$ -match 'wuauserv'}).Restart()

  • Hi Boe,


    Really awesome ... being no admin I can't really test and use it in full detail.

    BUT: I'm very impressed by your code to make it work, which is an excellent learning resource and I can really believe that it was HARD work to get so far!

    THANKS, Klaus

  • I am getting this error?

    PS C:\PoshPAIG_2_0_1> .\Start-PoshPAIG.ps1

    Cannot index into a null array.

    At C:\PoshPAIG_2_0_1\Start-PoshPAIG.ps1:53 char:9

    +         $Global:maxConcurrentJobs = $Optionshash['MaxJobs']

    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

       + FullyQualifiedErrorId : NullArray

    Cannot index into a null array.

    At C:\PoshPAIG_2_0_1\Start-PoshPAIG.ps1:54 char:9

    +         $Global:MaxRebootJobs = $Optionshash['MaxRebootJobs']

    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

       + FullyQualifiedErrorId : NullArray

    Cannot index into a null array.

    At C:\PoshPAIG_2_0_1\Start-PoshPAIG.ps1:55 char:13

    +         If ($Optionshash['ReportPath']) {

    +             ~~~~~~~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

       + FullyQualifiedErrorId : NullArray

  • hi gurus,

    Sorry a bit anoying you guys.

    I have tried and run but my interface didn't show any radio button such as audit patchees, Install patches, test network connection and reboot at the bottom.  Should I need to run script on wsus server or client PC.  Am I doing anything wrong or help me via Email