The following snippet assumes that a Windows machine has access to download Microsoft patches directly from the Internet
# applyMsuPatch.ps1
# Assuptions:
# a. Jump host has access to download from Microsoft
# b. Jump host has WinRM and SMB access to target Windows machine
# User inputs
$fileURL="http://download.windowsupdate.com/d/msdownload/update/software/secu/2022/01/windows10.0-kb5009546-x64_d3ab97e9f811d7bf19c268e5e6b5e00e92e110ed.msu"
$stageFolder='C:\Temp\'
$computerName='testWindows'
# Autogen variables
$fileName=[regex]::match($fileURL,'[^/\\&\?]+\.\w{3,4}(?=([\?&].*$|$))').value
$translatedVolume=[regex]::match($stageFolder,'^(\w)\:').captures.groups[1].value+'$'
$translatedFoldername=[regex]::match($stageFolder,'\:(.*)$').captures.groups[1].value
$remoteSmbPath=join-path $('\\'+$computerName+"\$translatedVolume") $translatedFoldername
try{
# Download the file directly onto target server's staging folder
Import-Module BitsTransfer
if(!(test-path $remoteSmbPath)){$null=mkdir $remoteSmbPath}
# Start-BitsTransfer -Source $fileURL -Destination $output
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Start-BitsTransfer -Source $fileURL -Destination $remoteSmbPath
}catch{
write-warning $_
return $false
}
$psSession=new-pssession $computerName
if($psSession.State -eq 'Opened'){
invoke-command -Session $psSession -ScriptBlock{
param(
$stageFolder,
$filename
)
# Generate variables
$msuFile=join-path $stageFolder $fileName
$logFile=join-path $stageFolder "$([System.IO.Path]::GetFileNameWithoutExtension($msuFile)).log"
$shortFilename=[regex]::match($filename,'^(.*)_').captures.groups[1].value
# $expectedCabFile=join-path $stageFolder "$([System.IO.Path]::GetFileNameWithoutExtension($msuFile)).cab"
$expectedCabFile=join-path $stageFolder "$shortFilename.cab"
$ErrorActionPreference='Stop'
try{
# Extracting MSU into CAB
# if(!(test-path $stageFolder)){$null=mkdir $stageFolder}
$command="expand -F:* $msuFile $stageFolder"
write-host $command
invoke-expression $command
# Check system
$kb=[regex]::match($msuFile,'-(kb\d+)-').captures.groups[1].value.toupper()
$hotFixes=Get-Hotfix
$alreadyInstalled=$kb -in $hotFixes
# Use DISM to apply the patch
if(!$alreadyInstalled){
dism.exe /online /add-package /packagepath:$expectedCabFile /quiet /norestart /logpath:$logFile
# Alternative method of applying the patch
# Start-Process -FilePath "wusa.exe" -ArgumentList "$output /quiet /norestart" -Wait
}else{
write-host "$kb is already installed on $env:computername"
}
rm C:\Temp\*.*
}catch{
write-warning $_
}
} -Args $stageFolder,$fileName
Remove-PSSession $psSession
}else{
write-warning "Unable to connect to $computername via WinRM"
}
Categories: