Test ausstehender Neustart auf Windows-Servern mit PowerShell

Immer wenn Sie Software installieren, Updates durchführen oder Konfigurationsänderungen vornehmen, ist es üblich, dass Windows einen Neustart benötigt. Viele Betriebssystemaufgaben erfordern manchmal einen Neustart von Windows. Wenn ein Neustart ansteht, fügt Windows einige Registrierungswerte hinzu, um dies anzuzeigen. In diesem Blogbeitrag erfahren Sie, wie Sie einen ausstehenden Neustart überprüfen können und wie Sie ein PowerShell-Skript erstellen, um diese Aufgabe zu automatisieren.

Windows muss neu gestartet werden

Wenn Sie sich auf der Konsole befinden, können Sie einen ausstehenden Neustart anhand eines Popup-Fensters oder einer Benachrichtigung wie unten gezeigt erkennen.

Aus dieser Benachrichtigung heraus können Sie Windows neu starten und fertig. Aber was ist, wenn Sie eine Maschine nicht sofort neu starten können, wenn sie es muss? Was ist, wenn Sie gerade Updates auf einem Produktivserver installiert haben und dieser Server im Moment nicht neu gestartet werden kann?

Pending Windows reboot message

Der Neustart muss warten.

Die Zeit vergeht und bis dahin kann der Neustart vollständig vergessen sein! Wenn Sie es dann bemerken, müssen viele Server oder Arbeitsstationen neu gestartet werden, aber welche?

Ausstehende Neustartflags befinden sich in der Registrierung

A pending reboot is defined in many places. Scroll right to see the values and conditions. A Windows computer is pending a reboot if any of the conditions in this table are true.

Key Value Condition
HKLM:\SOFTWARE\Microsoft\Updates UpdateExeVolatile Value is anything other than 0
HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager PendingFileRenameOperations value exists
HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager PendingFileRenameOperations2 value exists
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired NA key exists
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending NA Any GUID subkeys exist
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting NA key exists
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce DVDRebootSignal value exists
HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending NA key exists
HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootInProgress NA key exists
HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending NA key exists
HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttempts NA key exists
HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon JoinDomain value exists
HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon AvoidSpnSet value exists
HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName ComputerName Value ComputerName in HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName is different

Wenn Sie den Microsoft System Center Configuration Manager (SCCM)-Client installiert haben, können Sie diese Methoden auch in der WMI sehen.

Namespace Class Property Value Product Notes
ROOT\ccm\ClientSDK CCM_ClientUtilities DetermineifRebootPending RebootPending SCCM ReturnValue needs to be 0 and this value is not null
ROOT\ccm\ClientSDK CCM_ClientUtilities DetermineifRebootPending IsHardRebootPending SCCM ReturnValue needs to be 0 and this value is not null

Sobald Sie jede Methode zur Überprüfung eines ausstehenden Neustarts kennen, gibt es viele verschiedene Möglichkeiten, Registrierungswerte zu überprüfen. Sie könnten regedit.exe öffnen und manuell jeden Registrierungsschlüssel durchgehen.

Checking regedit manually

Das manuelle Überprüfen über die Registrierung funktioniert, aber wir sind Menschen. Was ist, wenn Sie vergessen, einen Registrierungspfad zu überprüfen oder einfach vergessen, welche überprüft werden sollen? Es gibt eine viel bessere Möglichkeit, dies zu tun. Sie können ein Skript oder eine Funktion erstellen, um dies für Sie zu tun. In meinem Fall bevorzuge ich PowerShell, also werde ich das verwenden.

Indem Sie ein PowerShell-Skript verwenden, können Sie einen oder alle Computer in unserer Domäne abfragen oder manuell die Servernamen angeben, um zu sehen, ob ein Neustart ausstehend ist. Sie können dann entscheiden, ob Sie sie sofort neu starten oder eine Liste erstellen, um sie später neu zu starten. Die Wahl liegt bei Ihnen.

