Determine Pending Reboot Status—PowerShell Style! Part 2

Summary: Guest blogger, Brian Wilhite, talks about using Windows PowerShell to detect a server that is in pending reboot status.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have the conclusion to Brian Wilhite’s guest blog series about detecting pending reboots via Windows PowerShell. Prior to reading today’s post, you should read Determine Pending Reboot Status—PowerShell Style! Part 1. You can also check out Brian’s prior Hey, Scripting Guy! Blog posts.

The Windows PowerShell “awesomeness”

Now that I’m satisfied with my research and validation, I’m ready to write some Windows PowerShell “awesomeness.” Also, I think it’s important to determine exactly what my function will return before getting started. I decided on the following properties in a PSObject:

  • Computer: Target computer (string)
  • CBServicing: Representing the component-based servicing after Windows 2008 (Boolean)
  • WindowsUpdate: Representing the WindowsUpdate\Auto Update Registry value (Boolean)
  • CCMClientSDK: Representing the result of the Service Center Configuration Manager 2012 client WMI DetermineIfRebootPending method (Boolean)
  • PendFileRename: Representing whether the PendingFileRenameOperations REG_MULTI_SZ entry has a value (Boolean)
  • PendFileRenVal: Representing the value of the PendingFileRenameOperations REG_MULTI_SZ entry (string[])
  • RebootPending: Representing a culmination of the Boolean values from the previous properties. If any value returns $true, so this property will be (Boolean)

With the object defined, I can commence developing the function. When authoring a function, I use a template that basically prepopulates the function's framework. I will be covering the core code components of the function, mainly the contents of the Process block. Although the template is a good tool, it's important to show how the ComputerName parameter is defined:







The previous parameter line has ValueFromPipeline and ValueFromPipelineByPropertyName defined for the ComputerName parameter.

  • ValueFromPipleline enables strings to be piped into the function and processed via the process block.
  • ValueFromPipelineByPropertyName enables the ComputerName property from other objects to be piped to the function and processed by the process block.

Because ComputerName was typed as a string array, I’m going to iterate through every computer in ComputerName and run the following code, explaining each step along the way.

First, I set several values to $false to reduce the number of if/else statements. Notice that I can use one equal sign (=) to assign multiple values to multiple variables on one line.

$PendFileRename,$Pending,$SCCM = $false,$false,$false

Next, I assign $null to the $CBSRebootPend variable because operating systems prior to Windows Server 2003 do not have Component-Based Servicing (CBS).

$CBSRebootPend = $null

This allows the CBServicing property, which the function returns, to be null if the Component-Based Servicing registry key does not exist. Next, I query for the BuildNumber via WMI to determine whether the Component-Based Servicing key needs to be queried.

$WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer

Notice that I used the Property parameter for Get-WmiObject for optimization purposes. Performing the query in this way reduces the number of properties returned—therefore, decreasing the time it takes to run against multiple systems. Next, I make a connection to the registry by using the [Microsoft.Win32.RegistryKey] type accelerator.

$RegCon = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]"LocalMachine",$Computer)

If you review the Microsoft.Win32.RegistryKey class reference page on MSDN, you’ll notice an OpenRemoteBaseKey method that accepts two arguments, first the RegistryHive, and then a string that represents the remote computer. The following code is used to determine if the CBS key is present. If it is present, it executes the query, and performs it on computers with a BuildNumber of 6001 (post Windows Vista and Windows Server 2008 RTM versions).

If ($WMI_OS.BuildNumber -ge 6001)


The OpenSubKey method has a GetSubKeyNames method that is called and stored in the $RegSubKeysCBS variable.

 $RegSubKeysCBS = $RegCon.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\").GetSubKeyNames()

The $CBSRebootPend variable will be $true if the RebootPending key is present in the $RegSubKeysCBS variable; it will be $false if it’s not. The $CBSRebootPend variable will be used later for the CBServicing property.

