Public/system/Get-PendingReboot.ps1

#Requires -Version 5.1
function Get-PendingReboot {
    <#
    .SYNOPSIS
        Checks pending reboot status from multiple Windows sources
    .DESCRIPTION
        Queries multiple system sources to determine if a Windows machine has a pending
        reboot. Sources include Component Based Servicing, Windows Update, pending file
        rename operations, pending computer rename, and SCCM client SDK.
    .PARAMETER ComputerName
        One or more computer names to check. Defaults to the local machine name.
        Accepts pipeline input by value and by property name.
    .PARAMETER Credential
        Optional PSCredential object for authenticating to remote machines.
        Not used for local machine checks.
    .EXAMPLE
        Get-PendingReboot
 
        Checks the local machine for any pending reboot indicators.
    .EXAMPLE
        Get-PendingReboot -ComputerName 'SERVER01'
 
        Checks a single remote machine for pending reboot status.
    .EXAMPLE
        'SERVER01', 'SERVER02' | Get-PendingReboot -Credential (Get-Credential)
 
        Checks multiple remote machines via pipeline input with alternate credentials.
    .OUTPUTS
    PSWinOps.PendingReboot
        Pending reboot status from multiple detection sources.
 
    .NOTES
        Author: Franck SALLET
        Version: 1.0.0
        Last Modified: 2026-03-18
        Requires: PowerShell 5.1+ / Windows only
        Permissions: Some checks may require administrative privileges.
            Remote checks require WinRM to be enabled on target machines.
            SCCM checks require the ConfigMgr client to be installed.
            Local computer name detection covers $env:COMPUTERNAME, 'localhost', and '.'.
            FQDN or IP of the local machine will be treated as a remote target.
     
    .LINK
    https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-movefileexw
    #>

    [CmdletBinding()]
    [OutputType('PSWinOps.PendingReboot')]
    param(
        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias('CN', 'Name', 'DNSHostName')]
        [string[]]$ComputerName = $env:COMPUTERNAME,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand)] Starting pending reboot check"

        $localNames = @($env:COMPUTERNAME, 'localhost', '.')

        $checkScript = {
            $cbsPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
            $wuPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired'
            $smPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
            $activeNamePath = 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName'
            $pendingNamePath = 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName'

            $componentBasedServicing = Test-Path -Path $cbsPath
            $windowsUpdate = Test-Path -Path $wuPath

            $pfroValue = (Get-ItemProperty -Path $smPath -Name 'PendingFileRenameOperations' -ErrorAction SilentlyContinue).PendingFileRenameOperations
            $pendingFileRename = ($null -ne $pfroValue) -and (@($pfroValue).Count -gt 0)

            $activeName = (Get-ItemProperty -Path $activeNamePath -Name 'ComputerName' -ErrorAction SilentlyContinue).ComputerName
            $pendingName = (Get-ItemProperty -Path $pendingNamePath -Name 'ComputerName' -ErrorAction SilentlyContinue).ComputerName
            $pendingComputerRename = $activeName -ne $pendingName

            $ccmClientSDK = $null
            try {
                $ccmResult = Invoke-CimMethod -Namespace 'ROOT\ccm\ClientSDK' -ClassName 'CCM_ClientUtilities' -MethodName 'DetermineIfRebootPending' -ErrorAction Stop
                $ccmClientSDK = [bool]($ccmResult.IsHardRebootPending -or $ccmResult.RebootPending)
            } catch {
                $ccmClientSDK = $null
            }

            @{
                ComponentBasedServicing = $componentBasedServicing
                WindowsUpdate           = $windowsUpdate
                PendingFileRename       = $pendingFileRename
                PendingComputerRename   = $pendingComputerRename
                CCMClientSDK            = $ccmClientSDK
            }
        }
    }

    process {
        foreach ($computer in $ComputerName) {
            Write-Verbose "[$($MyInvocation.MyCommand)] Checking pending reboot on '$computer'"

            try {
                $isLocal = $localNames -contains $computer

                if ($isLocal) {
                    $checkResult = & $checkScript
                } else {
                    $invokeParams = @{
                        ComputerName = $computer
                        ScriptBlock  = $checkScript
                        ErrorAction  = 'Stop'
                    }
                    if ($PSBoundParameters.ContainsKey('Credential')) {
                        $invokeParams['Credential'] = $Credential
                    }
                    $checkResult = Invoke-Command @invokeParams
                }

                $isRebootPending = (
                    $checkResult['ComponentBasedServicing'] -or
                    $checkResult['WindowsUpdate'] -or
                    $checkResult['PendingFileRename'] -or
                    $checkResult['PendingComputerRename'] -or
                    ($checkResult['CCMClientSDK'] -eq $true)
                )

                [PSCustomObject]@{
                    PSTypeName              = 'PSWinOps.PendingReboot'
                    ComputerName            = $computer
                    IsRebootPending         = [bool]$isRebootPending
                    ComponentBasedServicing = [bool]$checkResult['ComponentBasedServicing']
                    WindowsUpdate           = [bool]$checkResult['WindowsUpdate']
                    PendingFileRename       = [bool]$checkResult['PendingFileRename']
                    PendingComputerRename   = [bool]$checkResult['PendingComputerRename']
                    CCMClientSDK            = $checkResult['CCMClientSDK']
                    Timestamp               = (Get-Date -Format 'o')
                }
            } catch {
                Write-Error "[$($MyInvocation.MyCommand)] Failed to check pending reboot on '$computer': $_"
            }
        }
    }

    end {
        Write-Verbose "[$($MyInvocation.MyCommand)] Completed pending reboot check"
    }
}