PowerShell: Update Remote Windows

This script has been deprecated in favor of a more universal one here.

function invokeWindowsUpdate{ 
    [CmdletBinding()] 
    param ( 
        [parameter(Mandatory=$true,Position=0)][string]$computer,
        [parameter(Mandatory=$false,Position=1)][System.Management.Automation.PSCredential]$adminCredential,
        [parameter(Mandatory=$false,Position=2)][bool]$microsoftUpdates=$false,
        [parameter(Mandatory=$false,Position=3)][bool]$bypassWsus=$false, 
        [parameter(Mandatory=$false,Position=4)][int]$winRmPort=5985,
        [parameter(Mandatory=$false,Position=5)][string]$logFile='C:\PSWindowsUpdate.log'
        )    
    $ErrorActionPreference='stop'
    <#
    .SYNOPSIS
    This script will automatically install all avaialable windows updates on a device and will automatically reboot if needed.
    After reboots, windows updates will continue to run until all updates are installed.
    # Features: 
    # - Check WSUS settings (bypass if required by the boolean value)
    # - Prepare the targets by installing prerequisites prior to proceeding further to preemptively resolve dependency errors
    # - Include additional dedendencies such as TLS1.2, Nuget & PSGALLERY
    # - Check if server needs a reboot before issuing the reboot command, instead of just inadvertently trigger reboots
    # - Fixed the blank lines in output log causing bug in status query
    # - More thorough cleanup routine
    # Future developments:
    # - Detect and handle proxies
    #>
 
    function installPsWindowsUpdate{
        $ErrorActionPreference='stop'
        $psWindowsUpdateAvailable=Get-Module -ListAvailable -Name PSWindowsUpdate -ErrorAction SilentlyContinue;
        if (!($psWindowsUpdateAvailable)){
            try {                
                [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;          
                Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Confirm:$false | Out-Null;
                Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted | Out-Null;
                Install-Module PSWindowsUpdate -Confirm:$false -Force | Out-Null;
                Import-Module PSWindowsUpdate -force | Out-Null;
                return $true;
                }
            catch{
                "Prerequisites not met on $ENV:COMPUTERNAME.";
                return $false;
                }
        }else{
            return $true
            }
    }
   
    function checkPendingReboot([string]$computer=$ENV:computername,$session){ 
        function checkRegistry{
            if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -EA Ignore) { return $true }
            if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootInProgress" -EA Ignore) { return $true }
            if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -EA Ignore) { return $true }
            if (Get-Item "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending" -EA Ignore) { return $true }
            if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting" -EA Ignore) { return $true }
            if (Get-Item 'HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttemps' -EA Ignore) { return $true }
            if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name 'PendingFileRenameOperations' -EA Ignore) { return $true }
            if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name 'PendingFileRenameOperations2' -EA Ignore) { return $true }
            if (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' -Name 'DVDRebootSignal' -EA Ignore) { return $true }
            if (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon' -Name 'JoinDomain' -EA Ignore) { return $true }
            if (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon' -Name 'AvoidSpnSet' -EA Ignore) { return $true }
            try{ # This peruses CCM utility, if exists
                $util = [wmiclass]"\\.\root\ccm\clientsdk:CCM_ClientUtilities"
                $status = $util.DetermineIfRebootPending()
                if(($null -ne $status) -and $status.RebootPending){
                    $result.SCCMRebootPending = $true
                }
            }catch{
                return $false
            }                    
        }

        if ($ENV:computername -eq $computer){
            $result=checkRegistry;
        }else{
            $result=Invoke-Command -session $session -ScriptBlock{
                    param($importedFunc);
                    [ScriptBlock]::Create($importedFunc).Invoke();
                } -ArgumentList ${function:checkRegistry}
        }
        return $result;
    }
 
    function clearRebootFlags($computer,$session,$verbose=$false){
        if (checkPendingReboot $computer $session){                    
            $isLocalHost=$env:computername -eq $computer
            if($isLocalHost){
                write-warning "$computer is LOCALHOST. Please reboot manually to clear reboot flags"
                return $false
            }else{
                write-host "`nRestarting remote computer $computer to clear pending reboot flags..." 
                if($adminCredential){
                    Restart-Computer -Wait -ComputerName $computer -Credential $adminCredential -Force
                }else{
                    Restart-Computer -Wait -ComputerName $computer -Force
                    }
                write-host "$computer has been successfully restarted!" -ForegroundColor Yellow
                return $true
            }
        }else{
            if($verbose){write-host "There are no pending reboot flags to clear." -ForegroundColor Green}
            return $true
        }
    }   
    function checkWsus{
        # Check if this machine has WSUS settings configured
        $wuPath="Registry::HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU";
        $wuKey="UseWUServer";
        $wuIsOn=(Get-ItemProperty -path $wuPath -name $wuKey -ErrorAction SilentlyContinue).$wuKey;
        #if($wuIsOn){$GLOBAL:wsus=$True}else{$GLOBAL:wsus=$False}
        return $wuIsOn  
    }
   
    function turnoffWsus{
        # Turn WSUS settings OFF temporarily...
        $wuPath="Registry::HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU";
        $wuKey="UseWUServer";
        Set-Itemproperty -path $wuPath -Name $wuKey -value 0
        restart-service wuauserv;        
        }
   
    function turnonWsus{
        # Turning WSUS settings back to ON status
        $wuPath="Registry::HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU";
        $wuKey="UseWUServer";
        Set-Itemproperty -path $wuPath -Name $wuKey -value 1
        restart-service wuauserv;
        }
    function connectWinRm($computer,$adminCredential,$winRmPort=5985){
        if(!$computer){
            write-warning "Computer name must be specified to initiate a WinRM connection."
            return $false
        }
        # Legacy equivalent to Test-Netconnection
        function checkNetConnection($computername,$port,$timeout=1000,$verbose=$false) {
            $tcp = New-Object System.Net.Sockets.TcpClient;
            try {
                $connect=$tcp.BeginConnect($computername,$port,$null,$null)
                $wait = $connect.AsyncWaitHandle.WaitOne($timeout,$false)
                if(!$wait){
                    $null=$tcp.EndConnect($connect)
                    $tcp.Close()
                    if($verbose){
                        Write-Host "Connection Timeout" -ForegroundColor Red
                        }
                    Return $false
                }else{
                    $error.Clear()
                    $null=$tcp.EndConnect($connect) # Dispose of the connection to release memory
                    if(!$?){
                        if($verbose){
                            write-host $error[0].Exception.Message -ForegroundColor Red
                            }
                        $tcp.Close()
                        return $false
                        }
                    $tcp.Close()
                    Return $true
                }
            } catch {
                return $false
            }
        }
        function enableWinRmRemotely($remoteComputer,$winRmPort,$adminCredential){
            function Check-NetConnection($computername,$port,$timeout=1000,$verbose=$false) {
                    $tcp = New-Object System.Net.Sockets.TcpClient;
                    try {
                        $connect=$tcp.BeginConnect($computername,$port,$null,$null)
                        $wait = $connect.AsyncWaitHandle.WaitOne($timeout,$false)
                        if(!$wait){
                            $null=$tcp.EndConnect($connect)
                            $tcp.Close()
                            if($verbose){
                                Write-Host "Connection Timeout" -ForegroundColor Red
                                }
                            Return $false
                        }else{
                            $error.Clear()
                            $null=$tcp.EndConnect($connect) # Dispose of the connection to release memory
                            if(!$?){
                                if($verbose){
                                    write-host $error[0].Exception.Message -ForegroundColor Red
                                    }
                                $tcp.Close()
                                return $false
                                }
                            $tcp.Close()
                            Return $true
                        }
                    } catch {
                        return $false
                    }
            }
            if (!(get-command psexec)){
                # Install Chocolatey
                if (!(Get-Command choco.exe -ErrorAction SilentlyContinue)) {
                    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
                    Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
                    }
                choco install sysinternals -y;  
                }
            $success=check-netconnection $remoteComputer $winRmPort
            write-host 'Attempting to use psexec to enable WinRM remotely...'
            if(!$adminCredential){ # Enable WinRM Remotely
                $null=psexec.exe \\$remoteComputer -s C:\Windows\system32\winrm.cmd qc -quiet; 
            }else{
                $username=$adminCredential.Username
                $password=$adminCredential.GetNetworkCredential().Password
                $null=psexec.exe \\$remoteComputer -u $username -p $password -s C:\Windows\system32\winrm.cmd qc -quiet
                }
            return check-netconnection $remoteComputer $winRmPort
        }

        # If machine is not pingable, wait five minutes
        $fiveMinuteTimer=[System.Diagnostics.Stopwatch]::StartNew()
        do{
            $ping = Test-Connection $computer -quiet
            if($ping -eq $false){sleep 1}
            $pastFiveMinutes=$fiveMinuteTimer.Elapsed.TotalMinutes -ge 5
        }until ($ping -eq $true -or $pastFiveMinutes)
        $fiveMinuteTimer.stop()

        $winRmAvailable=checkNetConnection $computer $winRmPort
        if(!$winRmAvailable){
            write-host "Attempting to enable WinRM on $computer" -ForegroundColor Yellow
            $enableWinRmSuccessful=enableWinRmRemotely $computer
            if($enableWinRmSuccessful){
                write-host "WinRM enabled: $enableWinRmSuccessful"
            }else{
                write-warning "WinRM could not be enabled remotely. WinRM connection aborted."
                return $false
                }
        }   
        # Wait for WinRm session prior to proceeding
        if($session.state -eq 'Opened'){remove-pssession $session}
        do{
            if($adminCredential){
                try{
                    $session=New-PSSession -ComputerName $computer -Credential $adminCredential
                }catch{
                    $session=New-PSSession -ComputerName $computer -Credential $adminCredential -SessionOption $(new-pssessionoption -IncludePortInSPN)
                }
            }else{
                try{
                    $session = New-PSSession -ComputerName $computer
                }catch{
                    $session=New-PSSession -ComputerName $computer -SessionOption $(new-pssessionoption -IncludePortInSPN)
                }
                }
            write-host "Connecting to remote computer $computer..."
            sleep -seconds 1
            if ($session){
                write-host "Connected."
                return $session
            }
        } until ($session.state -match "Opened")
    }

    # Advisories
    write-warning "$computer will go through Windows Update and will REBOOT AUTOMATICALLY (if necessary). Press Ctrl+C at anytime to cancel."
   
    $session=connectWinRm $computer $adminCredential
    # Install prerequisites
    $psWindowsUpdateAvailable=invoke-command -session $session -scriptblock {
        param($installPsWindowsUpdate);
        [ScriptBlock]::Create($installPsWindowsUpdate).Invoke();
        } -Args ${function:installPsWindowsUpdate}
        
    if(!$psWindowsUpdateAvailable){
        write-warning "$computername`t: PSWindowsUpdate installation failed."
        return $false
        }
    if($bypassWsus){
        $wsusIsOn=Invoke-Command -session $session -scriptblock{
            param($checkWsus);
            [scriptblock]::create($checkWsus).invoke()
            } -Args ${function:checkWsus}
        if($wsusIsOn){
            Invoke-Command -session $session -scriptblock{
            param($turnoffWsus);
            [scriptblock]::create($turnoffWsus).invoke()
            } -args ${function:turnoffWsus}
        }
    }
    Do{
        #retrieves a list of available updates 
        write-host "Checking for new updates on $computer..."
        $updates=invoke-command -session $session -scriptblock {
            param($microsoftUpdates)
            if($(Get-ExecutionPolicy) -ne 'RemoteSigned'){
                Set-ExecutionPolicy RemoteSigned -force
            }
            $null=Import-Module PSWindowsUpdate
            $availableUpdates=if($microsoftUpdates){
                    Get-wulist -MicrosoftUpdate -verbose
                }else{
                    Get-Wulist -verbose
                }
            write-host $($availableUpdates|out-string).trim()
            return $availableUpdates
        } -Args $microsoftUpdates
        $updatesCount=$updates.KB.count # $updates.count returns $null when count equals 1
        # If there are available updates, proceed with installing the updates and then reboot the remote machine if required
        if ($updatesCount){ 
            #Invoke-WUJob will insert a scheduled task on the remote target as a mean to bypass 2nd hop issues            
            invoke-command -Session $session {
                param($microsoftUpdates)
                $logFile='C:\PSWindowsUpdate.log'
                if(test-path $logFile){remove-item $logFile -force}                
                if($microsoftUpdates){
                    # Register Microsoft Update Service if it has not been registered
                    $microsoftUpdateId='7971f918-a847-4430-9279-4a52d1efe18d'
                    if (!($microsoftUpdateId -in (Get-WUServiceManager).ServiceID)){
                        Add-WUServiceManager -ServiceID $microsoftUpdateId -Confirm:$false
                        }
                    $invokeScript={
                        import-module PSWindowsUpdate;
                        Get-WindowsUpdate -AcceptAll -MicrosoftUpdate -Install | Out-File 'C:\PSWindowsUpdate.log'
                    }
                }else{
                    $invokeScript={
                        import-module PSWindowsUpdate;
                        Get-WindowsUpdate -AcceptAll -Install | Out-File 'C:\PSWindowsUpdate.log'
                    }
                }
                Invoke-WUjob -ComputerName $env:computername -Script $invokeScript -Confirm:$false -RunNow
                write-host "Windows Updates have been triggerred. Now checking for its log file to generate...`r`n"
                Do{
                    $logFileGenerated=if(test-path $logFile){get-content $logFile}else{$false}
                    if(!$logFileGenerated){
                        Start-Sleep -Seconds 1
                        write-host '.' -NoNewline
                    }
                }until($logFileGenerated)
            } -Args $microsoftUpdates

            #Show update status until the amount of installed updates equals the same as the count of updates being reported 
            $dotLimit=10
            $dotCount=0
            $minute=0
            $lastActivity=$null;
            $installedCount=0;
            Write-Host "There $(if($updatesCount -eq 1){'is'}else{'are'}) $updatesCount pending update(s)`r`n";
            do {                
                $getWindowsUpdateLog={param($logFile);Get-Content $logFile}
                $updatestatus=Invoke-Command -Session $session -ScriptBlock $getWindowsUpdateLog -Args $logFile
                $currentActivity=.{
                    $index=$updatestatus.count-1
                    do{
                        $value=$updatestatus[$index--]
                    }until($value)
                    return $value
                    }
                $installedCount=([regex]::Matches($updatestatus, "Installed")).count
                $failsCount=([regex]::Matches($updatestatus, "Failed")).count
                $processedCount=$installedCount+$failsCount
                if ($currentActivity -ne $lastActivity){
                    if($currentActivity -match 'Installed'){
                        Write-Host "`r`nProcessed $processedCount of $updatesCount updates: $currentActivity" -ForegroundColor Green
                    }elseif($currentActivity -match 'Failed'){
                        Write-Host "`r`nFailed item: $currentActivity" -ForegroundColor Yellow
                    }else{
                        Write-Host "`r`n$currentActivity"
                        }
                    $lastActivity=$currentActivity;
                    }else{
                        if ($dotCount++ -le $dotLimit){
                            Write-Host -NoNewline "."
                            if($processedCount -eq $updatesCount){
                                Write-Host "Processing last update: $processedCount of $updatesCount." -ForegroundColor Yellow
                            }
                        }else{
                            $minute++
                            Write-Host "`r`nMinute count: $minute"
                            $dotCount=0
                            }                                                   
                        }                
                Start-Sleep -Seconds 6                              
            }until ($processedCount -eq $updatesCount)
        }else{
            write-host "There are no available patches detected on $computer basing on current update criteria."
            }
        $localhostRebootFlag=!(clearRebootFlags $computer $session)
        if($session.State -ne 'Opened'){
            write-host "Reconnecting to $computer..." -ForegroundColor Yellow
            $session=connectWinRm $computer $adminCredential
        }
        $updatesCompleted=$installedCount -eq $updatesCount
        if($updatesCompleted){
            Write-Host "Windows is now up to date on $computer" -ForegroundColor Green
        }
    }until(($null -eq $updates) -or $updatesCompleted -or $localhostRebootFlag)
    invoke-command -Session $session -ScriptBlock {
        param($logFile)
        if (Get-ScheduledTask -TaskName "PSWindowsUpdate" -ErrorAction SilentlyContinue){
            Write-Host "Removing PSWindowsUpdate scheduled task from $env:computername..."
            Unregister-ScheduledTask -TaskName PSWindowsUpdate -Confirm:$false};
        if (Test-Path $logFile -Credential $adminCredential -ErrorAction SilentlyContinue){
            Write-Host "Deleting log to prevent collisions with subsequent runs."                    
            Write-Host "Removing $logFile."
            Remove-item $logFile
            }
        } -Args $logFile
    if($bypassWsus -and $wsusIsOn){
        write-host "Reverting WSUS registry edits"
        Invoke-Command -session $session -scriptblock{
            param($turnonWsus);
            [scriptblock]::create($turnonWsus).invoke()
        } -args ${function:turnonWsus} ;
    }    
    if($session.State -eq 'Opened'){Remove-PSSession $session}
    if($localhostRebootFlag){
        write-host "$computer requires a reboot to complete the updates"
        return $false
    }else{
        return $true
    }
}
# updateRemoteWindows_v0.0.3.ps1
# Requirement: WinRM and Internet Access must be enabled on target computer(s)
 
