Batch install Windows Server patches


Windows Server often needs to install security patches. wsus's policy of installing patches, especially in the aspect of restarting the server, does not meet the needs. Manual installation needs a lot of time. Therefore, the deployment of a controller can greatly improve the work efficiency by running PowerShell script to remotely install windows patches in batches.


Install Windows server patches in bulk.


Microsoft defines a WS management protocol, which provides a public standard for remote exchange of management data for computer equipment. On Windows platform, MS implements WS management protocol through Windows Remote Management service (WinRM). This is the basis of remote operation that we can perform through PowerShell, because PowerShell operates remotely through WinRM service.

But in the actual test, when we use the following command to install remotely, the installation always fails.

Invoke-Command -ComputerName  $Computer -ScriptBlock { wusa.exe   xxx.msu /quiet /norestart}

As you can see from the log, the error is as follows: Windows Update cannot be installed because of the error: 2147942405 "access denied. "

Originally, Microsoft did not support using wusa and its API to install patch updates remotely. The solution was to use dism instead.


There are three steps for batch installation:

  • Copy the patch file [download patch - > control machine - > target machine]
  • Remote installation patch file
  • Verify installation results

Step 1: copy files

Download the patch file (. msu), copy it to the corresponding directory of the control machine (such as c:\fix), decompress it by script (after pressurization, it is convenient for dism installation) and copy it to the target machine.

"Computer list. TXT" user stores the target machine name, one per line. This file is the only one that needs to be edited manually by the administrator (the other is to download the patch and copy it to the controller).


$PC = Get-Content("C:\scripts_wusa\computer_list.txt")
$FileMSU = Get-ChildItem C:\fix -Name
$CAB_PATH = "C:\fix_cab\"
wusa.exe "C:\fix\$FileMSU" /extract:$CAB_PATH
#Decompression process rest 90s
Start-Sleep -Seconds 90

$i = 0

foreach ($h in $PC){

Copy-Item -Path $CAB_PATH -Destination \\$h\C$\ -Recurse -Force

if ($h -eq $PC[-1]){
    Write-Progress -Activity "Progress display" -status "Processing last host $h !"
    Write-Output "Aggregate processing $i Console host,Transmission completed!"
    #Start-Sleep -Seconds 20
    Write-Progress -Activity "Progress display" -status "Processing section $i Console host $h ,Please wait patiently!"  -PercentComplete  ($i/$PC.count*100)


Step 2: run the remote installation script
There are two scripts in this step. One is used to perform the installation action, and the other is used to call the first one to perform remote operation.

It should be noted that after the domain administrator logs in to the controller, the remote operation does not need to be authenticated. For computers outside the domain, remote operation management Credential authentication must be provided through the parameter - Credential.

Installation script:


$FileCAB = Get-ChildItem  C:\fix_cab  *KB*.cab -Name
Foreach ($file in $FileCAB)
 Add-WindowsPackage -Online -PackagePath C:\fix_cab\$file  -NoRestart  

Remote call:


$PC = Get-Content("C:\scripts_wusa\computer_list.txt")
foreach ($h in $PC){

#Computers in the domain
Invoke-Command -ComputerName $h -FilePath C:\scripts_wusa\action_fix.ps1
#Domain free computer
#Invoke-Command -ComputerName $h -FilePath C:\action_fix.ps1 -Credential administrator
Write-Progress -Activity "Installation progress" -Status "Hosting $h Please wait patiently for the patch to be installed!" -PercentComplete ($i/$PC.Count*100)


Step 3: remote installation result verification

There are also two scripts in this step. One is used to perform the check action, and the other is used to call the first one to perform the remote operation.

Check the installation results:


$FileCAB = Get-ChildItem  C:\fix_cab  *KB*.cab -Name

Function Get_fix()
     foreach ($i in $FileCAB){
        $KB = $i.Split("-")[1]
        Get-hotfix | where {$_.HotFixID -eq $KB }


Remote call:


$PCs = Get-Content("C:\scripts_wusa\computer_list.txt")

foreach ($h in $PCs)
    $result = Invoke-Command -ComputerName $h -FilePath C:\scripts_wusa\get_fix.ps1
    if ($result){
        Write-Output "$h  install Sucess!"
        Write-Output "$h  install Failure!"  


Look at the operation results of step 3 (you can only print the hosts that failed to install, after all, we are more concerned about the ones that failed to install):

After the patch is installed successfully, the rest is that the administrator can choose a reasonable time to restart the server, or restart the server in batches remotely through powershell. [Restart-Computer -ComputerName pc-1,pc-2,pc-N -Force]

Tags: Windows REST

Posted on Fri, 01 Nov 2019 21:01:36 -0700 by jmakeig