اختبار في انتظار إعادة التشغيل على خوادم Windows باستخدام PowerShell

كلما قمت بتثبيت البرمجيات أو التحديثات أو قمت بإجراء تغييرات في الإعدادات، فإنه من الشائع أن يحتاج نظام التشغيل Windows إلى إعادة التشغيل. بعض المهام في النظام قد تجبر 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، يمكنك الاستعلام عن جهاز كمبيوتر واحد أو جميع أجهزة الكمبيوتر في نطاقنا أو تقديم أسماء الخوادم يدويًا لمعرفة ما إذا كانت قيد الإعادة التشغيل. يمكنك بعد ذلك اتخاذ قرار بإعادة التشغيل في ذلك الوقت أو إعادة تشغيلها لاحقًا. الاختيار لك.

لاستخدام الطريقة الخاصة بي في PowerShell، ستحتاج إلى التأكد من تكوين التحكم عن بُعد في PowerShell وأنه متاح على خوادمك.

اختبار وجود إعادة تشغيل معلقة (الطريقة السهلة)

إذا كنت لا ترغب في تعلم كيفية التحقق من هذه المفاتيح في التسجيل وإنشاء أداة مثل هذه في PowerShell، قد قمت بتسهيل الأمور بالنسبة لك. قم بفتح نافذة التحكم في PowerShell واكتب ببساطة Install-Script Test-PendingReboot. سيقوم Install-Script بتنزيل سيناريو PowerShell الخاص بي من PowerShell Gallery إلى C:\Program Files\WindowsPowerShell\Scripts. ثم قم بتشغيل السيناريو كما هو موضح أدناه.

PS51> Test-PendingReboot.ps1 -ComputerName localhost

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

يمكنك تقديم العديد من الخوادم كما تريد عبر معلمة ComputerName. سيقوم السيناريو بإرجاع True أو False إلى جانب اسم الخادم.

تقوم هذه الأداة بالتحقق من جميع مفاتيح التسجيل في الجدول أعلاه بالنسبة لك.

إذا كنت ترغب في إضافة شروط قد أفتقدتها أو تصحيح أي أخطاء قد قمت بها، فلا تتردد في تقديم طلب pull على موقع GitHub لتصحيحها.

إذا كنت ترغب في معرفة كيفية إنشاء أداة مثل هذه، استمر في القراءة!

إنشاء أداة PowerShell لفحص إعادة التشغيل المعلقة

أولاً، ستحتاج إلى تعريف جميع الحواسيب التي ترغب في اختبار إعادة التشغيل عليها. هناك العديد من الطرق المختلفة للقيام بذلك، ولكن لهذا العرض التوضيحي، سأقوم بتعريفها يدويًا عبر مصفوفة.

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

الآن، قم بإنشاء حلقة تكرارية foreach للتكرار على كل منها.

foreach ($computer in $ComputerName)

}

بعد ذلك، أوصي باستخدام PowerShell Remoting وفحص كل مفتاح سجل وشرط قيمة داخل PSSession واحد. قم بإنشاء PSSession لكل خادم.

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

عندما تقوم بإنشاء PSSession، ستحتاج بعد ذلك إلى تشغيل الفحوصات.

نظرًا لأنك ستقوم بتشغيل العديد من الفحوصات المختلفة باستخدام نفس الشيفرة مثل:

  • اختبار ما إذا كان مفتاح السجل موجودًا
  • اختبار ما إذا كانت قيمة السجل موجودة
  • اختبار ما إذا كانت قيمة السجل ليست فارغة

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" سيعيد صحيحًا بشكل غير صحيح
            '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 ترجع كائنًا واحدًا لكل خادم. يجب عليك معرفة أنه إذا كانت أي من قيم التسجيل موجودة، فإن الخادم في انتظار إعادة التشغيل. بعد معرفة ذلك، يجب عليك بعد ذلك إرجاع 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، يمكنك تجميع العديد من الخطوات المملة في نص واحد. يتيح لك هذا النص إمكانية اختبار إعادة التشغيل المعلقة عبر العديد من الخوادم مرة واحدة.

إذا كنت تعرف عن أي مؤشرات أخرى للتحقق من إعادة التشغيل المعلقة، يرجى إعلامي.

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