Um meine PowerShell-Methode zu verwenden, müssen Sie sicherstellen, dass PowerShell Remoting auf Ihren Servern eingerichtet und verfügbar ist.

Überprüfung auf ausstehenden Neustart (der einfache Weg)

Wenn Sie nicht lernen möchten, wie Sie diese Registrierungsschlüssel überprüfen und ein Tool wie dieses in PowerShell erstellen, habe ich es Ihnen einfach gemacht. Öffnen Sie einfach Ihre PowerShell-Konsole und geben Sie Install-Script Test-PendingReboot ein. Install-Script lädt mein PowerShell-Skript aus der PowerShell Gallery herunter nach C:\Program Files\WindowsPowerShell\Scripts. Führen Sie dann das Skript wie unten gezeigt aus.

PS51> Test-PendingReboot.ps1 -ComputerName localhost

ComputerName IsPendingReboot
------------ ---------------
localhost              False

Sie können so viele Server wie gewünscht über den ComputerName-Parameter angeben. Das Skript gibt True oder False zusammen mit dem Servernamen zurück.

Dieses Tool überprüft alle in der obigen Tabelle aufgeführten Registrierungsschlüssel für Sie.

Wenn Sie Bedingungen hinzufügen möchten, die ich übersehen habe oder Fehler korrigieren möchten, können Sie gerne einen Pull-Request auf GitHub erstellen, um es zu beheben.

Wenn Sie lernen möchten, wie Sie ein solches Tool erstellen können, lesen Sie weiter!

Erstellen eines Pending Reboot PowerShell Tools

Zunächst müssen Sie alle Computer definieren, auf denen Sie einen Neustart testen möchten. Es gibt viele verschiedene Möglichkeiten, dies zu tun, aber für diese Demonstration werde ich sie manuell über ein Array definieren.

$servers = 'SRV1','SRV2','SRV3'

Erstellen Sie nun eine foreach-Schleife, um über jeden von ihnen zu iterieren.

foreach ($computer in $ComputerName)

}

Als nächstes empfehle ich die Verwendung von PowerShell Remoting und das Überprüfen jeder Registrierungsschlüssel- und Wertbedingung innerhalb einer einzelnen PSSession. Erstellen Sie eine PSSession für jeden Server.

foreach ($computer in $ComputerName)
    $session = New-PSSession
}

Sobald Sie eine PSSession erstellt haben, müssen Sie dann die Überprüfungen durchführen.

Da Sie viele verschiedene Überprüfungen mit demselben Code durchführen werden, wie zum Beispiel:

  • Überprüfen, ob ein Registrierungsschlüssel vorhanden ist
  • Überprüfen, ob ein Registrierungswert vorhanden ist
  • Überprüfen, ob ein Registrierungswert nicht null ist

I recommend creating simple functions for each of these checks. This allows you to call a function instead of duplicating code. The Test-PendingReboot script builds all of these helper functions into a single scriptblock as shown below.

function Test-RegistryKey {
        [OutputType('bool')]
        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$Key
        )
    
        $ErrorActionPreference = 'Stop'

        if (Get-Item -Path $Key -ErrorAction Ignore) {
            $true
        }
    }

    function Test-RegistryValue {
        [OutputType('bool')]
        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$Key,

            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$Value
        )
    
        $ErrorActionPreference = 'Stop'

        if (Get-ItemProperty -Path $Key -Name $Value -ErrorAction Ignore) {
            $true
        }
    }

    function Test-RegistryValueNotNull {
        [OutputType('bool')]
        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$Key,

            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$Value
        )
    
        $ErrorActionPreference = 'Stop'

        if (($regVal = Get-ItemProperty -Path $Key -Name $Value -ErrorAction Ignore) -and $regVal.($Value)) {
            $true
        }
    }

Definieren Sie in demselben Scriptblock jede Bedingung, indem Sie auf die gerade erstellten Hilfsfunktionen verweisen.

