Use PowerShell to Determine if Specific Windows Updates are Installed on Remote Servers

It has been a crazy week to say the least. If you’re like me, you wanted to make sure that the specific Windows updates that patch the WannaCry ransomware vulnerability have been installed on all of your servers. I’ve seen a lot of functions and scripts this week to accomplish that task, but most of them seem too complicated in my opinion.

While it’s personal preference, I also always think about whether I should use a PowerShell one-liner, script, or function. Usually one-liners are something I type into the PowerShell console using all the aliases and positional parameters that I want since I’ll simply close out of the console when I’m done and the code is gone. Code with aliases and positional parameters shouldn’t be saved as scripts or shared with others.

For me, it’s a little more difficult to distinguish the difference between whether to use a PowerShell script or function. I write functions as reusable tools that I place into modules which allow me to easily access them. They’re generally generic enough to be used in multiple scenarios. Often times, I’ll write caller scripts for the functions so the specific data such as server names is not contained within the function itself which makes them easier to share with others outside of my organization.

In the scenario of testing for Windows updates that are installed specifically for WannaCry, I’ll use a script since the updates are cumulative and the KB numbers that are valid this month won’t be all of the ones that are valid next month that patch this vulnerability. In other words, I chose a script because the shelf life isn’t long enough to justify writing a function.

The Get-Hotfix cmdlet is used to check for hotfixes that are installed. It has a ComputerName parameter for targeting remote computers but more than likely it will be blocked by either a network or host firewall since it uses older protocols for communication. Although multiple computer names can be specified with Get-Hotfix, it runs against one computer at a time and it does not continue to the next computer once it tries to connect to one that is unreachable.

The following example demonstrates this problem where Get-Hotfix does not continue to the next computer once it reaches a computer that’s unreachable. It also confirms that Get-Hotfix does not run in parallel.

Long story short, don’t use the ComputerName parameter of Get-Hotfix to query remote computers because there’s a better way.

Wrap the Get-Hotfix cmdlet inside Invoke-Command to take advantage of PowerShell remoting. By default, Invoke-Command runs against 32 remote computers at a time in parallel which can be adjusted using the ThrottleLimit parameter. PowerShell remoting is also more firewall friendly and is enabled by default on servers running Windows Server 2012 and higher. It can be enabled on other versions using Enable-PSRemoting as long as PowerShell 2.0 or higher is installed.

Write-Warning-Message"Patch not found on $($p.origininfo.pscomputername)"

}

elseif($p.targetobject){

Write-Warning-Message"Unable to connect to $($p.targetobject)"

}

}

You could just as easily query Active Directory for the computer names or use Get-Content to obtain a list of computer names from a text file.

I placed the Patches variable inside of Invoke-Command to make the script PowerShell 2.0 compatible. If all of the remote servers were running PowerShell 3.0 or higher, that could have been defined at the top and the Using variable scope modifier could have used to use the local variable in the remote sessions.

Some scripts and functions that I’ve seen make this process more complicated than it needs to be by first checking to see what operating system and architecture the target computer is running to then only check for the specific updates that are applicable to that OS. There’s no reason for that since updates that aren’t applicable won’t be installed anyway and if any of these updates are found, it’s been patched. If you decided to write a function, you could simply return a Boolean value letting you know that the computer is good to go if any one of these updates is found.

7 Comments

The reason that I check for the OS version/build first is that the get-hotfix command takes a long time. The time to check the OS and only check for the appropriate patches significantly sped up my script.

I disagree. Checking for all of the hotfixes in my script versus only the one hotfix that was installed on a server ten times each and measuring the results with Measure-Command took almost the same amount of time. It would definitely take more time than the difference to check for the OS and architecture not mentioning the amount of time it would take to write a more complicated script.

Get-Hotfix is not slow at all unless used with the ComputerName parameter. As my blog article mentioned, don’t use the ComputerName parameter of Get-HotFix.

What then is to distinguish between patches that are not present because they are applicable but missing and patches that not applicable (so, also missing)? Won’t both of these cause ‘problem’ to be populated with an error entry? And then won’t your warning message print out ‘patch not found’ for patches on computers where there’s no expectation that they’d be found? Don’t you only want to know if a patch is applicable but not found?

My Speaking Engagements

User Groups

Disclaimer

All data and information provided on this site is for informational purposes only. Mike F Robbins (mikefrobbins.com) makes no representations as to accuracy, completeness, currentness, suitability, or validity of any information on this site and will not be liable for any errors, omissions, or delays in this information or any losses, injuries, or damages arising from its display or use. All information is provided on an as-is basis.