$CBSRebootPend = $RegSubKeysCBS -contains "RebootPending"                               

}## End If ($WMI_OS.BuildNumber -ge 6001)

Because the WindowsUpdate/Auto Update and the PendingFileRenameOperations registry values are common on all versions of Windows (at least since Windows XP and Windows Server 2003), I’ll query those entries unconditionally.

$RegWUAU = $RegCon.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\")

$RegWUAURebootReq = $RegWUAU.GetSubKeyNames()

The $WUAURebootReq variable will be $true if the RebootRequired key is present. Again, it will be $false if it’s not. This variable will also be used later for the WindowsUpdate property.

$WUAURebootReq = $RegWUAURebootReq -contains "RebootRequired"

$RegSubKeySM = $RegCon.OpenSubKey("SYSTEM\CurrentControlSet\Control\Session Manager\")

Here I’ve captured the value of the PendingFileRenameOperations entry. I’ll use the $RegValuePFRO variable to populate the PendFileRenVal property:

$RegValuePFRO = $RegSubKeySM.GetValue("PendingFileRenameOperations",$null)

The registry data collection is complete, so I’m going to clean up the connection object. Any time you us this method to connect to the registry, you should close the connection. Think of this as opening a door before entering a room—generally you close it when you leave. Working with the registry is really no different. So the following script will close your registry connection by calling the Close method from the $RegCon object that I created when I initially connected to the registry.


Now let’s evaluate the $RegValuePFRO variable to determine if the PendingFileRenameOperations REG_MULTI_SZ registry entry has a value. If it has a value, I’m going to set the $PendFileRename variable to $true, which will be used as the PendFileRename property.

If ($RegValuePFRO)


 $PendFileRename = $true

}#End If ($RegValuePFRO)

Here, I’m setting the $CCMClientSDK to $null because there may be a chance that the function iterates through multiple systems and not allow the previous value to persist from another system:

$CCMClientSDK = $null

Next, I’m going to use a technique called splatting, when calling Invoke-WmiMethod for the DetermineIfRebootPending method.

Note  Splatting is really cool. You can pass multiple parameters to a cmdlet or function by using a hash table. The “key” in the hash table will be the parameter name and the “value” will be the parameter’s argument.

$CCMSplat = @{







To use the hash table that was just created, with all my Invoke-WmiMethod parameters, I’ll have to use the “@” character instead of referencing the “$” as you might assume.

$CCMClientSDK = Invoke-WmiMethod @CCMSplat

I like this technique because it allows me to clean up a one-liner in a script that may stretch across two screens. In the following image, check out how the splat technique is cleaner than the traditional one-liner.

Image of command output

OK, enough about splatting. Jumping back on track…

After the DetermineIfRebootPending WMI method is called, the following code is run to determine if the $SCCM value should be $true, $false, or $null. The $null value is returned if the CCM_ClientUtilities WMI class doesn’t exist. I have also incorporated error handling if the return code from the WMI method call is not equal to 0.

If ($CCMClientSDK)


 If ($CCMClientSDK.ReturnValue -ne 0)


  Write-Warning "Error: DetermineIfRebootPending returned error code $($CCMClientSDK.ReturnValue)"

 }## End If ($CCMClientSDK -and $CCMClientSDK.ReturnValue -ne 0)

 If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending)


  $SCCM = $true

 }## End If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending)

}## End If ($CCMClientSDK)



 $SCCM = $null


Next, if any of the $CBSRebootPend, $WUAURebootReq, $SCCM, or $PendFileRename variables are $true, I’ll set $Pending to $true, which will in turn populate the final property in my custom object, RebootPending.

If ($CBSRebootPend -or $WUAURebootReq -or $SCCM -or $PendFileRename)


 $Pending = $true

}## End If ($CBS -or $WUAU -or $PendFileRename)

Finally, I create my custom object. Once again, I’ve used the splatting technique for my select statement. Because my PSObject was created by using a hash table, I will use the Select-Object cmdlet to order the properties based on how I think it is best presented to the user.