$tests = @(
        { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending' }
        { Test-RegistryKey -Key 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootInProgress' }
        { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' }
        { Test-RegistryKey -Key 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending' }
        { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting' }
        { Test-RegistryValueNotNull -Key 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Value 'PendingFileRenameOperations' }
        { Test-RegistryValueNotNull -Key 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Value 'PendingFileRenameOperations2' }
        { 
            # Hinzugefügter Test, um zuerst zu überprüfen, ob der Schlüssel existiert; die Verwendung von "ErrorAction ignore" gibt fälschlicherweise $true zurück
            'HKLM:\SOFTWARE\Microsoft\Updates' | Where-Object { test-path $_ -PathType Container } | ForEach-Object {            
                (Get-ItemProperty -Path $_ -Name 'UpdateExeVolatile' -ErrorAction Ignore | Select-Object -ExpandProperty UpdateExeVolatile) -ne 0 
            }
        }
        { Test-RegistryValue -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' -Value 'DVDRebootSignal' }
        { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttemps' }
        { Test-RegistryValue -Key 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon' -Value 'JoinDomain' }
        { Test-RegistryValue -Key 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon' -Value 'AvoidSpnSet' }
        {
            # Hinzugefügter Test, um zuerst zu überprüfen, ob die Schlüssel existieren; falls nicht, gibt jede Gruppe $Null zurück
            # Möglicherweise müssen Sie bewerten, was es bedeutet, wenn einer oder beide dieser Schlüssel nicht existieren
            ( 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName' | Where-Object { test-path $_ } | %{ (Get-ItemProperty -Path $_ ).ComputerName } ) -ne 
            ( 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName' | Where-Object { Test-Path $_ } | %{ (Get-ItemProperty -Path $_ ).ComputerName } )
        }
        {
            # Hinzugefügter Test, um zuerst zu überprüfen, ob der Schlüssel existiert
            'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending' | Where-Object { 
                (Test-Path $_) -and (Get-ChildItem -Path $_) } | ForEach-Object { $true }
        }
    )

Sie können jetzt eine foreach-Schleife innerhalb Ihrer $servers foreach-Schleife erstellen, die jeden Test ausführt.

foreach ($test in $tests) {
	if (& $test) {
		$true
		break
	}
}

Wenn Sie den Code ausführen, gibt das Skript eine Ausgabe wie diese zurück:

ComputerName IsPendingReboot
------------ ---------------
Server1              False
Server2              True

Sie können diese Ausgabe erstellen, indem Sie sicherstellen, dass die foreach-Schleife pro Server ein einzelnes Objekt zurückgibt. Sie sollten wissen, dass wenn irgendeiner der Registrierungswerte existiert, dann der Server auf einen Neustart wartet. Aufgrund dessen müssen Sie True zurückgeben, wenn einer der Werte existiert, und False, wenn keiner von ihnen existiert.

Fassen Sie dies alles in ein Skript zusammen, und es sollte wie folgt aussehen (mit einigen geringfügigen Ergänzungen wie Credential).

[CmdletBinding()]
param(
    [Parameter(Mandatory)]
    [ValidateNotNullOrEmpty()]
    [string[]]$ComputerName,
	
    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [pscredential]$Credential
)

$ErrorActionPreference = 'Stop'

$scriptBlock = {

    $VerbosePreference = $using:VerbosePreference
    function Test-RegistryKey {
        [OutputType('bool')]
        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$Key
        )
    
        $ErrorActionPreference = 'Stop'

        if (Get-Item -Path $Key -ErrorAction Ignore) {
            $true
        }
    }

    function Test-RegistryValue {
        [OutputType('bool')]
        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$Key,

            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$Value
        )
    
        $ErrorActionPreference = 'Stop'

        if (Get-ItemProperty -Path $Key -Name $Value -ErrorAction Ignore) {
            $true
        }
    }

    function Test-RegistryValueNotNull {
        [OutputType('bool')]
        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$Key,

            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$Value
        )
    
        $ErrorActionPreference = 'Stop'

        if (($regVal = Get-ItemProperty -Path $Key -Name $Value -ErrorAction Ignore) -and $regVal.($Value)) {
            $true
        }
    }

    # Zu jedem Test wurde "test-path" hinzugefügt, der keine benutzerdefinierte Funktion von oben verwendete, da
    # eine Ausnahme ausgelöst wird, wenn Get-ItemProperty oder Get-ChildItem einen nicht vorhandenen Schlüsselpfad übergeben wird
    $tests = @(
        { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending' }
        { Test-RegistryKey -Key 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootInProgress' }
        { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' }
        { Test-RegistryKey -Key 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending' }
        { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting' }
        { Test-RegistryValueNotNull -Key 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Value 'PendingFileRenameOperations' }
        { Test-RegistryValueNotNull -Key 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Value 'PendingFileRenameOperations2' }
        { 
            # Test hinzugefügt, um zuerst zu überprüfen, ob der Schlüssel existiert, die Verwendung von "ErrorAction ignore" gibt fälschlicherweise $true zurück
            'HKLM:\SOFTWARE\Microsoft\Updates' | Where-Object { test-path $_ -PathType Container } | ForEach-Object {            
                (Get-ItemProperty -Path $_ -Name 'UpdateExeVolatile' | Select-Object -ExpandProperty UpdateExeVolatile) -ne 0 
            }
        }
        { Test-RegistryValue -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' -Value 'DVDRebootSignal' }
        { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttemps' }
        { Test-RegistryValue -Key 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon' -Value 'JoinDomain' }
        { Test-RegistryValue -Key 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon' -Value 'AvoidSpnSet' }
        {
            # Test hinzugefügt, um zuerst zu überprüfen, ob die Schlüssel existieren, falls nicht gibt jede Gruppe $Null zurück
            # Möglicherweise müssen Sie bewerten, was es bedeutet, wenn einer oder beide dieser Schlüssel nicht existieren
            ( 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName' | Where-Object { test-path $_ } | %{ (Get-ItemProperty -Path $_ ).ComputerName } ) -ne 
            ( 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName' | Where-Object { Test-Path $_ } | %{ (Get-ItemProperty -Path $_ ).ComputerName } )
        }
        {
            # Test hinzugefügt, um zuerst zu überprüfen, ob der Schlüssel existiert
            'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending' | Where-Object { 
                (Test-Path $_) -and (Get-ChildItem -Path $_) } | ForEach-Object { $true }
        }
    )

    foreach ($test in $tests) {
        Write-Verbose "Running scriptblock: [$($test.ToString())]"
        if (& $test) {
            $true
            break
        }
    }
}

foreach ($computer in $ComputerName) {
    try {
        $connParams = @{
            'ComputerName' = $computer
        }
        if ($PSBoundParameters.ContainsKey('Credential')) {
            $connParams.Credential = $Credential
        }

        $output = @{
            ComputerName    = $computer
            IsPendingReboot = $false
        }

        $psRemotingSession = New-PSSession @connParams
        
        if (-not ($output.IsPendingReboot = Invoke-Command -Session $psRemotingSession -ScriptBlock $scriptBlock)) {
            $output.IsPendingReboot = $false
        }
        [pscustomobject]$output
    } catch {
        Write-Error -Message $_.Exception.Message
    } finally {
        if (Get-Variable -Name 'psRemotingSession' -ErrorAction Ignore) {
            $psRemotingSession | Remove-PSSession
        }
    }
}

Sie können es jetzt so ausführen:

PS51> .\Test-PendingReboot.ps1 -Server SRV1,SRV2,SRV3,etc

Zusammenfassung

Sie sollten jetzt eine schnelle Möglichkeit haben, einen ausstehenden Neustart auf Windows-Servern zu testen. Sie können sehen, dass Sie mit PowerShell viele mühsame Schritte in ein Skript zusammenfassen können. Dieses Skript ermöglicht es Ihnen, schnell auf einem Mal nach einem ausstehenden Neustart auf vielen Servern zu suchen.

Wenn Sie weitere Hinweise kennen, um nach einem ausstehenden Neustart zu suchen, lassen Sie es mich bitte wissen.

Source:
https://adamtheautomator.com/pending-reboot-registry/