PowerShellを使用してWindowsサーバーの再起動が必要かどうかをテストする

ソフトウェアをインストールしたり、アップデートしたり、設定を変更したりすると、Windowsは再起動が必要になることがよくあります。いくつかのOSのタスクは、Windowsを再起動させる必要があります。再起動が必要な場合、Windowsはいくつかのレジストリ値を追加して表示します。このブログ投稿では、再起動が必要かどうかを確認する方法と、タスクを自動化するためのPowerShellスクリプトの作成方法について学びます。

Windowsの再起動が必要です

コンソール上にいるときに、以下のようなポップアップボックスや通知で再起動が必要であることが分かります。

その通知からWindowsを再起動して終了することができます。しかし、すぐに再起動できない場合はどうでしょうか?プロダクションサーバーにアップデートをインストールしたばかりで、そのサーバーを今すぐ再起動することができない場合はどうでしょうか?

Pending Windows reboot message

再起動は待たなければなりません。

時間が経ち、その間に再起動を完全に忘れてしまうこともあります!気付いたときには、多くのサーバーやワークステーションを再起動する必要がありますが、どれが必要なのでしょうか?

再起動が保留中であることを示すフラグはレジストリにあります。

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

Microsoft System Center Configuration Manager (SCCM) クライアントがインストールされている場合、WMIでこれらのメソッドも表示される場合があります。

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

再起動が必要かどうかを確認するための各方法を知ったら、レジストリの値を確認するためのさまざまな方法があります。regedit.exeを開き、手動で各レジストリキーをマウスでたどることができます。

Checking regedit manually

レジストリを手動で確認する方法もありますが、私たちは人間です。一つのレジストリパスをチェックし忘れるか、どのパスをチェックするかを忘れた場合にはどうでしょうか?より良い方法があります。スクリプトまたは関数を作成して、代わりにそれらを実行することができます。私の場合、PowerShellを使用することをお勧めします。

PowerShellスクリプトを使用することで、ドメイン内の1つまたはすべてのコンピュータをクエリしたり、手動でサーバー名を指定して再起動が必要かどうかを確認することができます。その後、再起動するかどうかの判断を下したり、後で再起動するためのリストを作成することができます。選択はあなた次第です。

私のPowerShellの方法を使用するには、サーバー上でPowerShellリモーティングが設定されていることを確認する必要があります。

保留中の再起動のテスト(簡単な方法)

これらのレジストリキーを確認し、PowerShellでこのようなツールを作成する方法を学びたくない場合は、簡単にしました。PowerShellコンソールを開き、Install-Script Test-PendingRebootと入力してください。 Install-Scriptは、PowerShell Galleryから私のPowerShellスクリプトをC:\Program Files\WindowsPowerShell\Scriptsにダウンロードします。その後、以下のようにスクリプトを実行します。

PS51> Test-PendingReboot.ps1 -ComputerName localhost

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

ComputerNameパラメータを使用して、任意の数のサーバーを指定できます。スクリプトはTrueまたはFalseを返し、サーバー名と共に表示されます。

このツールは、上記の表のすべてのレジストリキーをチェックします。

私が見逃した条件を追加したり、間違いを修正したりしたい場合は、自由にを発行して修正してください。

このようなツールを作成する方法を学びたい場合は、以下をお読みください!

Pending Reboot PowerShellツールの作成

まず、テストしたいすべてのコンピュータを定義する必要があります。これにはさまざまな方法がありますが、このデモでは、配列を使用して手動で定義します。

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

次に、それぞれのコンピュータを反復処理するためのループを作成します。

foreach ($computer in $ComputerName)

}

次に、PowerShellリモートを使用して、各レジストリキーと値の条件を単一のPSSession内でチェックすることをお勧めします。それぞれのサーバーに対してPSSessionを作成します。

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

PSSessionを作成したら、チェックを実行する必要があります。

同じコードを使用して多くの異なるチェックを実行する場合、例えば:

  • レジストリキーが存在するかどうかのテスト
  • レジストリ値が存在するかどうかのテスト
  • レジストリ値がnullでないことをテストする

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
        }
    }

その同じスクリプトブロック内で、ヘルパー関数を参照して各条件を定義します。

$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' }
        { 
            # 最初にキーが存在するかどうかをチェックするテストを追加しました。「ErrorAction ignore」を使用すると誤って$trueを返します
            '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' }
        {
            # 最初にキーが存在するかどうかをチェックするテストを追加しました。存在しない場合、各グループは$Nullを返します
            # これらのキーのいずれかまたは両方が存在しない場合の評価が必要かもしれません
            ( '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 } )
        }
        {
            # 最初にキーが存在するかどうかをチェックするテストを追加しました
            'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending' | Where-Object { 
                (Test-Path $_) -and (Get-ChildItem -Path $_) } | ForEach-Object { $true }
        }
    )

これで、foreachループを$servers foreachループの内部に作成することができます。各テストを読み込み、実行します。

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

コードを実行すると、以下のような出力が返されます:

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

foreachループがサーバーごとに1つのオブジェクトを返すようにすることで、この出力を作成できます。レジストリの値がいずれか存在する場合、そのサーバーは再起動が保留中であることを示します。これを知った上で、値のいずれも存在しない場合はTrueを返し、存在しない場合はFalseを返す必要があります。

これらすべてをスクリプトにまとめると、次のようになります(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
        }
    }

    # 上記のカスタム関数を利用していない各テストに "test-path" を追加しました
    # Get-ItemProperty や Get-ChildItem に存在しないキーパスが渡されると例外が発生するため、例外を投げました
    $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' }
        { 
            # キーが存在するかどうかを最初にチェックするテストを追加しました。"ErrorAction ignore" を使用すると誤って $true が返されます
            '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' }
        {
            # キーが存在するかどうかを最初にチェックするテストを追加しました。各グループが $Null を返すことになります
            # これらのキーのいずれかまたは両方が存在しない場合の評価方法を確認する必要があるかもしれません
            ( '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 } )
        }
        {
            # キーが存在するかどうかを最初にチェックするテストを追加しました
            '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
        }
    }
}

以下のように実行できます:

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

概要

Windows サーバー全体での保留中の再起動のテスト方法が簡単になりました。PowerShell を使用することで、多くの手間のかかる手順を1つのスクリプトにまとめることができます。このスクリプトを使用すると、複数のサーバーで一度に保留中の再起動を簡単にテストできます。

保留中の再起動をチェックするための他の指標があれば、教えてください。

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