Checking for pending reboots with PowerShell

July 29th, 2009

I found out the hard way a few days ago that our old, tried-and-true method of determining if there were pending reboots had changed with the latest Windows Server OS, Server 2008. In the old days (last year), we could check for pending reboots on Windows 2003 simply by looking at the ‘PendingFileRenameOperations’ property in the registry, but now with Server 2008, times have changed.

I have now tasked myself with spending quality time with a Server 2008 VM and a copy of ProcessMonitor, so as to see if I can find a new way to determine this sometimes-critical piece of information. Unfortunately, I have not yet found a 100% reliable way of getting this information, but I’d like to share what I’ve found so far.

function Get-PendingReboot($computer = '.') {
	$hkey		= 'LocalMachine';
	$path_server	= 'SOFTWARE\Microsoft\ServerManager';
	$path_control	= 'SYSTEM\CurrentControlSet\Control';
	$path_session	= join-path $path_control 'Session Manager';
	$path_name	= join-path $path_control 'ComputerName';
	$path_name_old	= join-path $path_name 'ActiveComputerName';
	$path_name_new	= join-path $path_name 'ComputerName';

	$pending_rename	= 'PendingFileRenameOperations';
	$pending_rename_2	= 'PendingFileRenameOperations2';
	$attempts	= 'CurrentRebootAttempts';
	$computer_name	= 'ComputerName';

	$num_attempts	= 0;
	$name_old	= $null;
	$name_new	= $null;

	$reg= [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hkey, $computer);

	$key_session	= $reg.OpenSubKey($path_session);
	if ($key_session -ne $null) {
		$session_values	= @($key_session.GetValueNames());
		$key_session.Close() | out-null;
	}

	$key_server	= $reg.OpenSubKey($path_server);
	if ($key_server -ne $null) {
		$num_attempts = $key_server.GetValue($attempts);
		$key_server.Close() | out-null;
	}

	$key_name_old	= $reg.OpenSubKey($path_name_old);
	if ($key_name_old -ne $null) {
		$name_old = $key_name_old.GetValue($computer_name);
		$key_name_old.Close() | out-null;

		$key_name_new	= $reg.OpenSubKey($path_name_new);
		if ($key_name_new -ne $null) {
			$name_new = $key_name_new.GetValue($computer_name);
			$key_name_new.Close() | out-null;
		}
	}

	$reg.Close() | out-null;

	if ((($session_values -contains $pending_rename) `
	-or ($session_values -contains $pending_rename_2)) `
	-or (($num_attempts -gt 0) -or ($name_old -ne $name_new))) {
		return $true;
	}
	else {
		return $false;
	}
}

Let’s start with a basic question…why would you need this information at all? For me, I use this function to fetch the current state when automating application installs and patches. I like to know ahead of time if I’m going to need to schedule reboots prior to mass deployments.

Anyway, let’s look at the code.

We take in the computer name as an argument, and if there isn’t one, we assume that the computer in question is the local machine (denoted by the “.”).

function Get-PendingReboot($computer = '.') {

Then, we set up our variables. A lot of what I did below was done in order to fit the paths into the space provided by the blogging software, which is why it looks a bit unwieldy.

The key things that we’re tracking are the presence of pending file renames, a count of attempted reboots, and whether or not the computer name has changed since the last reboot.

$hkey		= 'LocalMachine';
$path_server	= 'SOFTWARE\Microsoft\ServerManager';
$path_control	= 'SYSTEM\CurrentControlSet\Control';
$path_session	= join-path $path_control 'Session Manager';
$path_name	= join-path $path_control 'ComputerName';
$path_name_old	= join-path $path_name 'ActiveComputerName';
$path_name_new	= join-path $path_name 'ComputerName';

$pending_rename	= 'PendingFileRenameOperations';
$pending_rename_2	= 'PendingFileRenameOperations2';
$attempts	= 'CurrentRebootAttempts';
$computer_name	= 'ComputerName';

$num_attempts	= 0;
$name_old	= $null;
$name_new	= $null;

Next, we open the remote registry.

$reg	= [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hkey, $computer);

Then, see if the Session Manager key exists, and see what its values are, so that we can find out if there are pending file renames. Close it if it exists.

$key_session	= $reg.OpenSubKey($path_session);
if ($key_session -ne $null) {
	$session_values	= @($key_session.GetValueNames());
	$key_session.Close() | out-null;
}

Next, get the specific value of current reboot attempts from the registry.

$key_server	= $reg.OpenSubKey($path_server);
if ($key_server -ne $null) {
	$num_attempts = $key_server.GetValue($attempts);
	$key_server.Close() | out-null;
}

Then, grab the active (old) computer name and the new computer name from the registry, and close the registry, since we’re done with it.

$key_name_old	= $reg.OpenSubKey($path_name_old);
if ($key_name_old -ne $null) {
	$name_old = $key_name_old.GetValue($computer_name);
	$key_name_old.Close() | out-null;

	$key_name_new	= $reg.OpenSubKey($path_name_new);
	if ($key_name_new -ne $null) {
		$name_new = $key_name_new.GetValue($computer_name);
		$key_name_new.Close() | out-null;
	}
}

$reg.Close() | out-null;

Finally, check each of the values we stored to see if they meet the conditions necessary to consider there to be a reboot pending.

if ((($session_values -contains $pending_rename) `
-or ($session_values -contains $pending_rename_2)) `
-or (($num_attempts -gt 0) -or ($name_old -ne $name_new))) {
	return $true;
}
else {
	return $false;
}

Once you have the function accessible, let’s try testing it. I installed a bunch of security updates on my Windows XP machine, called the function, then called it when passing the name of one of my Server 2008 test VMs.

PS > Get-PendingReboot
True
PS > Get-PendingReboot "devserver0001"
False
PS >

Perfect. Now, with all that said and done, I’m still not finished researching the 2008 changes. I know that changing the domain membership triggers pending reboots, which this function does not detect. It’s entirely possible that I’ve overlooked something simple that’s been added to the Server 2008 WMI spec. I’ll update this article once I have a chance to research this further.

Categories: PowerShell, Server 2003, Server 2008

Tags: , Leave a comment

Comments Feed3 Comments

  1. Joe Heaton

    This is exactly the type of thing I need. Our environment is difficult with patching, as there are numerous dependencies between servers, making it impossible to allow automatic reboots with updates. We’ve found a product that will do the patching for us, without the automatic reboot, but I needed some tool to give me a report of servers that are in a Pending Reboot state. Your script here looks like it is exactly what I need. I have a couple of additional questions about it, since I’m extremely new to any type of scripting, much less Powershell. Could you contact me offline, to discuss?

  2. Aaron Dodd

    This doesn’t work for WSUS/AutomaticUpdates-based patches requiring reboot. You’d want to add checking for the existence of subkeys at:

    HKLM\Software\MicrosofWindows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired

    Thanks!

  3. Aaron Dodd

    I’ve modified the script to work with WSUS and here’s what I added. Thanks for your work on this :)

    Added variable:

    $path_wsus = ‘SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired’;

    Added check:

    $key_wsus = $reg.OpenSubKey($path_wsus);
    if ($key_wsus -ne $null) {
    $wsus_values = @($key_wsus.GetValueNames());
    if ($wsus_values) {
    $wsus_rbpending = $true
    } else {
    $wsus_rbpending = $false
    }
    $key_wsus.Close() | out-null;
    }

    modified return section:

    if ( `
    (($session_values -contains $pending_rename) -or ($session_values -contains $pending_rename_2)) `
    -or (($num_attempts -gt 0) -or ($name_old -ne $name_new)) `
    -or ($wsus_rbpending)) {
    return $true;
    }
    else {
    return $false;
    }

    This just checks if ANY subkeys exist at that path and returns true if they do.

Leave a comment

Feed

http://www.techmumbojumblog.com / Checking for pending reboots with PowerShell