Private/Invoke-InfraPulsePendingRebootCheck.ps1
|
function Invoke-InfraPulsePendingRebootCheck { [CmdletBinding()] param( [Parameter(Mandatory)] [psobject]$Context, [Parameter(Mandatory)] [System.Collections.IDictionary]$Settings ) $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() $scriptBlock = { if ($env:OS -ne 'Windows_NT') { throw 'The PendingReboot check requires a Windows target.' } $reasons = New-Object System.Collections.Generic.List[string] $componentBasedServicing = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending' if (Test-Path -LiteralPath $componentBasedServicing) { [void]$reasons.Add('Component Based Servicing') } $windowsUpdate = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' if (Test-Path -LiteralPath $windowsUpdate) { [void]$reasons.Add('Windows Update') } $sessionManager = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' try { $pendingRename = (Get-ItemProperty -LiteralPath $sessionManager -Name PendingFileRenameOperations -ErrorAction Stop).PendingFileRenameOperations if ($null -ne $pendingRename -and @($pendingRename).Count -gt 0) { [void]$reasons.Add('Pending file rename operations') } } catch { Write-Debug "PendingFileRenameOperations is not present: $($_.Exception.Message)" } $updateExeVolatile = 'HKLM:\SOFTWARE\Microsoft\Updates' try { $volatileValue = (Get-ItemProperty -LiteralPath $updateExeVolatile -Name UpdateExeVolatile -ErrorAction Stop).UpdateExeVolatile if ([int]$volatileValue -ne 0) { [void]$reasons.Add('UpdateExeVolatile') } } catch { Write-Debug "UpdateExeVolatile is not present: $($_.Exception.Message)" } try { $activeName = (Get-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName' -Name ComputerName -ErrorAction Stop).ComputerName $configuredName = (Get-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName' -Name ComputerName -ErrorAction Stop).ComputerName if ($activeName -ne $configuredName) { [void]$reasons.Add('Computer rename') } } catch { Write-Debug "Computer-name comparison was unavailable: $($_.Exception.Message)" } try { if (Get-Command -Name Invoke-CimMethod -ErrorAction SilentlyContinue) { $ccmResult = Invoke-CimMethod -Namespace 'root\ccm\ClientSDK' -ClassName CCM_ClientUtilities -MethodName DetermineIfRebootPending -ErrorAction Stop if ($ccmResult.RebootPending -or $ccmResult.IsHardRebootPending) { [void]$reasons.Add('Configuration Manager client') } } } catch { Write-Debug "Configuration Manager reboot state was unavailable: $($_.Exception.Message)" } [pscustomobject]@{ Pending = $reasons.Count -gt 0 Reasons = @($reasons) } } $pending = Invoke-InfraPulseCommand -Context $Context -ScriptBlock $scriptBlock $stopwatch.Stop() # Exclusions are applied before deciding Pending so that hosts whose only # indicators are excluded report Healthy; every detected reason still # reaches the evidence through Reasons plus ExcludedReasons. $activeReasons = New-Object System.Collections.Generic.List[string] $excludedReasons = New-Object System.Collections.Generic.List[string] foreach ($reason in @($pending.Reasons)) { $excluded = $false foreach ($pattern in @($Settings.ExcludeReasons)) { if ([string]$reason -like [string]$pattern) { $excluded = $true break } } if ($excluded) { [void]$excludedReasons.Add([string]$reason) } else { [void]$activeReasons.Add([string]$reason) } } $isPending = $activeReasons.Count -gt 0 if ($isPending) { $status = [string]$Settings.PendingStatus $reasonText = @($activeReasons.ToArray()) -join ', ' $message = "A reboot is pending: $reasonText." $recommendation = 'Schedule a controlled restart, confirm service restoration, and verify that all reboot indicators clear afterward.' } elseif ($excludedReasons.Count -gt 0) { $status = 'Healthy' $message = "All detected reboot indicators ($(@($excludedReasons.ToArray()) -join ', ')) are excluded by configuration." $recommendation = '' } else { $status = 'Healthy' $message = 'No supported pending-reboot indicators were detected.' $recommendation = '' } $evidence = [ordered]@{ Pending = $isPending Reasons = @($activeReasons.ToArray()) ExcludedReasons = @($excludedReasons.ToArray()) } $warningThreshold = $null $criticalThreshold = $null if ([string]$Settings.PendingStatus -eq 'Critical') { $criticalThreshold = 'Any supported reboot indicator' } else { $warningThreshold = 'Any supported reboot indicator' } return New-InfraPulseResult -Status $status -CheckName 'PendingReboot' -Category 'Lifecycle' -ComputerName $Context.ComputerName -Target 'Operating system' -Message $message -ObservedValue $isPending -WarningThreshold $warningThreshold -CriticalThreshold $criticalThreshold -Recommendation $recommendation -Evidence $evidence -DurationMs $stopwatch.Elapsed.TotalMilliseconds } |