Update Windows with Restricted Internet Access

This script is an extract of a longer version with the features of simultaneous executions and dealing with computers that have Internet connectivity – click here

# updateWindowsNoInternet.ps1

# Specify computer name(s)
$computer='testWindows'

# Set credentials - to be injected by Jenkins
$adminUsername='domain\admin'
$plaintextPassword='PASSWORD'
$encryptedPassword=ConvertTo-securestring $plaintextPassword -AsPlainText -Force
$adminCredential=New-Object -TypeName System.Management.Automation.PSCredential -Args $adminUsername,$encryptedPassword

$targetHasInternet=invoke-command -ComputerName $computer -Credential $adminCredential -scriptblock{test-connection 8.8.8.8 -Count 1 -Quiet}
if(!$targetHasInternet){
    write-host "$computer`: no internet connectivity detected"
    function updateLocalWindowsUsingComObjects($autoReboot=$false,$logfile='c:\WindowsUpdate.log'){
        # in case contents of function is called from scheduled tasks without any default argument values
        $logFile=if($logFile){$logFile}else{'c:\WindowsUpdate.log'}
        if(!(test-path $logfile)){
            new-item $logfile -type file -force
        }
        $status="$(get-date) => $env:computername patching STARTED"
        write-host $status
        Add-Content -Path $logfile -Value $status
        $updateSession=New-Object -Com Microsoft.Update.Session
        $updateSearcher=$updateSession.CreateUpdateSearcher()
        $searchCriteria="IsInstalled=0 AND Type='Software'" # "IsHidden=0 AND IsInstalled=0 AND Type='Software'"
        $availableUpdates=$updateSearcher.Search($searchCriteria)
        $availableUpdatesCount=$availableUpdates.Updates.count
        if($availableUpdatesCount -eq 0){
            $status="$(get-date) => $env:computername has 0 available updates. Patching FINISHED"
            write-host $status
            Add-Content -Path $logfile -Value $status
            return $true
        }else{
            $status="$(get-date) => $availableUpdatesCount available updates detected"
            write-host $status
            Add-Content -Path $logfile -Value $status
            $updatesToDownload=New-Object -Com Microsoft.Update.UpdateColl
            For ($i=0; $i -lt $availableUpdates.Updates.Count; $i++){
                $item = $availableUpdates.Updates.Item($i)
                $Null = $updatesToDownload.Add($item)
            }                        
            $updateSession=New-Object -Com Microsoft.Update.Session
            $downloader=$updateSession.CreateUpdateDownloader()
            $downloader.Updates=$updatesToDownload
            try{
                $null=$downloader.Download()
            }catch{
                write-warning $_
            }
            $downloadedUpdates=New-Object -Com Microsoft.Update.UpdateColl
            For ($i=0; $i -lt $availableUpdates.Updates.Count; $i++){
                $item = $availableUpdates.Updates.Item($i)
                If ($item.IsDownloaded) {
                    $null=$downloadedUpdates.Add($item)        
                }
            }
            $downloadedCount=$downloadedUpdates.count                        
            if($downloadedCount -eq 0){
                $status="$(get-date) => Installation cannot proceed 0 successful downloads."
                write-host $status
                Add-Content -Path $logfile -Value $status
                return $false
            }else{
                $status="$(get-date) => $downloadedCount of $availableUpdatesCount updates downloaded successfully"
                write-host $status
                Add-Content -Path $logfile -Value $status
                $installCount=0
                foreach($update in $downloadedUpdates){
                    $thisUpdate = New-object -com "Microsoft.Update.UpdateColl"
                    $thisCount=$installCount++ +1
                    $null=$thisUpdate.Add($update)
                    $installer = $updateSession.CreateUpdateInstaller()
                    $installer.Updates = $thisUpdate
                    $installResult = $installer.Install()
                    $translatedCode=switch ($installResult.ResultCode){
                        0  {'not started'}
                        1  {'in progress'}
                        2  {'succeeded'}
                        3  {'succeeded with errors'}
                        4  {'failed'}
                        5  {'aborted'}
                    }
                    $status="$(get-date) => $thisCount of $downloadedCount $translatedCode`: $($update.Title)"
                    write-host $status
                    Add-Content -Path $logfile -Value $status                                
                }
            }
        }
        $pendingReboot=.{
            if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -EA Ignore) { return $true }
            if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -EA Ignore) { return $true }
            if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -EA Ignore) { return $true }
            try { 
                $util = [wmiclass]"\\.\root\ccm\clientsdk:CCM_ClientUtilities"
                $status = $util.DetermineIfRebootPending()
                if (($null -ne $status) -and $status.RebootPending) {
                    return $true
                }
            }catch{}                            
            return $false
        }
        If($pendingReboot){
            if($autoReboot){
                $status="$(get-date) => $env:computername REBOOTED"
                write-host $status
                Add-Content -Path $logfile -Value $status
                (Get-WMIObject -Class Win32_OperatingSystem).Reboot()
            }else{
                $status="$(get-date) => $env:computername required a REBOOT"
                write-host $status
                Add-Content -Path $logfile -Value $status
            }
        }else{
            $status="$(get-date) => $env:computername patching FINISHED"
            write-host $status
            Add-Content -Path $logfile -Value $status
        }
    }
    function invokeScheduledTaskCallPowerShellFunction{
        param(
            [string[]]$computernames,
            [string]$scriptblock,
            [string]$taskName,
            [string]$description,
            [string]$repeatIntervalMinutes,
            [System.Management.Automation.PSCredential]$credential
            )
        $username=$credential.Username;
        $plaintextPassword=$credential.GetNetworkCredential().password;                    
        if ($credential){
            foreach ($computer in $computernames){
                write-host "Adding task on $computer..."
                $thisSession=if($credential){
                        try{
                            New-PSSession -ComputerName $computer -Credential $credential -ea Stop
                        }catch{
                            New-PSSession -ComputerName $computer -Credential $credential -SessionOption $(new-pssessionoption -IncludePortInSPN)
                        }
                    }else{
                        try{
                            New-PSSession -ComputerName $computer -ea Stop
                        }catch{
                            New-PSSession -ComputerName $computer -SessionOption $(new-pssessionoption -IncludePortInSPN)
                        }
                    }
                if ($thisSession.state -match "Opened"){
                    $scheduledTaskAdded=Invoke-Command -session $thisSession -ScriptBlock{
                        param($scriptblock,$taskName,$description,$repeatMinutes,$user,$password)          
                        $windowsVersion=[Environment]::OSVersion.Version
                        $windowsUpdateScript='C:\Scripts\WindowsUpdates.ps1'                               
                        if(!(test-path $windowsUpdateScript)){
                            $null=new-item $windowsUpdateScript -type File -force
                            $null=Unblock-File -Path $windowsUpdateScript
                        }
                        $null=Set-Content -Path $windowsUpdateScript -Value $scriptblock
                        if($windowsVersion -ge [version]'6.2'){
                            $username=if($user -notmatch '\\'){"$env:USERDOMAIN`\$user"}else{$user}
                            #$encryptedPassword=ConvertTo-SecureString $password -AsPlainText -Force
                            #$adminCredential=New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username,$encryptedPassword;
                            # Unrestrict this Domain Administrator from security prompts
                            Set-Executionpolicy -Scope CurrentUser -ExecutionPolicy UnRestricted -Force
                            $settingsCommand = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew -ExecutionTimeLimit 0
                            $callPowerShell = New-ScheduledTaskAction -Execute "Powershell.exe" -Argument "-ExecutionPolicy Bypass $windowsUpdateScript"
                            $runNow=$false
                            $taskTrigger = if($repeatMinutes -gt 0){ 
                                    New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes $repeatMinutes)
                                }else{
                                    $runNow=$true;
                                    New-ScheduledTaskTrigger -Once -At (get-date).AddSeconds(-1)
                                }
                            # Unregister the Scheduled task if it already exists
                            Get-ScheduledTask $taskName -ErrorAction SilentlyContinue | Unregister-ScheduledTask -Confirm:$false;                
                            # Create new scheduled task
                            $null=Register-ScheduledTask -Action $callPowerShell -Trigger $taskTrigger `
                                -TaskName $taskName -Description $description `
                                -User $username -Password $password `
                                -Settings $settingsCommand -RunLevel Highest;
                            if($runNow){
                                Start-ScheduledTask -TaskName $taskname
                                $timeout = 60 ##  seconds
                                $timer =  [Diagnostics.Stopwatch]::StartNew()
                                while (((Get-ScheduledTask -TaskName $taskname).State -ne 'Running') -and  ($timer.Elapsed.TotalSeconds -lt $timeout)) {    
                                    Write-Verbose -Message "Waiting on scheduled task..."
                                    Start-Sleep -Seconds 2   
                                    }                                            
                                Write-host "$taskname has taken $([math]::round(($timer.Elapsed.TotalSeconds),2)) seconds to initiate"
                                $timer.Stop()
                                start-sleep -seconds 5                                
                            }
                            return $true
                        }else{
                            write-host "$env:computername is too old. Just turn it off."
                            return $false
                        }
                    } -ArgumentList $scriptblock,$taskName,$description,$repeatIntervalMinutes,$username,$plainTextPassword
                    Remove-PSSession $thisSession
                    return $scheduledTaskAdded
                }
            }        
        }else{
            write-host "Please run this program with a valid Administrator account."
            return $false
        }
    }
    $taskName='windowsUpdate'
    $taskDescription='Perform Windows Updates'
    $taskAdded=invokeScheduledTaskCallPowerShellFunction `
        -computernames $computer `
        -scriptblock $function:updateLocalWindowsUsingComObjects `
        -taskName $taskName `
        -description $taskDescription `
        -repeatIntervalMinutes 0 `
        -credential $adminCredential                
    if($taskAdded){
        $updatesLog="\\$computer\c$\WindowsUpdate.log"
        $localUpdatesLog='c:\WindowsUpdate.log'   
        $logContent=$previousLine=''
        $updatesCompleted=$false
        write-host "Checking $updatesLog for Windows Update statuses..."
        while(!$updatesCompleted){
            # Testing variance in results of local vs invoke-command
            # $computer='testWindows'
            # $updatesLog="\\$computer\c$\windows\system32\drivers\etc\hosts"
            # $localUpdatesLog='c:\windows\system32\drivers\etc\hosts'
            # $x=get-content $updatesLog
            # $y=invoke-command -ComputerName $computer -ScriptBlock{
            #     param($localUpdatesLog)
            #     get-content $localUpdatesLog
            # } -Args $localUpdatesLog
            $logContent=try{
                    get-content $updatesLog -credential $adminCredential -ea Stop
                }catch{
                    invoke-command -ComputerName $computer -Credential $adminCredential -ScriptBlock{
                        param($localUpdatesLog)
                        get-content $localUpdatesLog
                    } -Args $localUpdatesLog
                }
            if($logContent){
                $currentLine=($logContent|select -last 1|out-string).trim()
                $updatesCompleted=$currentLine -match 'FINISHED$'
                $rebootRequired=$currentLine -match 'REBOOT$'
                if($currentLine -ne $previousLine){
                    write-host "`r`n$currentLine"
                    $previousLine=$currentLine
                }else{
                    write-host '.' -nonewline
                    sleep 10
                }
                if($rebootRequired){                    
                    $null=try{                            
                            Restart-Computer -Force -ComputerName $computer -credential $adminCredential -Wait -ea Stop
                            $currentLine="$(get-date) => $computer REBOOTED"                            
                        }catch{
                            invoke-command -ComputerName $computer -Credential $adminCredential -ScriptBlock{
                                write-host "Restarting $env:computername..."
                                Restart-Computer -Force
                            }
                            $computerIsOff=$computerIsOn=$computerRestarted=$False
                            do{                                
                                if(!$computerIsOff){
                                    write-host "." -NoNewline
                                    sleep 1
                                    $computerIsOff=!(test-connection $computer -Count 1 -Quiet)
                                }elseif(!$computerIsOn){
                                    write-host "." -NoNewline
                                    sleep 1
                                    $computerIsOn=test-connection $computer -Count 1 -Quiet
                                    if($computerIsOn){                                        
                                        $startUpTime=invoke-command -computername $computer -Credential $adminCredential -scriptblock{(Get-CimInstance -ClassName win32_OperatingSystem).lastbootuptime}
                                        $computerRestarted=$True
                                        $currentLine="$(($startupTime|out-string).trim()) => $computer REBOOTED"
                                    }                                   
                                }
                            }until($computerRestarted)                            
                        }                      
                    try{
                        Add-content $updatesLog $currentLine -credential $adminCredential
                    }catch{
                        invoke-command -ComputerName $computer -Credential $adminCredential -ScriptBlock{
                            param($localUpdatesLog,$currentLine)
                            Add-content $localUpdatesLog $currentLine
                        } -Args $localUpdatesLog,$currentLine
                    }
                    write-host $currentLine
                    do{
                        $newSession=if($adminCredential){
                            try{
                                New-PSSession -ComputerName $computer -Credential $adminCredential -ea Stop
                            }catch{
                                New-PSSession -ComputerName $computer -Credential $adminCredential -SessionOption $(new-pssessionoption -IncludePortInSPN)
                            }
                        }else{
                            try{
                                New-PSSession -ComputerName $computer -ea Stop
                            }catch{
                                New-PSSession -ComputerName $computer -SessionOption $(new-pssessionoption -IncludePortInSPN)
                            }
                        }
                        sleep 1
                    }until($newSession.State -eq 'Opened')
                    $currentLine="$(get-date) => $taskName re-triggered"
                    invoke-command -session $newSession -scriptblock {
                        param ($localUpdatesLog,$taskName,$currentLine)
                        Start-ScheduledTask -TaskName $taskname                    
                        $null=Add-content $localUpdatesLog $currentLine
                    } -Args $localUpdatesLog,$taskName,$currentLine
                    Remove-PSSession $newSession                
                    write-host $currentLine
                }elseif($updatesCompleted){
                    $currentLine="$(get-date) => $computer patching FINISHED"
                    $null=try{
                        Add-content $updatesLog $currentLine -ea Stop
                    }catch{
                        invoke-command -ComputerName $computer -Credential $adminCredential -ScriptBlock{
                            param($localUpdatesLog,$currentLine)
                            Add-content $localUpdatesLog $currentLine
                        } -Args $localUpdatesLog,$currentLine
                    }
                    write-host $currentLine -ForegroundColor Green    
                }
            }else{
                write-host '.' -nonewline
                sleep 10
            }        
        }
    }
}else{
    write-host "$computer currently has Internet access. Use a diffferent function to patch it."
}
# Sample Output
#
# testwindows: no internet connectivity detected
# Adding task on testwindows...
# -Message windowsUpdate has taken 0.5089243 seconds to initiate
# Checking \\testwindows\c$\WindowsUpdate.log for Windows Update statuses...
# 01/18/2021 18:51:36 => TESTWINDOWS patching STARTED
# .01/18/2021 18:51:51 => 1 of 3 succeeded: Security Update for Windows Server 2019 for x64-based Systems (KB4535680)
# ........................................................................................................................
# ............................01/18/2021 19:16:34 => 2 of 3 succeeded: 2021-01 Cumulative Update for Windows Server 2019 f
# or x64-based Systems (KB4598230)
# ......01/18/2021 19:17:38 => 3 of 3 succeeded: 2021-01 Servicing Stack Update for Windows Server 2019 for x64-based Syst
# ems (KB4598480)
# .01/18/2021 19:17:43 => TESTWINDOWS required a REBOOT
# ...................................................
# 01/18/2021 19:29:25 => testwindows REBOOTED
# 01/18/2021 19:34:12 => windowsUpdate re-triggered
# Checking \\testwindows\c$\WindowsUpdate.log for Windows Update statuses...
# 01/18/2021 19:36:09 => TESTWINDOWS has 0 available updates. Patching FINISHED
# 01/18/2021 19:37:52 => testwindows patching FINISHED

15 thoughts on “Update Windows with Restricted Internet Access”

  1. get-content : Access is denied
    At line:217 char:17
    + get-content $updatesLog
    + ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : PermissionDenied: (\\testWindows\c$\WindowsUpdate.log:String) [Get-Content], UnauthorizedAccessException
    + FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetContentCommand

    get-content : Cannot find path ‘\\testWindows\c$\WindowsUpdate.log’ because it does not exist.

    And if I define more then one computers in the $computerc variable, the scipt says:
    “testWindows has Internet access. Use a diffferent function to patch it.”

    Isn’t there a way for a simple script, that only recognized thge pending updates, windows shows in the update section, then it shoul d install these updates an make a reebot. maybe scan again for new updates.

  2. Ok… I figured out something. Every time, the servers reboot, they will lost the credentials of each other for the connection between them. Even, if I store them by activate the Checkbox, when connect: Seems a group policy from the custome is doing that.

    So for that reason the error message from my above comment is shown up. I tried t access the file by hand from remote and It says “acces denied” too. I have no chance to enter credentials at this moment. So If I open only “\\\c$windows” without “\WindowsUpdate.log” , I can enter my credentials and connect to the drive on the remote Server.
    When I now run the script again, It can open the logfile an work on the task. So I don’t know, why the script can’t access the file from beginning, because the scheduled task is generatet without any problem on the remote server.

    Ok. So I was able to run the script with manual help , which should be fixed.
    Now the WindowsUpdate.log says “<servername" has 0 available updates. Patching FINISHED
    But when I look into the available windows Updates on the server, it says:

    "Updates are available.
    2020-12 Cumulative Update for Windows Server 2016 for x64-based Systems (KB4593226).
    Updates are ready to install"

    Why doesn't your script detect this outstanding update?

    1. I’ve finally find some additional time to check this script out and update it.

      The script didn’t detect that outstanding update because I’ve missed the “-ea Stop” part in the try-catch

      $logContent=try{
      get-content $updatesLog -ea Stop
      }catch{
      invoke-command -ComputerName $computer -Credential $adminCredential -ScriptBlock{
      get-content ‘C:\WindowsUpdate.log’}}

      It should work via WinRM now.

  3. Hi , and a happy new year. I’ve just tested your updated script.
    The server itself says a reboot is necessary to install “2020-12 Cumulative Update for Windows Server 2016 for x64-based Systems (KB4593226)”.
    When I run your script, the following output is shown:

    “TESTWINDOWS: no internet connectivity detected
    Adding task on TESTWINDOWS…
    -Message windowsUpdate has taken 0.5000465 seconds to initiate
    Checking \\TESTWINDOWS\c$\WindowsUpdate.log for Windows Update statuses…”

    Next, this message is shown severel hundred times:

    “01/07/2021 07:33:00 => 1 of 1 updates downloaded successfully”

    After that, this message cames up, several hundred times, till I end the Scipt manually:

    “01/07/2021 07:33:14 => 0 of 1 succeeded: Security Intelligence Update for Microsoft Defender Antivirus – KB2267602 (Version 1.329.1786.0)”

    So the Script finds an update, Windows is not showing as outstanding. But why does it show these messages so many times? Will it show the messages as long, as the installation is in progress in the background? If, yes, is it possible to write these message only one time with the additional note “please wait” ?

    1. Happy new year, Marc

      The script has been updated – please give it another try. The next revision should have a feature to simultaneously execute on a list of Windows machine to reduce maintenance time window. Perhaps, the original script now meets your requirements for all Windows environments.

  4. Hi Kim,
    Looks like the finish line is near. With this updated script, The server installs their found upadets. The output ays, that a reeboot is triggered, but this semms to be fail. Tghe output further says, patching finished, but with outstanding reeboot, the last update could’t be installed complete and is still outstanding.:

    : no internet connectivity detected
    Adding task on …
    -Message windowsUpdate has taken 0.4799641 seconds to initiate
    Checking \\\c$\WindowsUpdate.log for Windows Update statuses…
    01/19/2021 07:55:55 => 3 available updates detected
    ……………………………………………………….01/19/2021 08:06:34 => 3 of 3 updates downloaded successfully
    ……………………………………………………………………………….01/19/2021 08:21:39 => 1 of 3 succeeded: 2021-01 Cumulative Update for Windows Server 2016 for x64-based Systems (KB4598243)
    .01/19/2021 08:21:50 => required a REBOOT
    Restart-Computer : The computer is skipped. Fail to retrieve its LastBootUpTime via the WMI service with the following error message: Access is denied. (Exception from
    HRESULT: 0x80070005 (E_ACCESSDENIED)).
    At line:254 char:29
    + … Restart-Computer -Force -ComputerName $computer -Wait
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OperationStopped: (:String) [Restart-Computer], InvalidOperationException
    + FullyQualifiedErrorId : RestartComputerSkipped,Microsoft.PowerShell.Commands.RestartComputerCommand

    01/19/2021 08:21:55 => REBOOTED
    01/19/2021 08:21:57 => windowsUpdate re-triggered
    01/19/2021 08:21:57 => windowsUpdate re-triggered
    .01/19/2021 08:21:59 => patching STARTED
    .01/19/2021 08:22:13 => has 0 available updates. Patching FINISHED
    01/19/2021 08:22:18 => patching FINISHED

    On some other servers, there is only one update ready to install:
    – Security Intelligence Update for Microsoft Defender Antivirus – KB2267602 (Version 1.329.2448.0)

    , but this script doesn’t detect it and outputs:

    : no internet connectivity detected
    Adding task on …
    -Message windowsUpdate has taken 0.4548604 seconds to initiate
    Checking \\\c$\WindowsUpdate.log for Windows Update statuses…
    01/19/2021 08:42:40 => has 0 available updates. Patching FINISHED
    01/19/2021 08:42:44 => patching FINISHED

    Same with the “original” Script

  5. I just fount this script, that checks for pending updates in many registry path, and detects my outstanding reboot.
    But for me it’s hard, to include it in your script. May you could check this out?

    ###############################################################################
    # Check-PendingReboot.ps1
    # Andres Bohren
    # Version 1.0 / 03.06.202020 - Initial Version
    ###############################################################################

    function Test-RegistryValue {
    param (
    [parameter(Mandatory=$true)][ValidateNotNullOrEmpty()]$Path,
    [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()]$Value
    )
    try {
    Get-ItemProperty -Path $Path | Select-Object -ExpandProperty $Value -ErrorAction Stop | Out-Null
    return $true
    }
    catch {
    return $false
    }
    }

    [bool]$PendingReboot = $false

    #Check for Keys
    If ((Test-Path -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") -eq $true)
    {
    $PendingReboot = $true
    }

    If ((Test-Path -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting") -eq $true)
    {
    $PendingReboot = $true
    }

    If ((Test-Path -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") -eq $true)
    {
    $PendingReboot = $true
    }

    If ((Test-Path -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending") -eq $true)
    {
    $PendingReboot = $true
    }

    If ((Test-Path -Path "HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttempts") -eq $true)
    {
    $PendingReboot = $true
    }

    #Check for Values
    If ((Test-RegistryValue -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing" -Value "RebootInProgress") -eq $true)
    {
    $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing" -Value "PackagesPending") -eq $true)
    {
    $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Value "PendingFileRenameOperations") -eq $true)
    {
    $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Value "PendingFileRenameOperations2") -eq $true)
    {
    $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" -Value "DVDRebootSignal") -eq $true)
    {
    $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon" -Value "JoinDomain") -eq $true)
    {
    $PendingReboot = $true
    }

    If ((Test-RegistryValue -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon" -Value "AvoidSpnSet") -eq $true)
    {
    $PendingReboot = $true
    }

    Write-Host "Reboot pending: $PendingReboot"

    1. This function by Andres Bohren is a nice one. The original script does have a limited subset of the check list being queried by this script. I’ll consider updating that original with some borrowed techniques from Andres Bohren, if necessary.

  6. Hey. Got it by myself with the additional regestry paths.
    Now the reeboot was initiated. But one thing is now pending: It seems, the task is not waiting enough tine to reinitiate the update process:

    .01/19/2021 11:48:07 => required a REBOOT
    01/19/2021 11:48:14 => REBOOTED
    01/19/2021 11:48:15 => windowsUpdate re-triggered
    01/19/2021 11:48:15 => windowsUpdate re-triggered
    [] Connecting to remote server failed with the following error message : WinRM cannot complete the operation. Verify that the specified computer name is
    valid, that the computer is accessible over the network, and that a firewall exception for the WinRM service is enabled and allows access from this computer. By default, the WinRM
    firewall exception for public profiles limits access to remote computers within the same local subnet. For more information, see the about_Remote_Troubleshooting Help topic.
    + CategoryInfo : OpenError: (:String) [], PSRemotingTransportException
    + FullyQualifiedErrorId : WinRMOperationTimeout,PSSessionStateBroken
    ……………………………………………………………………………………..

  7. Another “bug” I found is, when one of the Servers in my List, habve an internet connection, the script will end immediately with “currently has Internet access. Use a diffferent function to patch it.”
    It should install the founded updates in this case, too

  8. Hi Kim,

    I think you did it! The original script works nearly perfect now. I put in 10 server IP’s to the variable and all of them got their updates and makes a reboot successfully. No outstanding updates after that! Thank you very, very, very much for your hard work!
    So only thing is, script runs further, after all is done. The minutes still counting. I stopped it at minute 152. Updates were all done. I’ve checked that . So I don’t know, what the script is waiting for all the time.

    Another question: Would it be a hard work to summarize the installed updates to an html formatted table, so that the script can send it out per e-mail?
    I only needt the html code for that:

    IP | KB Number | Update description | reboot time

    Would be a charm and takes this script to the next level.

    1. Hi Marc,

      Thanks for trying this out and giving me good ideas. When I have more time, I’ll definitely update the original script to include:
      1. HTML/CSS report to be send via email
      2. Function to email via SMTP or relay

      Of course, the ‘minutes still counting’ bug will be fixed as well.

      To be continued…

      1. Thank you for your great help till now!!!
        That makes workflow a lot easier for me.
        I will look to this site regulary!

Leave a Reply

Your email address will not be published. Required fields are marked *