Get-VBPendingReboot.ps1
|
function Get-VBPendingReboot { <# .SYNOPSIS Checks various registry locations and system flags to determine if Windows is pending a reboot. .DESCRIPTION This function examines multiple Windows subsystems to detect pending reboot conditions including: - Component-Based Servicing (CBS) - Windows Update - Pending file rename operations - Server Feature Manager (Server roles/features) - Computer rename operations - Domain join operations - Optional: SCCM client reboot flags .PARAMETER ComputerName Specifies the computer name(s) to check. Defaults to local computer. .PARAMETER IncludeSCCM Include SCCM/ConfigMgr client reboot detection (requires SCCM client). .PARAMETER Credential Specifies credentials for remote computer access. .EXAMPLE Get-VBPendingReboot Checks the local computer for pending reboots. .EXAMPLE Get-VBPendingReboot -ComputerName SERVER01, SERVER02 -IncludeSCCM Checks multiple servers including SCCM reboot flags. .EXAMPLE Get-VBPendingReboot | Where-Object RebootRequired | Select-Object ComputerName, *Pending Shows only computers that require a reboot with details. .OUTPUTS PSCustomObject with properties: - ComputerName : Computer that was checked - ComponentBasedServicing : CBS awaiting reboot - WindowsUpdateReboot : Windows Update requires reboot - PendingFileRenameOp : File rename operations pending - ServerFeatureManager : Server roles/features pending - ComputerRenamePending : Computer rename pending - DomainJoinReboot : Domain join/unjoin pending - SCCMReboot : SCCM client reboot pending (if -IncludeSCCM) - RebootRequired : Overall reboot status - LastBootTime : Last system boot time - UptimeDays : System uptime in days - CollectionTime : When the data was collected - Status : Success or Failed - Error : Error message if Status is Failed #> [CmdletBinding()] [OutputType([pscustomobject])] param ( [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('CN', 'Name')] [string[]]$ComputerName = $env:COMPUTERNAME, [switch]$IncludeSCCM, [PSCredential]$Credential ) begin { $scriptBlock = { param([bool]$IncludeSCCM) try { # Initialize result object $result = [pscustomobject]@{ ComputerName = $env:COMPUTERNAME ComponentBasedServicing = $false WindowsUpdateReboot = $false PendingFileRenameOp = $false ServerFeatureManager = $false ComputerRenamePending = $false DomainJoinReboot = $false SCCMReboot = $false RebootRequired = $false LastBootTime = $null UptimeDays = $null CollectionTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss') Status = 'Success' Error = $null } # Get system boot time and uptime try { $bootTime = (Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop).LastBootUpTime $result.LastBootTime = $bootTime $result.UptimeDays = [math]::Round((Get-Date).Subtract($bootTime).TotalDays, 2) } catch { Write-Warning "Could not retrieve boot time: $($_.Exception.Message)" } # Component-Based Servicing $cbsPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending' $result.ComponentBasedServicing = Test-Path $cbsPath # Windows Update Auto Update $wuPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' $result.WindowsUpdateReboot = Test-Path $wuPath # Pending File Rename Operations try { $sessionKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' $pendingRenames = (Get-ItemProperty -Path $sessionKey -Name 'PendingFileRenameOperations' -ErrorAction SilentlyContinue).PendingFileRenameOperations $result.PendingFileRenameOp = $null -ne $pendingRenames -and $pendingRenames.Count -gt 0 } catch { $result.PendingFileRenameOp = $false } # Server Feature Manager (Server 2008+) $sfmPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending' if (Test-Path $sfmPath) { $result.ServerFeatureManager = $true } else { $sfmPath2 = 'HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttempts' $result.ServerFeatureManager = Test-Path $sfmPath2 } # Computer Rename Pending try { $computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $result.ComputerRenamePending = $computerSystem.Name -ne $computerSystem.PendingComputerName -and ![string]::IsNullOrEmpty($computerSystem.PendingComputerName) } catch { $result.ComputerRenamePending = $false } # Domain Join/Unjoin pending $netlogonPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon' try { $netlogonParams = Get-ItemProperty -Path $netlogonPath -ErrorAction Stop $result.DomainJoinReboot = ($netlogonParams.JoinDomain -eq 1) -or ($netlogonParams.AvoidSpnSet -eq 1) } catch { $result.DomainJoinReboot = $false } # SCCM Client reboot pending (if requested) if ($IncludeSCCM) { try { $sccmClient = Get-CimInstance -Namespace 'ROOT\ccm\ClientSDK' -ClassName 'CCM_ClientUtilities' -ErrorAction Stop $rebootStatus = Invoke-CimMethod -InputObject $sccmClient -MethodName 'DetermineIfRebootPending' -ErrorAction Stop $result.SCCMReboot = $rebootStatus.RebootPending -or $rebootStatus.IsHardRebootPending } catch { $result.SCCMReboot = $false } } # Determine overall reboot requirement $rebootChecks = @( $result.ComponentBasedServicing, $result.WindowsUpdateReboot, $result.PendingFileRenameOp, $result.ServerFeatureManager, $result.ComputerRenamePending, $result.DomainJoinReboot ) if ($IncludeSCCM) { $rebootChecks += $result.SCCMReboot } $result.RebootRequired = $rebootChecks -contains $true return $result } catch { return [pscustomobject]@{ ComputerName = $env:COMPUTERNAME ComponentBasedServicing = $false WindowsUpdateReboot = $false PendingFileRenameOp = $false ServerFeatureManager = $false ComputerRenamePending = $false DomainJoinReboot = $false SCCMReboot = $false RebootRequired = $false LastBootTime = $null UptimeDays = $null CollectionTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss') Status = 'Failed' Error = $_.Exception.Message } } } } process { foreach ($computer in $ComputerName) { try { if ($computer -eq $env:COMPUTERNAME -or $computer -eq 'localhost' -or $computer -eq '.') { # Local execution & $scriptBlock $IncludeSCCM.IsPresent } else { # Remote execution $invokeParams = @{ ComputerName = $computer ScriptBlock = $scriptBlock ArgumentList = $IncludeSCCM.IsPresent ErrorAction = 'Stop' } if ($Credential) { $invokeParams.Credential = $Credential } Invoke-Command @invokeParams } } catch { [pscustomobject]@{ ComputerName = $computer ComponentBasedServicing = $false WindowsUpdateReboot = $false PendingFileRenameOp = $false ServerFeatureManager = $false ComputerRenamePending = $false DomainJoinReboot = $false SCCMReboot = $false RebootRequired = $false LastBootTime = $null UptimeDays = $null CollectionTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss') Status = 'Failed' Error = "Connection failed: $($_.Exception.Message)" } } } } } |