PowerShell: Download and Apply Windows Patch KB

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]
        # $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"
}

Leave a Reply

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