You can install Servicing Stack Updates (SSU) for Windows Server 2016 and Windows Server 2019 using PowerShell, without downtime. Because they must be installed prior to your normal Windows Server security updates, you can install them anytime you want to during the day. Here’s a small PowerShell example to do so.

Servicing the Windows servicing stack

Keeping the servicing stack updated is crucial for the security of your Windows-based infrastructure. Unfortunately, it’s also becoming more of a challenge. This post helps you installing Windows servicing stack updates prior to Windows Updates, using PowerShell, and streamlines your workflow a bit.

But first, what exactly are Servicing stack updates, or SSU’s? Microsoft writes:

Servicing stack updates provide fixes to the servicing stack, the component that installs Windows updates. Additionally, it contains the “component-based servicing stack” (CBS), which is a key underlying component for several elements of Windows deployment, such as DISM, SFC, changing Windows features or roles, and repairing components. The CBS is a small component that typically does not have updates released every month.

Servicing stack updatesMicrosoft Docs

Protip: I mentioned Windows Component-Based Servicing and CBS log files in my blog post with 5 ways to clean up files and free up disk space in Windows Server.

In my monthly Windows Updates routine, mostly using WSUS, I use the following PowerShell snippets to install Servicing Stack Updates manually in a loop.

Read this too:   How to fix Joomla HTTP 500 errors after updates

First I approve all updates available and wait a moment for the updates to be downloaded to the WSUS client servers. In that moment of waiting, I write down the KB numbers for Server 2016 and Server 2019 Servicing Stack Updates (this month: KB4503537 for Server 2016 and KB4504369 for Server 2019.

Protip: ever wondered why Windows Server Update Services (WSUS) offers Flash updates for Windows Server? Here is how to uninstall and remove Adobe Flash Player in Windows Server.

The following step is to verify files are downloaded properly to the clients. Some parts can be further automated, but I always like to have a visual comfirmation of the results.

On one server I issue the following PowerShell Get-ChildItem cmdlet to search for the update-file and location in question:

PS C:\Users\janr> Get-ChildItem -Recurse C:\Windows\SoftwareDistribution\Download | ?{ $_.PSPath -like "*KB4503537*" }


    Directory: C:\Windows\SoftwareDistribution\Download\5781475735efb924887fcf8057a37977


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----         6/3/2019   7:16 PM       12020671 Windows10.0-KB4503537-x64.cab

Knowing this, I can verify the file path on all servers in my domain network:

foreach( $server in (Get-ADComputer -Filter {(enabled -eq $True) -and (OperatingSystem -like "Windows Server 2016*")}).DNSHostName ) {
	write-output $server;
	invoke-command -computername $server -scriptblock {
		if( Test-Path( "C:\Windows\SoftwareDistribution\Download\5781475735efb924887fcf8057a37977\Windows10.0-KB4503537-x64.cab" )) {
			Write-Output "Servicing Stack Update KB4503537 present."
			# &DISM.exe /Online /Add-Package /PackagePath:C:\Windows\SoftwareDistribution\Download\5781475735efb924887fcf8057a37977\Windows10.0-KB4503537-x64.cab
		}
	}
}

When all servers report the file is present, you can issue DISM to install the SSU’s .cab file:

foreach( $server in (Get-ADComputer -Filter {(enabled -eq $True) -and (OperatingSystem -like "Windows Server 2016*")}).DNSHostName ) {
	write-output $server;
	invoke-command -computername $server -scriptblock {
		if( Test-Path( "C:\Windows\SoftwareDistribution\Download\5781475735efb924887fcf8057a37977\Windows10.0-KB4503537-x64.cab" )) {
			&DISM.exe /Online /Add-Package /PackagePath:C:\Windows\SoftwareDistribution\Download\5781475735efb924887fcf8057a37977\Windows10.0-KB4503537-x64.cab
		}
	} -AsJob
}

Here I also used -AsJob to start the command asynchronously (async). Use Get-Job and Receive-Job to get information about running jobs. Change “Windows Server 2016*” to “Windows Server 2019*” to search for Server 2019 servers in your AD network.

Read this too:   .svc WCF web service returns 404 Not Found on IIS 8

Update 02-2020
Over time I automated the checks and SSU installation a bit. Here is an updated script that takes KB numbers as parameter.

[CmdletBinding()]
	Param(
		[Parameter(Mandatory = $true, Position = 0, HelpMessage="Windows Server OS version (2019, 2016, 2012)")]
		[string]$serveros,
		[Parameter(Mandatory = $true, Position = 1, HelpMessage="Servicing Stack Update KB number")]
		[ValidateScript({
			if (!($_ -match "KB")) {
				Throw "[!] the letters KB are missing"
			}
			else {
				$true
			}
		})]
		[string]$ssukb
	)

function installeer_SSU($servername, $kb) {
	invoke-command -computername $servername -scriptblock {
		$update = ((Get-ChildItem -Recurse C:\Windows\SoftwareDistribution\Download | Where-Object { $_.PSPath -like "*${using:kb}*" }) | Select-Object Name,DirectoryName)
		if(Test-Path("$($update.DirectoryName)\$($update.Name)")) {
			$process = Start-Process -NoNewWindow "c:\windows\system32\DISM.exe" -argument "/Online /Add-Package /PackagePath:$($update.DirectoryName)\$($update.Name)" -PassThru -Wait
			if($process.ExitCode -ne 0) {
				$a = "Installation process returned error code: $($process.ExitCode)"
				return $a
			}
			else {
				$a = "true, update installed"
				return $a
			}
		} else {
			$a = "$($update.DirectoryName)\$($update.Name) not found"
			return $a
		}
	}
}

function restart_WindowsUpdate($servername, $os) {
	invoke-command -computername $servername -scriptblock {
		&net stop wuauserv
		&net start wuauserv
		if($os -eq "2019" -or $os -eq "2016") {
			Start-Process -NoNewWindow "c:\windows\system32\UsoClient.exe" -argument "startscan" -Wait
			Start-Process -NoNewWindow "c:\windows\system32\UsoClient.exe" -argument "RefreshSettings" -Wait
		} else {
			Start-Process -NoNewWindow "c:\windows\system32\wuauclt.exe" -argument "/resetauthorization /DetectNow" -Wait
			Start-Process -NoNewWindow "c:\windows\system32\wuauclt.exe" -argument "/ReportNow" -Wait
		}
	}
}

if(($serveros -eq "2016") -or ($serveros -eq "2019")) {
	$serverversion = "Windows Server " + $serveros + "*"
	foreach($server in (Get-ADComputer -Filter "OperatingSystem -like '${serverversion}' -and enabled -eq '$true'").DNSHostName) {
		write-output "${server} -kb ${ssukb}"
		$b = installeer_SSU -servername "${server}" -kb "${ssukb}"
		$b
		restart_WindowsUpdate -server "${server}" -os "${serveros}"
	}
}

if($serveros -eq "2012") {
	foreach($server in (Get-ADComputer -Filter {(enabled -eq $True) -and (OperatingSystem -like "Windows Server 2012*")}).DNSHostName) {
		write-output "${server} -kb ${ssukb}"
		$b = installeer_SSU -servername "${server}" -kb "${ssukb}"
		$b
		restart_WindowsUpdate -server "${server}" -os "${serveros}"
	}
}

It may not be perfect but works for my situation. You can execute it for your Windows Server operating system versions like:

.\install-SSU.ps1 -serveros 2019 -ssukb KB4539571
.\install-SSU.ps1 -serveros 2016 -ssukb KB4540723
.\install-SSU.ps1 -serveros 2012 -ssukb KB4540725

The regular Windows Updates are installed using WSUS and PowerShell module PSWindowsUpdate. You can install this with NuGet:

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Confirm:$false -Force
Install-Module -Name PSWindowsUpdate -Confirm:$false -Force

I have a Dutch article about PSWindowsUpdate at ITFAQ.nl.

Read this too:   How to verify SMBv1 is disabled in Windows and Windows Server
0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

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