$computername='192.168.100.114'
$adminCredential=get-credential
 
function updateRemoteWindows{ 
    [CmdletBinding()] 
    param ( 
        [parameter(Mandatory=$true,Position=0)][string]$computer,
        [parameter(Mandatory=$false,Position=1)][System.Management.Automation.PSCredential]$adminCredential,
        [parameter(Mandatory=$false,Position=2)][bool]$microsoftUpdates=$false,
        [parameter(Mandatory=$false,Position=3)][bool]$bypassWsus=$false, 
        [parameter(Mandatory=$false,Position=4)][int]$winRmPort=5985,
        [parameter(Mandatory=$false,Position=5)][string]$logFile='C:\PSWindowsUpdate.log'
        )    
    $ErrorActionPreference='stop'
    <#
    .SYNOPSIS
    This script will automatically install all avaialable windows updates on a device and will automatically reboot if needed.
    After reboots, windows updates will continue to run until all updates are installed.
    # Features: 
    # - Check WSUS settings (bypass if required by the boolean value)
    # - Prepare the targets by installing prerequisites prior to proceeding further to preemptively resolve dependency errors
    # - Include additional dedendencies such as TLS1.2, Nuget & PSGALLERY
    # - Check if server needs a reboot before issuing the reboot command, instead of just inadvertently trigger reboots
    # - Fixed the blank lines in output log causing bug in status query
    # - More thorough cleanup routine
    # Future developments:
    # - Detect and handle proxies
    #>
 
    function installPsWindowsUpdate{
        $ErrorActionPreference='stop'
        $psWindowsUpdateAvailable=Get-Module -ListAvailable -Name PSWindowsUpdate -ErrorAction SilentlyContinue;
        if (!($psWindowsUpdateAvailable)){
            try {                
                [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;          
                Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Confirm:$false | Out-Null;
                Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted | Out-Null;
                Install-Module PSWindowsUpdate -Confirm:$false -Force | Out-Null;
                Import-Module PSWindowsUpdate -force | Out-Null;
                return $true;
                }
            catch{
                "Prerequisites not met on $ENV:COMPUTERNAME.";
                return $false;
                }
        }else{
            return $true
            }
    }
   
    function checkPendingReboot([string]$computer=$ENV:computername,$session){ 
        function checkRegistry{
            if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -EA Ignore) { return $true }
            if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootInProgress" -EA Ignore) { return $true }
            if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -EA Ignore) { return $true }
            if (Get-Item "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending" -EA Ignore) { return $true }
            if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting" -EA Ignore) { return $true }
            if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -EA Ignore) { return $true }
            if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations2 -EA Ignore) { return $true }
            try{ 
                $util = [wmiclass]"\\.\root\ccm\clientsdk:CCM_ClientUtilities"
                $status = $util.DetermineIfRebootPending()
                if(($null -ne $status) -and $status.RebootPending){
                    $result.SCCMRebootPending = $true
                }
            }catch{
                return $false
            }                    
        }

        if ($ENV:computername -eq $computer){
            $result=checkRegistry;
        }else{
            $result=Invoke-Command -session $session -ScriptBlock{
                    param($importedFunc);
                    [ScriptBlock]::Create($importedFunc).Invoke();
                } -ArgumentList ${function:checkRegistry}
        }
        return $result;
    }
 
    function clearRebootFlags($computer,$session,$verbose=$false){
        if (checkPendingReboot $computer $session){                    
            write-host "`nRestarting remote computer $computer to clear pending reboot flags..." 
            if($adminCredential){
                Restart-Computer -Wait -ComputerName $computer -Credential $adminCredential -Force
            }else{
                Restart-Computer -Wait -ComputerName $computer -Force
                }
            write-host "$computer has been successfully restarted!" -ForegroundColor Yellow
        }elseif($verbose){
            write-host "Reboot flags are cleared." -ForegroundColor Green
            }
    }   
    function checkWsus{
        # Check if this machine has WSUS settings configured
        $wuPath="Registry::HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU";
        $wuKey="UseWUServer";
        $wuIsOn=(Get-ItemProperty -path $wuPath -name $wuKey -ErrorAction SilentlyContinue).$wuKey;
        #if($wuIsOn){$GLOBAL:wsus=$True}else{$GLOBAL:wsus=$False}
        return $wuIsOn  
    }
   
    function turnoffWsus{
        # Turn WSUS settings OFF temporarily...
        $wuPath="Registry::HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU";
        $wuKey="UseWUServer";
        Set-Itemproperty -path $wuPath -Name $wuKey -value 0
        restart-service wuauserv;        
        }
   
    function turnonWsus{
        # Turning WSUS settings back to ON status
        $wuPath="Registry::HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU";
        $wuKey="UseWUServer";
        Set-Itemproperty -path $wuPath -Name $wuKey -value 1
        restart-service wuauserv;
        }
    function connectWinRm($computer,$adminCredential,$winRmPort=5985){
        if(!$computer){
            write-warning "Computer name must be specified to initiate a WinRM connection."
            return $false
        }
        # Legacy equivalent to Test-Netconnection
        function checkNetConnection($computername,$port,$timeout=1000,$verbose=$false) {
            $tcp = New-Object System.Net.Sockets.TcpClient;
            try {
                $connect=$tcp.BeginConnect($computername,$port,$null,$null)
                $wait = $connect.AsyncWaitHandle.WaitOne($timeout,$false)
                if(!$wait){
                    $null=$tcp.EndConnect($connect)
                    $tcp.Close()
                    if($verbose){
                        Write-Host "Connection Timeout" -ForegroundColor Red
                        }
                    Return $false
                }else{
                    $error.Clear()
                    $null=$tcp.EndConnect($connect) # Dispose of the connection to release memory
                    if(!$?){
                        if($verbose){
                            write-host $error[0].Exception.Message -ForegroundColor Red
                            }
                        $tcp.Close()
                        return $false
                        }
                    $tcp.Close()
                    Return $true
                }
            } catch {
                return $false
            }
        }
        function enableWinRmRemotely($remoteComputer,$winRmPort,$adminCredential){
            function Check-NetConnection($computername,$port,$timeout=1000,$verbose=$false) {
                    $tcp = New-Object System.Net.Sockets.TcpClient;
                    try {
                        $connect=$tcp.BeginConnect($computername,$port,$null,$null)
                        $wait = $connect.AsyncWaitHandle.WaitOne($timeout,$false)
                        if(!$wait){
                            $null=$tcp.EndConnect($connect)
                            $tcp.Close()
                            if($verbose){
                                Write-Host "Connection Timeout" -ForegroundColor Red
                                }
                            Return $false
                        }else{
                            $error.Clear()
                            $null=$tcp.EndConnect($connect) # Dispose of the connection to release memory
                            if(!$?){
                                if($verbose){
                                    write-host $error[0].Exception.Message -ForegroundColor Red
                                    }
                                $tcp.Close()
                                return $false
                                }
                            $tcp.Close()
                            Return $true
                        }
                    } catch {
                        return $false
                    }
            }
            if (!(get-command psexec)){
                # Install Chocolatey
                if (!(Get-Command choco.exe -ErrorAction SilentlyContinue)) {
                    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
                    Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
                    }
                choco install sysinternals -y;  
                }
            $success=check-netconnection $remoteComputer $winRmPort
            write-host 'Attempting to use psexec to enable WinRM remotely...'
            if(!$adminCredential){ # Enable WinRM Remotely
                $null=psexec.exe \\$remoteComputer -s C:\Windows\system32\winrm.cmd qc -quiet; 
            }else{
                $username=$adminCredential.Username
                $password=$adminCredential.GetNetworkCredential().Password
                $null=psexec.exe \\$remoteComputer -u $username -p $password -s C:\Windows\system32\winrm.cmd qc -quiet
                }
            return check-netconnection $remoteComputer $winRmPort
        }

        $fiveMinuteTimer=[System.Diagnostics.Stopwatch]::StartNew()
        do{
            $ping = Test-Connection $computer -quiet
            if($ping -eq $false){sleep 1}
            $pastFiveMinutes=$fiveMinuteTimer.Elapsed.TotalMinutes -ge 5
        }until ($ping -eq $true -or $pastFiveMinutes)
        $fiveMinuteTimer.stop()

        $winRmAvailable=checkNetConnection $computer $winRmPort
        if(!$winRmAvailable){
            write-host "Attempting to enable WinRM on $computer" -ForegroundColor Yellow
            $enableWinRmSuccessful=enableWinRmRemotely $computer
            if($enableWinRmSuccessful){
                write-host "WinRM enabled: $enableWinRmSuccessful"
            }else{
                write-warning "WinRM could not be enabled remotely. WinRM connection aborted."
                return $false
                }
        }   
        # Wait for WinRm session prior to proceeding
        if($session.state -eq 'Opened'){remove-pssession $session}
        do{
            if($adminCredential){
                $session = New-PSSession -ComputerName $computer -Credential $adminCredential
            }else{
                $session = New-PSSession -ComputerName $computer
                }
            write-host "Connecting to remote computer $computer..."
            sleep -seconds 1
            if ($session){
                write-host "Connected."
                return $session
            }
        } until ($session.state -match "Opened")
    }

    # Ensure that this function does not execute on the localhost
    if($env:computername,'localhost'|?{$_ -like "$computer*"}){
        write-warning "$computer is detected as the localhost where this program is invoked. This is out of scope of this function."
        return $false
        }
    # Advisories
    write-warning "$computer will go through Windows Update and will REBOOT AUTOMATICALLY (if necessary). Press Ctrl+C at anytime to cancel."
   
    $session=connectWinRm $computer $adminCredential
    Do{
        # Install prerequisites
        $psWindowsUpdateAvailable=invoke-command -session $session -scriptblock {
            param($installPsWindowsUpdate);
            [ScriptBlock]::Create($installPsWindowsUpdate).Invoke();
            } -Args ${function:installPsWindowsUpdate}
           
        if(!$psWindowsUpdateAvailable){
            write-warning "$computername`t: PSWindowsUpdate installation failed."
            return $false
            }
   
        #retrieves a list of available updates 
        write-host "Checking for new updates on $computer..."
        $updates=invoke-command -session $session -scriptblock {
            param($microsoftUpdates)
            if($(Get-ExecutionPolicy) -ne 'RemoteSigned'){
                Set-ExecutionPolicy RemoteSigned -force
            }
            $null=Import-Module PSWindowsUpdate
            $availableUpdates=if($microsoftUpdates){
                    Get-wulist -MicrosoftUpdate -verbose
                }else{
                    Get-Wulist -verbose
                }
            write-host $($availableUpdates|out-string).trim()
            return $availableUpdates
        } -Args $microsoftUpdates
        $updatesCount=$updates.KB.count # $updates.count returns $null when count equals 1
        # If there are available updates, proceed with installing the updates and then reboot the remote machine if required
        if ($updatesCount){ 
            if($bypassWsus){
                $wsusIsOn=Invoke-Command -session $session -scriptblock{
                    param($checkWsus);
                    [scriptblock]::create($checkWsus).invoke()
                    } -Args ${function:checkWsus}
                if($wsusIsOn){
                    Invoke-Command -session $session -scriptblock{
                    param($turnoffWsus);
                    [scriptblock]::create($turnoffWsus).invoke()
                    } -args ${function:turnoffWsus}
                }
            }
               
            #Invoke-WUJob will insert a scheduled task on the remote target as a mean to bypass 2nd hop issues            
            invoke-command -Session $session {
                param($microsoftUpdates)
                $logFile='C:\PSWindowsUpdate.log'
                if(test-path $logFile){remove-item $logFile -force}                
                if($microsoftUpdates){
                    # Register Microsoft Update Service if it has not been registered
                    $microsoftUpdateId='7971f918-a847-4430-9279-4a52d1efe18d'
                    if (!($microsoftUpdateId -in (Get-WUServiceManager).ServiceID)){
                        Add-WUServiceManager -ServiceID $microsoftUpdateId -Confirm:$false
                        }
                    $invokeScript={
                        import-module PSWindowsUpdate;
                        Get-WindowsUpdate -AcceptAll -MicrosoftUpdate -Install | Out-File 'C:\PSWindowsUpdate.log'
                    }
                }else{
                    $invokeScript={
                        import-module PSWindowsUpdate;
                        Get-WindowsUpdate -AcceptAll -Install | Out-File 'C:\PSWindowsUpdate.log'
                    }
                }
                Invoke-WUjob -ComputerName $env:computername -Script $invokeScript -Confirm:$false -RunNow
                write-host "Windows Updates have been triggerred. Now checking for its log file to generate...`r`n"
                Do{
                    $logFileGenerated=if(test-path $logFile){get-content $logFile}else{$false}
                    if(!$logFileGenerated){
                        Start-Sleep -Seconds 1
                        write-host '.' -NoNewline
                    }
                }until($logFileGenerated)
            } -Args $microsoftUpdates

            #Show update status until the amount of installed updates equals the same as the count of updates being reported 
            $dotLimit=10
            $dotCount=0
            $minute=0
            $lastActivity=$null;
            $installedCount=0;
            Write-Host "There $(if($updatesCount -eq 1){'is'}else{'are'}) $updatesCount pending update(s)`r`n";
            do {                
                $getWindowsUpdateLog={param($logFile);Get-Content $logFile}
                $updatestatus=Invoke-Command -Session $session -ScriptBlock $getWindowsUpdateLog -Args $logFile
                $currentActivity=.{
                    $index=$updatestatus.count-1
                    do{
                        $value=$updatestatus[$index--]
                    }until($value)
                    return $value
                    }
                $installedCount = ([regex]::Matches($updatestatus, "Installed")).count
                if ($currentActivity -ne $lastActivity){
                    if($currentActivity -match 'Installed'){
                        Write-Host "`r`nProcessed $installedCount of $updatesCount updates: $currentActivity" -ForegroundColor Green
                    }elseif($currentActivity -match 'Failed'){
                        Write-Host "`r`nFailed item: $currentActivity" -ForegroundColor Yellow
                    }else{
                        Write-Host "`r`n$currentActivity"
                        }
                    $lastActivity=$currentActivity;
                    }else{
                        if ($dotCount++ -le $dotLimit){
                            Write-Host -NoNewline "."
                            if($installedCount -eq $updatesCount){
                                Write-Host "Processing last update: $installedCount of $updatesCount." -ForegroundColor Yellow
                            }
                        }else{
                            $minute++
                            Write-Host "`r`nMinute count: $minute"
                            $dotCount=0
                            }                                                   
                        }                
                Start-Sleep -Seconds 6                              
            }until ($installedCount -eq $updatesCount)
        }else{
            write-host "There are no available patches detected on $computer basing on current update criteria."
            }
        clearRebootFlags $computer $session
        if($session.State -ne 'Opened'){
            write-host "Reconnecting to $computer..." -ForegroundColor Yellow
            $session=connectWinRm $computer $adminCredential
        }
    }until(($null -eq $updates) -OR ($installedCount -eq $updatesCount))   
    invoke-command -Session $session -ScriptBlock {
        param($logFile)
        if (Get-ScheduledTask -TaskName "PSWindowsUpdate" -ErrorAction SilentlyContinue){
            Write-Host "Removing PSWindowsUpdate scheduled task from $env:computername..."
            Unregister-ScheduledTask -TaskName PSWindowsUpdate -Confirm:$false};
        if (Test-Path $logFile -Credential $adminCredential -ErrorAction SilentlyContinue){
            Write-Host "Deleting log to prevent collisions with subsequent runs."                    
            Write-Host "Removing $logFile."
            Remove-item $logFile
            }
        } -Args $logFile
    if($bypassWsus -and $wsusIsOn){
        write-host "Reverting WSUS registry edits"
        Invoke-Command -session $session -scriptblock{
            param($turnonWsus);
            [scriptblock]::create($turnonWsus).invoke()
        } -args ${function:turnonWsus} ;
    }
    Write-Host "Windows is now up to date on $computer" -ForegroundColor Green
    if($session.State -eq 'Opened'){Remove-PSSession $session}
    return $true
}
  