Note   If you use Windows PowerShell 3.0, you can use the [PSCustomObject] type accelerator when you create an object and bypass the need to use the Select-Object technique.

$SelectSplat = @{



New-Object -TypeName PSObject -Property @{








 } | Select-Object @SelectSplat

The following screenshot illustrates the output of my function when run without the ComputerName parameter, which defaults to $env:COMPUTERNAME.

Image of command output

You’ll notice that there are two values that are null. As noted earlier, this is by design because my local workstation does not have SCCM 2012 installed, nor was the PendingFileRenameOperations populated at the time I ran the function.

I’ve rambled enough for now. If you want to download my function, you can find it in the Script Center Repository:

Get-PendingReboot - Query Computer(s) For Pending Reboot State

If you have suggestions, questions, or concerns, please leave feedback on the download page of the Script Center Repository. Without the suggestions from others, this function wouldn’t be as complete as it is today. So if you have an idea, don’t hesitate to contact me. Until next time…


Thank you, Brian. Awesome job. Join me tomorrow when I will talk about cool Windows PowerShell stuff.

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

Comments (7)

  1. Excluding the SCCM bits, I’ve written a simpler version of this function for local use:

    function Test-RebootNeeded

    $NeedsReboot = $false

    #Windows Update
    $WURegKey = get-item "HKLM:SOFTWAREMicrosoftWindowsCurrentVersionWindowsUpdateAuto Update"
    if ($WURegKey.Property -contains "RebootRequired") {$NeedsReboot = $true}
    #Component Based Servicing
    $CBSRegkey = get-item "HKLM:SOFTWAREMicrosoftWindowsCurrentVersionComponent Based Servicing"
    if ($CBSRegkey.Property -contains "RebootRequired") {$NeedsReboot = $true}
    #Pending File Rename Operations
    $PFRORegkey = get-item "HKLM:SYSTEMCurrentControlSetControlSession ManagerFileRenameOperations"
    if ($PFRORegkey.Property) {$NeedsReboot = $true}

  2. Anonymous says:

    $Servers = "","FAILTest","",""
    $LocalhostNetBios = $ENV:COMPUTERNAME
    $RegKeyCBS = "SOFTWAREMicrosoftWindowsCurrentVersionComponent Based ServicingRebootPending"
    $RegKeyWUAU = "SOFTWAREMicrosoftWindowsCurrentVersionWindowsUpdateAuto UpdateRebootRequired"
    $RegKeyFileRename = "SYSTEMCurrentControlSetControlSession Manager"
    #Registry Hive
    [long]$HIVE_HKLM = 2147483650
    $Hive = $HIVE_HKLM

    $OptionCredentials = Read-Host("Do you want to provide explicit credentials?(Y/N)")

    If ($OptionCredentials -eq "Y") {
    Write-Host "Please provide Domain credentials: `n 1. DomainUser – scans servers with provided credentials. `n 2. IF only user is provided will scan servers by their Domain and provided user. `n E.g. ServerDomainUser"
    $CredAsk = Get-Credential -Credential ”
    $UserName = $CredAsk.Username
    $SecuredPassWord = ConvertTo-SecureString -AsPlainText ($CredAsk.GetNetworkCredential().Password) -Force

    foreach ($Server in $Servers)

    $Pending = "OK"
    $errormsg = "OK"
    $PendingFor = $Null
    $extraParams = @{}
    $Credentials = $Null
    $Domain = $server -replace (($server.split(".")[0] + "."),"")

    # *** Check if additional credetial are explicity provided via Buton "Credentials".

    If (($CredAsk -ne $null) -and ($Server.split(".")[0] -ne "$LocalhostNetBios"))
    If ($UserName -match "\") {$User = $UserName}
    Else {$User = "${domain}${UserName}"}
    $Credentials = new-object System.Management.Automation.PSCredential $user, $SecuredPassWord
    $extraParams["credential"] = $Credentials
    If (Test-Connection -ComputerName $Server -count 2 -quiet)
    Try {
    $RegProv = Get-WmiObject -List -Namespace "rootdefault" -computername $Server @ExtraParams | Where-Object {$_.Name -eq "StdRegProv"}
    Catch {
    [string]$errormsg = $_.Exception.Message
    If ($errormsg -eq "OK")
    # Check for Pending CBS operations
    If ($($RegProv.EnumValues($Hive, $RegKeyCBS)).ReturnValue -eq 0) {$PendingFor += "CBS, "}
    # Check for Pending WUAU operations
    If ($($RegProv.EnumValues($Hive, $RegKeyWUAU)).ReturnValue -eq 0) {$PendingFor += "WUAU, "}
    # Check for Pending File Rename Operations
    $PendFileRenameRegValue = $RegProv.GetMultiStringValue($Hive, $RegKeyFileRename, "PendingFileRenameOperations")
    If ($PendFileRenameRegValue.ReturnValue -eq 0) { $PendingFor += "PendFileRename of $($PendFileRenameRegValue.sValue)" }

    # If any pending for reboot operation is found
    If ($PendingFor) {$Pending = "Reboot Pending : $PendingFor"}
    Write-Host "$Server : $Pending"
    Else { Write-Host "$Server : Unaccessible $errormsg"}
    Else { Write-Host "$Server : Unreachable (NO ICMP)"}

  3. Anonymous says:

    Ahhh, how do I edit my previous post(s)? 🙂 I need to edit the "TRY/Catch" in the WMI call with adding of course the:
    -EA "Stop" , in order the errormsg ($_.Exception.Message) to be captured… :

    $RegProv = Get-WmiObject -List -Namespace "rootdefault" -EA "Stop" -computername $Server @ExtraParams | Where-Object {$_.Name -eq "StdRegProv"}

    PS: If anyone could edit this line with adding the -EA "Stop", please remove this post. Thanks in advance! Happy Easter@all!

  4. Anonymous says:

    @Chris Koch, I have bumped my head previously with PS2 remote registry queries under explicit credentials as well, which of course led me to WMI’s one. Thanks for asking. Same question came to me at first. 🙂
    Above is a "WMI based Remote Registry Check example" I’ve written for Pending Reboot check, excluding the SCCM bits.

    Example outputs would be :

    PS > .pending_reboot.ps1
    Do you want to provide explicit credentials?(Y/N): y
    Please provide Domain credentials:
    1. DomainUser – scans servers with provided credentials.
    2. IF only user is provided will scan servers by their Domain and provided user.
    E.g. ServerDomainUser : Reboot Pending : CBS, WUAU,
    FAILTest : Unreachable (NO ICMP) : OK : OK

    PS > .pending_reboot.ps1
    Do you want to provide explicit credentials?(Y/N): n : Reboot Pending : CBS, WUAU,
    FAILTest : Unreachable (NO ICMP) : OK : Reboot Pending : PendFileRename of ??C:blqh-blqh.exe

    PS: Feel free to adapt it to your needs (I use it in multi-job based scripts, and not looping with foreach as the example in previous ) & Cheers! 🙂

  5. Chris Koch says:

    Brian — Although I can modify the WMI queries of this script to accept a -Credential parameter, this does not seem to be possible with [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey. Occasionally need to Get-PendingReboot against a workgroup server
    from a domain PC… Ever modified the script to pull the registry keys via a WMI call instead?

  6. Pete Simpson says:

    Does this require Win8/Server2012 to run? Running on Win7/Server2008R2 seems to return nothing once I got past the scripting permissions errors.

  7. Scott Galloway says:

    Getting this error, not on all systems, but here is the cmd
    Get-PendingReboot -ComputerName (Get-Content C:powershellservers.txt) |Format-Table -AutoSize

    Warning Server Name"You cannot call a method on a null-valued expression."

    mixture of xp, 2003 2008 systems

Skip to main content