Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to find servers that need to be rebooted.
Hey, Scripting Guy! My boss is a Falstaff sort of character, but every once in a while he does actually have a good idea. When he recently logged on to a server console (don’t ask me why my boss was actually logging on to the console of a server), a message popped up that stated the server needed to reboot to complete installing updates.
No one knows how long the server was in that state. So he “challenged” me (a euphuism for telling me to do it) to write a script that would detect any server on the network that needed to be rebooted following installation of hotfixes. I have looked around at several WMI classes, and searched the Hey, Scripting Guy! Blog, but to no avail. I am hoping you can come up with something that does not entail logging on to 1,000 servers and looking for reboot messages that may pop up (that was Falstaff’s idea—“hey, you can use RDP and not have to physically go to the box,” he said).
—YD
Hello YD,
Microsoft Scripting Guy, Ed Wilson, is here. Well, tomorrow February 21, 2013, at 9:30 PM Eastern Standard Time, I will be on the PowerScripting Podcast with Hal and Jon. It will be awesome, and it is a lot of fun. I have been looking forward to this event for weeks now; ever since the Scripting Wife asked me to be on the show (she does all their scheduling). I am not certain about the wisdom of turning on auto update for 1,000 servers. (I would think you would want to do testing, and the like, prior to pushing out the updates and use System Center or WSUS.)
Find servers that need a reboot
To find all servers that need a reboot, I look for the presence of the following registry key:
HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations
The PendingFileRenameOperations key does not appear if there are no files to rename. The rename operation occurs when a file is replaced, and the replacement file is renamed to take the place of the original file. The registry key is shown in the image that follows.
Use the ActiveDirectory module to find all servers
An easy way to find all of the servers is to use the ActiveDirectory module and the Get-ADComputer cmdlet. I am feeling a bit lazy today, so I am just going to get all the computers, choose the OperatingSystem attribute, pipe the results to the Where-Object, and pick up anything that looks like server. Here is the code.
Import-Module ActiveDirectory
$servers = Get-ADComputer -Filter * -Properties operatingsystem |
Where operatingsystem -match 'server'
Use Invoke-Command to find remote registry keys
Ok, now I need to see if the PendingFileRenameOperations registry key exists or not. There are myriad ways of doing this, but lately, I use the Invoke-Command and the Get-ItemProperty cmdlets to do this sort of thing.
Note To use a local variable on a remote computer requires using $using in Windows PowerShell 3.0 to make the local variable available on the remote computer. Otherwise, the Invoke-Command cmdlet looks for a variable on the remote computer and you will get a path not found, if you are lucky. If you are unlucky, you will get the value of a variable on the remote computer that does not work and it can be difficult to troubleshoot.
The command shown here uses the Invoke-Command cmdlet to run the command in the ScriptBlock against every server stored in the $servers variable. I use the automatic foreach feature of Windows PowerShell 3.0 to get back the name of each server. I specify the ErrorAction (EA is the alias) of silentlycontinue(0 is the enumeration value), so that no errors arise on the console. Remember the ScriptBlock uses Get-ItemProperty to retrieve the registry key value, and if that registry key does not exist, an error arises. The $using:Path brings in the value of the local $path variable, as $using:name brings in the value of $name. The code is shown here.
Invoke-command -ComputerName $servers.name -Ea 0 -ScriptBlock {
Get-ItemProperty -Path $using:path -Name $using:name}
Now, I use the Select-Object to choose only the computer name and to display whether the registry key exists. Here is the code that does this work:
Select-Object pscomputername, @{
LABEL='RebootRequired';
EXPRESSION={if($_.PendingFileRenameOperations){$true}}}
When the script runs, it returns a list of computers needing a reboot. This is shown in the following image.
Because the script returns objects with the computer name in it, the results can be piped to the Restart-Computer cmdlet to perform the restarts.
YD, that is all there is to using Windows PowerShell to find all servers that need a reboot. Join me tomorrow when I will talk about more way cool Windows PowerShell stuff.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy