Jere's Techblog

Jobs on PowerShell

Certain processes can take a long time. As an example, if you want to search a specific event log trough several servers.
To counteract this, Powershell has introduced “Jobs, Workflows and Foreach-Parallel”. It should be well estimated what you can use where best. Personally, I like to rely on jobs when it comes to remote querying / remote invocation.

There is a sensational blogpost by Harry Eagles bout the topic:

I would like to show a small example of how I use Jobs to read out Eventlogs about several machines. In the example, it is only checked if the corresponding log exists or was written in the last 45 minutes. For 128 servers I needed 30 minutes with this parallel Task. I killed the sequential script after 6 hours …

#by J.Kühnis
$Servers = @(

Remove-Job *
$outputArray = @()

Foreach ($Server in $Servers){

Start-Job -Name $Server -ArgumentList $Server{
    IF(Get-EventLog -LogName System -InstanceId '12306' -After (Get-Date).AddMinutes(-45) -ComputerName $servername -ErrorAction Ignore){
    Write-Output"$servername  true"
    Write-Output "$servername  false"


While (Get-Job -State "Running") {
    write-host "Jobs Running"  (Get-Job).count
    Start-Sleep 2
write-host "Jobs completed, getting output"

Get-Job | ForEach-Object {
    $a = Receive-Job $_.Id
    $outputArray += $a

#Use the variable $outputArray to get or export the Outputdata
Continue reading...

local & remote msu patchfile installation with Powershell / Windows Update Standalone Installer

Windows Update Standalone Installer

The patch installation is done via wusa.exe.

The following print screen shows the parameters of the Windows Update Standalone Installer.

local installation

As far as I know, the patch cannot be installed with native powershell, means we have to address the wusa.exe in powershell. Of course Powershell is a nice way to automate the whole process.

In the following example, the patch is copied from a UNC share and installed locally.

$patchname = "kb13245.msu"

$patchsource = "\\some\unc\path\kb13245.msu"

Copy-Item $patchsource -Destination "$env:SystemDrive\Temp" -Force

wusa.exe "$env:SystemDrive\Temp\$patchname" /quiet

remote installation

I was not able to run wusa.exe remotely, any tests with workflows, Remotepowershell (Invoke-Command CMDLeet) failed. Even triggering a localy copied batch file caused problems. The wusa.exe process was executed with the correct parameters but aborted after a few seconds.
Even with an Invoke-Command and the parameter -Wait it didn’t work.

Probably it’s because you intervene in the system and perform an unauthorized action.

With the PSExec.exe it is possible to start the process remotely.

The principle is very simple, you have to copy the patch to the appropriate target computer. Then PSExec starts a remote process on the target computer and executes the wusa.exe with the corresponding parameters. The wusae.exe must point to the path where the patch was copied.

#16.05.2019 by JKU
$Hotfix = 'kb-12345.msu'
$HostName = 'F.Q.D.N'
$DestinationPath = "\\$Hostname\c$\Temp\"

Copy-Item C:\temp\$Hotfix -Destination $DestinationPath
#Start Process with PSExec.exe
& C:\Temp\PsExec.exe -accepteula -s  \\$HostName wusa C:\Temp\$Hotfix /quiet /norestart

And so you can distribute a patch for multiple computers with a simple iteration.

full remote automation

There are of course several ready-made scripts to install multiple patches on multiple computers. The script which I use in the company environment, I have inserted below. The code does not come from me, but from the following forum post:


$RootHotfixPath = 'Patches\'
$Hotfixes = @('KB3125574_x64.msu')
$Servers = Get-Content 'MachineList.txt'

foreach ($Server in $Servers)
    Write-Host "Processing $Server..."

    $needsReboot = $False
    $remotePath = "\\$Server\c$\Temp\Patches\"
        if( ! (Test-Connection $Server -Count 1 -Quiet)) 
        Write-Warning "$Server is not accessible"

        if(!(Test-Path $remotePath))
        New-Item -ItemType Directory -Force -Path $remotePath | Out-Null
    foreach ($Hotfix in $Hotfixes)
        Write-Host "`thotfix: $Hotfix"
        $HotfixPath = "$RootHotfixPath$Hotfix"

        Copy-Item $Hotfixpath $remotePath
        # Run command as SYSTEM via PsExec (-s switch)
        & C:\Windows\PsExec -s \\$Server wusa C:\Temp\Patches\$Hotfix /quiet /norestart
        write-host "& C:\Windows\PsExec -s \\$Server wusa C:\Temp\Patches\$Hotfix /quiet /norestart"
        if ($LastExitCode -eq 3010) {
            $needsReboot = $true

    # Delete local copy of update packages
    Remove-Item $remotePath -Force -Recurse

        Write-Host "Restarting $Server..."
        Restart-Computer -ComputerName $Server -Force -Confirm
Continue reading...