updateRemoteWindows -computer $computername -adminCredential $adminCredential -microsoftUpdates $true
# Troubleshooting:
# Error:
#File C:\Program Files\WindowsPowerShell\Modules\PSWindowsUpdate\2.2.0.2\PSWindowsUpdate.psm1 cannot be loaded because running scripts is disabled on this
#system. For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
#At line:237 char:9
#+         $updates = invoke-command -session $session -scriptblock {$nu ...
#+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#    + CategoryInfo          : SecurityError: (:) [Import-Module], PSSecurityException
#    + FullyQualifiedErrorId : UnauthorizedAccess,Microsoft.PowerShell.Commands.ImportModuleCommand
#    + PSComputerName        : 192.168.1.54
# Resolve:
# Set-ExecutionPolicy RemoteSigned -Force
# updateRemoteWindows_v0.0.2.ps1

$computername='TEST-SERVER01'

function updateRemoteWindows{ 
    [CmdletBinding()] 
    param ( 
        [parameter(Mandatory=$true,Position=0)][string]$computer,
        [parameter(Mandatory=$false,Position=1)][bool]$bypassWsus=$false,
        [parameter(Mandatory=$false,Position=2)][System.Management.Automation.PSCredential]$adminCredential,
        [parameter(Mandatory=$false,Position=3)][int]$winRmPort=5985
        )
    $logFile="\\$computer\c$\PSWindowsUpdate.log"
    $ErrorActionPreference='stop'
    <#
    .SYNOPSIS
    This script will automatically install all avaialable windows updates on a device and will automatically reboot if needed.
    After reboots, windows updates will continue to run until all updates are installed.
    # Features: 
    # - Check WSUS settings (bypass if required by the boolean value)
    # - Prepare the targets by installing prerequisites prior to proceeding further to preemptively resolve dependency errors
    # - Include additional dedendencies such as TLS1.2, Nuget & PSGALLERY
    # - Check if server needs a reboot before issuing the reboot command, instead of just inadvertently trigger reboots
    # - Fixed the blank lines in output log causing bug in status query
    # - More thorough cleanup routine
    # Future developments:
    # - Detect and handle proxies
    #>

    function setRegKey{
        [CmdletBinding()]
        Param(
            [Parameter(Position=0, Mandatory=$True)][string]$path,
            [Parameter(Position=1, Mandatory=$True)][string]$name,
            [Parameter(Position=2, Mandatory=$True)][string]$value
        )
        Set-Itemproperty -path $path -Name $name -value $value
        }

    function installPsWindowsUpdate{
        $ErrorActionPreference='stop'
        $psWindowsUpdateAvailable=Get-Module -ListAvailable -Name PSWindowsUpdate -ErrorAction SilentlyContinue;
        if (!($psWindowsUpdateAvailable)){
            try {                
                [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;          
                Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Confirm:$false | Out-Null;
                Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted | Out-Null;
                Install-Module PSWindowsUpdate -Confirm:$false -Force | Out-Null;
                Import-Module PSWindowsUpdate -force | Out-Null;
                return $true;
                }
            catch{
                "Prerequisites not met on $ENV:COMPUTER.";
                return $false;
                }
        }else{
            return $true
            }
    }

    function checkPendingReboot{
            param([string]$computer=$ENV:computername)

            function checkRegistry{
                 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(($status -ne $null) -and $status.RebootPending){
                     return $true
                   }
                 }catch{}
                 return $false             
            }

            $localhost=$ENV:computername
            if ($localHost -eq $computer){
                $result=checkRegistry;
             }else{
                $result=Invoke-Command -ComputerName $computer -Credential $domainAdminCred -ScriptBlock{
                                                                    param($importedFunc);
                                                                    [ScriptBlock]::Create($importedFunc).Invoke();
                                                                    } -ArgumentList ${function:checkRegistry}
                }
            return $result;
        }
    
    # Function requires 2 parameters: -computerName and -processName
    function killProcess{
        [CmdletBinding()]
        param(
          [string[]]$computerName=$ENV:COMPUTERNAME,
          [parameter(Mandatory=$true)][string]$executableName="powershell.exe"
        )
        
        # WMI querying method
        $processes = Get-WmiObject -Class Win32_Process -ComputerName $ComputerName -Credential $domainAdminCred -Filter "name='$executableName'" 
        
        if ($processes){
            foreach ($process in $processes) {
              $terminationResult = $process.terminate()
              $processid = $process.handle
 
            if($terminationResult.returnvalue -eq 0) {
              write-host "The process $executableName `($processid`) terminated successfully"
            } else {
                  write-host "The process $executableName `($processid`) termination has some problems"
                }
            }
        }else{
            "$executableName is currently not running on $computerName."
            }
    }

    function cleanup{      
        #if(check-netconnection -ComputerName $computer -port 445){
        #    if(Get-Process -ComputerName $computer powershell -ErrorAction SilentlyContinue){
        #        Write-Host "Terminating any powershell.exe processes."
        #        killProcess -ComputerName $computer -ExecutableName powershell.exe
        #        }
        #    }
        
        if (Test-Path $logFile -ErrorAction SilentlyContinue){
            Write-Host "Deleting log to prevent collisions with subsequent runs."                       
            Write-Host "Removing $logFile."
            Remove-item $logFile
            }
        
        invoke-command -computername $computer -Credential $domainAdminCred -ScriptBlock {
                            if (Get-ScheduledTask -TaskName "PSWindowsUpdate" -ErrorAction SilentlyContinue){
                                Write-Host "Removing PSWindowsUpdate scheduled task from $computer..."
                                Unregister-ScheduledTask -TaskName PSWindowsUpdate -Confirm:$false};
                            }
                        }

    function checkWsus{
        # Check if this machine has WSUS settings configured
	    $wuPath="Registry::HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU";
	    $wuKey="UseWUServer";
	    $wuIsOn=(Get-ItemProperty -path $wuPath -name $wuKey -ErrorAction SilentlyContinue).$wuKey;
	    if($wuIsOn){$GLOBAL:wsus=$True}else{$GLOBAL:wsus=$False}    
    }

    function turnoffWsus{
		# Turn WSUS settings OFF temporarily...
        $wuPath="Registry::HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU";
        $wuKey="UseWUServer";
		setRegKey -path $wuPath -name $wuKey -value 0;
		restart-service wuauserv;        
        }

    function turnonWsus{
        # Turning WSUS settings back to ON status
        $wuPath="Registry::HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU";
        $wuKey="UseWUServer";
		setRegKey -path $wuPath -name $wuKey -value 1;
		restart-service wuauserv;
        }
    
    function enableWinRm($remoteComputer,$winRmPort){
        function Check-NetConnection($computername, $port=5985) {
            $session = New-Object System.Net.Sockets.TcpClient;
            try {
                $session.Connect($computername, $port);
                $true;
            } catch {
                $false;
            } finally {
                $session.Close();
            }
        }
        if (!(get-command psexec)){
            # Install Chocolatey
            if (!(Get-Command choco.exe -ErrorAction SilentlyContinue)) {
                [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
                Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
                }
            choco install sysinternals -y;  
            }
        $success=check-netconnection $remoteComputer $winRmPort
        psexec.exe \\$remoteComputer -s C:\Windows\system32\winrm.cmd qc -quiet; # Enable WinRM Remotely
        return check-netconnection $remoteComputer $winRmPort
    }

    # Advisories
    write-warning "$computer will go through Windows Update and will REBOOT AUTOMATICALLY (if necessary). Press Ctrl+C at anytime to cancel."

    # Ensure that this function does not execute on the localhost
    if($env:computername,'localhost'|?{$_ -like "$computer*"}){
        write-warning "$computer is detected as the localhost where this script is invoked. Please use another method to update, instead."
        return $false
        }

    # Legacy equivalent to Test-Netconnection
    function Check-NetConnection($computername, $port) {
        $session = New-Object System.Net.Sockets.TcpClient;
        try {
            $session.Connect($computername, $port);
            $true;
        } catch {
            $false;
        } finally {
            $session.Close();
        }
    }
    $winRmAvailable=Check-NetConnection $computer $winRmPort
    if(!$winRmAvailable){
        write-warning "Attempting to enable WinRM on $computer"
        $enableWinRmSuccessful=enableWinRm $computer

        }

    Do{ 
        # Wait for WinRm session prior to proceeding
        do{
            if($adminCredential){
                $session = New-PSSession -ComputerName $computer -Credential $adminCredential
            }else{
                $session = New-PSSession -ComputerName $computer
                }
            "Connecting to remote computer $computer..."
            sleep -seconds 5
            if ($session){"Connected."}
            } until ($session.state -match "Opened")

        # Install prerequisites
        $psWindowsUpdateAvailable=invoke-command -session $session -scriptblock {
                                    param($installPsWindowsUpdate);
                                    return [ScriptBlock]::Create($installPsWindowsUpdate).Invoke();
                                    } -Args ${function:installPsWindowsUpdate}
        
        if(!$psWindowsUpdateAvailable){
            write-warning "$computername`t: PSWindowsUpdate installation failed."
            return $false
            }

        #retrieves a list of available updates 
        "Checking for new updates on $computer..." 
        $updates = invoke-command -session $session -scriptblock {Get-wulist -verbose} 

        # Count how many updates are available 
        $updatesCount = ($updates.kb).count                

        # If there are available updates proceed with installing the updates and then reboot the remote machine if required
        if ($updates -ne $null){ 
            if($bypassWsus){
                checkWsus;
                if($wsus){turnoffWsus}
                }               
            
            #Invoke-WUJob will insert a scheduled task on the remote target as a mean to bypass 2nd hop issues            
            $job=invoke-command -Session $session -AsJob {
                    $invokeScript={import-module PSWindowsUpdate; Get-WindowsUpdate -AcceptAll -Install | Out-File C:\PSWindowsUpdate.log}
                    Invoke-WUjob -ComputerName $env:computername -Script $invokeScript -Confirm:$false -RunNow}

            #Show update status until the amount of installed updates equals the same as the count of updates being reported 
            sleep -Seconds 30 # Wait for the log file to generate
            $dots=80
            $dotsCount=0
            $lastActivity="";
            $installedCount=0;
            Write-Host "There is/are $updatesCount pending update(s)`n";
                        
            do {                
                $updatestatus = Get-Content "\\$computer\c$\PSWindowsUpdate.log"            
                $currentActivity=$updatestatus | select-object -last 1
                
                if (($currentActivity -ne $lastActivity) -AND ($currentActivity -ne $Null)){
                    Write-Host "Procesing $($installedCount+1) of $updatesCount updates."
                    Write-Host "`n$currentActivity";
                    $lastActivity=$currentActivity;
                    }else{
                        if ($dotCount++ -le $dots){
                            Write-Host -NoNewline ".";
                            if($installedCount -eq $updatesCount){Write-Host "Processing last update: $installedCount of $updatesCount."}
                        }else{
                            Write-Host ".";
                            $dotCount=0;
                            }                                                   
                        }                
                sleep -Seconds 10 
                $ErrorActionPreference = 'SilentlyContinue'                 
                $ErrorActionPreference = 'Continue'
                $installedCount = ([regex]::Matches($updatestatus, "Installed")).count
                }until ($installedCount -eq $updatesCount)
 
                #restarts the remote computer and waits till it starts up again
                if (checkPendingReboot $computer){                    
                    write-host "`nReboots required.`nRestarting remote computer $computer to clear pending reboot flags." 
                    Restart-Computer -Wait -ComputerName $computer -Force;
                    write-host "$computer has been restarted."
                }else{
                    write-host "No reboots required."
                    }                        
            }    
    }until(($updates -eq $null) -OR ($installedCount -eq $updatesCount))

    # Revert WSUS registry edits, if any
    cleanup;
    if($wsus -and $bypassWsus){turnonWsus;}
    Write-Host "Windows is now up to date on $computer"
    return $true
}

updateRemoteWindows -computer $computername

Leave a Reply

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