0

The Windows Servers in our environment (2008 R2 and 2012 R2) install updates automatically once a month, but don't restart automatically (yet), because the WSUS server should be the last server to restart and not all domain controllers should restart at the same time. How can I setup a system where each server reports to a server when it finished installing all updates? I'd like to write a script that gets the update status of a server and then decides if the server is allowed to reboot.

Also, I miss (WSUS/Windows) update notifications in general, for example if an update fails, I'd like to get a notification and not having to check each server manually.

0

On TechNet a lot of script exist for that, to query for the reboot required. There is one from a MSFT's employee. You can add the Restart-Computer cmdlet in the script as needed to fit your need.

See that blog post, that explain the script. Determine Pending Reboot Status—PowerShell Style! Part 1 Determine Pending Reboot Status—PowerShell Style! Part 2

Link to the script. Get-PendingReboot - Query Computer(s) For Pending Reboot State

Function Get-PendingReboot
{
<#
.SYNOPSIS
    Gets the pending reboot status on a local or remote computer.

.DESCRIPTION
    This function will query the registry on a local or remote computer and determine if the
    system is pending a reboot, from Microsoft updates, Configuration Manager Client SDK, Pending Computer 
    Rename, Domain Join or Pending File Rename Operations. For Windows 2008+ the function will query the 
    CBS registry key as another factor in determining pending reboot state.  "PendingFileRenameOperations" 
    and "Auto Update\RebootRequired" are observed as being consistant across Windows Server 2003 & 2008.

    CBServicing = Component Based Servicing (Windows 2008+)
    WindowsUpdate = Windows Update / Auto Update (Windows 2003+)
    CCMClientSDK = SCCM 2012 Clients only (DetermineIfRebootPending method) otherwise $null value
    PendComputerRename = Detects either a computer rename or domain join operation (Windows 2003+)
    PendFileRename = PendingFileRenameOperations (Windows 2003+)
    PendFileRenVal = PendingFilerenameOperations registry value; used to filter if need be, some Anti-
                     Virus leverage this key for def/dat removal, giving a false positive PendingReboot

.PARAMETER ComputerName
    A single Computer or an array of computer names.  The default is localhost ($env:COMPUTERNAME).

.PARAMETER ErrorLog
    A single path to send error data to a log file.

.EXAMPLE
    PS C:\> Get-PendingReboot -ComputerName (Get-Content C:\ServerList.txt) | Format-Table -AutoSize

    Computer CBServicing WindowsUpdate CCMClientSDK PendFileRename PendFileRenVal RebootPending
    -------- ----------- ------------- ------------ -------------- -------------- -------------
    DC01           False         False                       False                        False
    DC02           False         False                       False                        False
    FS01           False         False                       False                        False

    This example will capture the contents of C:\ServerList.txt and query the pending reboot
    information from the systems contained in the file and display the output in a table. The
    null values are by design, since these systems do not have the SCCM 2012 client installed,
    nor was the PendingFileRenameOperations value populated.

.EXAMPLE
    PS C:\> Get-PendingReboot

    Computer           : WKS01
    CBServicing        : False
    WindowsUpdate      : True
    CCMClient          : False
    PendComputerRename : False
    PendFileRename     : False
    PendFileRenVal     : 
    RebootPending      : True

    This example will query the local machine for pending reboot information.

.EXAMPLE
    PS C:\> $Servers = Get-Content C:\Servers.txt
    PS C:\> Get-PendingReboot -Computer $Servers | Export-Csv C:\PendingRebootReport.csv -NoTypeInformation

    This example will create a report that contains pending reboot information.

.LINK
    Component-Based Servicing:
    http://technet.microsoft.com/en-us/library/cc756291(v=WS.10).aspx

    PendingFileRename/Auto Update:
    http://support.microsoft.com/kb/2723674
    http://technet.microsoft.com/en-us/library/cc960241.aspx
    http://blogs.msdn.com/b/hansr/archive/2006/02/17/patchreboot.aspx

    SCCM 2012/CCM_ClientSDK:
    http://msdn.microsoft.com/en-us/library/jj902723.aspx

.NOTES
    Author:  Brian Wilhite
    Email:   bcwilhite (at) live.com
    Date:    29AUG2012
    PSVer:   2.0/3.0/4.0/5.0
    Updated: 27JUL2015
    UpdNote: Added Domain Join detection to PendComputerRename, does not detect Workgroup Join/Change
             Fixed Bug where a computer rename was not detected in 2008 R2 and above if a domain join occurred at the same time.
             Fixed Bug where the CBServicing wasn't detected on Windows 10 and/or Windows Server Technical Preview (2016)
             Added CCMClient property - Used with SCCM 2012 Clients only
             Added ValueFromPipelineByPropertyName=$true to the ComputerName Parameter
             Removed $Data variable from the PSObject - it is not needed
             Bug with the way CCMClientSDK returned null value if it was false
             Removed unneeded variables
             Added PendFileRenVal - Contents of the PendingFileRenameOperations Reg Entry
             Removed .Net Registry connection, replaced with WMI StdRegProv
             Added ComputerPendingRename
#>

[CmdletBinding()]
param(
    [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
    [Alias("CN","Computer")]
    [String[]]$ComputerName="$env:COMPUTERNAME",
    [String]$ErrorLog
    )

Begin {  }## End Begin Script Block
Process {
  Foreach ($Computer in $ComputerName) {
    Try {
        ## Setting pending values to false to cut down on the number of else statements
        $CompPendRen,$PendFileRename,$Pending,$SCCM = $false,$false,$false,$false

        ## Setting CBSRebootPend to null since not all versions of Windows has this value
        $CBSRebootPend = $null

        ## Querying WMI for build version
        $WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer -ErrorAction Stop

        ## Making registry connection to the local/remote computer
        $HKLM = [UInt32] "0x80000002"
        $WMI_Reg = [WMIClass] "\\$Computer\root\default:StdRegProv"

        ## If Vista/2008 & Above query the CBS Reg Key
        If ([Int32]$WMI_OS.BuildNumber -ge 6001) {
            $RegSubKeysCBS = $WMI_Reg.EnumKey($HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\")
            $CBSRebootPend = $RegSubKeysCBS.sNames -contains "RebootPending"        
        }

        ## Query WUAU from the registry
        $RegWUAURebootReq = $WMI_Reg.EnumKey($HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\")
        $WUAURebootReq = $RegWUAURebootReq.sNames -contains "RebootRequired"

        ## Query PendingFileRenameOperations from the registry
        $RegSubKeySM = $WMI_Reg.GetMultiStringValue($HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\","PendingFileRenameOperations")
        $RegValuePFRO = $RegSubKeySM.sValue

        ## Query JoinDomain key from the registry - These keys are present if pending a reboot from a domain join operation
        $Netlogon = $WMI_Reg.EnumKey($HKLM,"SYSTEM\CurrentControlSet\Services\Netlogon").sNames
        $PendDomJoin = ($Netlogon -contains 'JoinDomain') -or ($Netlogon -contains 'AvoidSpnSet')

        ## Query ComputerName and ActiveComputerName from the registry
        $ActCompNm = $WMI_Reg.GetStringValue($HKLM,"SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\","ComputerName")            
        $CompNm = $WMI_Reg.GetStringValue($HKLM,"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\","ComputerName")

        If (($ActCompNm -ne $CompNm) -or $PendDomJoin) {
            $CompPendRen = $true
        }

        ## If PendingFileRenameOperations has a value set $RegValuePFRO variable to $true
        If ($RegValuePFRO) {
            $PendFileRename = $true
        }

        ## Determine SCCM 2012 Client Reboot Pending Status
        ## To avoid nested 'if' statements and unneeded WMI calls to determine if the CCM_ClientUtilities class exist, setting EA = 0
        $CCMClientSDK = $null
        $CCMSplat = @{
            NameSpace='ROOT\ccm\ClientSDK'
            Class='CCM_ClientUtilities'
            Name='DetermineIfRebootPending'
            ComputerName=$Computer
            ErrorAction='Stop'
        }
        ## Try CCMClientSDK
        Try {
            $CCMClientSDK = Invoke-WmiMethod @CCMSplat
        } Catch [System.UnauthorizedAccessException] {
            $CcmStatus = Get-Service -Name CcmExec -ComputerName $Computer -ErrorAction SilentlyContinue
            If ($CcmStatus.Status -ne 'Running') {
                Write-Warning "$Computer`: Error - CcmExec service is not running."
                $CCMClientSDK = $null
            }
        } Catch {
            $CCMClientSDK = $null
        }

        If ($CCMClientSDK) {
            If ($CCMClientSDK.ReturnValue -ne 0) {
                Write-Warning "Error: DetermineIfRebootPending returned error code $($CCMClientSDK.ReturnValue)"          
            }
            If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending) {
                $SCCM = $true
            }
        }

        Else {
            $SCCM = $null
        }

        ## Creating Custom PSObject and Select-Object Splat
        $SelectSplat = @{
            Property=(
                'Computer',
                'CBServicing',
                'WindowsUpdate',
                'CCMClientSDK',
                'PendComputerRename',
                'PendFileRename',
                'PendFileRenVal',
                'RebootPending'
            )}
        New-Object -TypeName PSObject -Property @{
            Computer=$WMI_OS.CSName
            CBServicing=$CBSRebootPend
            WindowsUpdate=$WUAURebootReq
            CCMClientSDK=$SCCM
            PendComputerRename=$CompPendRen
            PendFileRename=$PendFileRename
            PendFileRenVal=$RegValuePFRO
            RebootPending=($CompPendRen -or $CBSRebootPend -or $WUAURebootReq -or $SCCM -or $PendFileRename)
        } | Select-Object @SelectSplat

    } Catch {
        Write-Warning "$Computer`: $_"
        ## If $ErrorLog, log the file to a user specified location/path
        If ($ErrorLog) {
            Out-File -InputObject "$Computer`,$_" -FilePath $ErrorLog -Append
        }               
    }           
  }## End Foreach ($Computer in $ComputerName)          
}## End Process

End {  }## End End

}## End Function Get-PendingReboot
  • Interesting script, thanks. However I'm wondering if this is a reliable way of checking if the updates are installed. And since this script queries the server instead of the server reporting actively when finished, I'm wondering if there isn't any other way. – Peter Nov 13 '15 at 8:54
  • @Peter If we forget the script part, the only viable workaround I think off then is to create computer group for different deployment time in WSUS. (with approving interval). That way you restart only those computer in the group. DC in group #1, DC2 in group #2 with some server and WSUS in group #3 in example. – yagmoth555 Nov 13 '15 at 15:52
  • PowerShell and Windows Update aren't friends. There is extremely limited support for it and they've even gone out of their way to block updates being installed through a remote session. Periodically querying if a restart is required is going to be your best bet. – Deadly-Bagel Nov 17 '15 at 15:54
  • @yagmoth555 What do you mean with deployment time and approving interval? Something to set in WSUS or GPO? If possible, I'd like to install the updates on all servers the same day. – Peter Nov 18 '15 at 15:23
  • @Deadly-Bagel I'm sad to hear that. The GPO options provided by Microsoft are too limited for our needs (for example only options to install every day or once a week, not once a month). We're currently looking into different ways to solve this with Powershell, but all seems too complicated. – Peter Nov 18 '15 at 15:29
0

if you are into scripting, you can have a look at the command line tool WuInstall

In the simplest case you can do

WuInstall /install

and parse the output if a reboot is required or not - if yes, you can also use

WuInstall /reboot

to reboot if you like to

Running WuInstall on remote machines works via psexec, so basically you call the /install on every machine from a centralized script remotely, parse the output and so you find out which machine needs to be rebooted and take action accordingly. Of course, you can also do more refined update searches or get the output also in an XML to process it further, just check out documentation on http://www.wuinstall.com

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.