PowerShell Start-Job

The Start-Job Cmdlet is a fast and extremely simple means of spawning asynchronous threads. Why do we care? Because performing parallel work is faster and our time is important. If I want to inventory the Windows services running on every server in my company, I don’t want to wait for each one to return data, one at a time. Before I move on with the Start-Job Cmdlet, there are two other notable concurrency methods that I won’t be covering in this post; runspaces, and work-flows.

To demonstrate I will use the Windows services task that I mentioned above. Here is how we can find a list of all of the Windows services running on a single server.

Get-Service | Where-Object { $_.Status -eq 'Running' }

The simplest form of this Cmdlet is to pass in a script block.

$jobs = @();
$jobs += Start-Job -ScriptBlock `
{ Get-Service | Where-Object { $_.Status -eq 'Running' } }

The Start-Job Cmdlet returns a System.Management.Automation.Job object which can be used for receiving the job output, waiting for thread completion, and disposing of the thread.

#wait for all threads in the array to complete and print their output.
$jobs | Wait-Job | Receive-Job
#dispose of your jobs
$jobs | Remove-Job

If you would like to pass in parameters to your script block you can create an argument array. In the script block you must use the $arg[n] syntax, as shown.

$args = @()
$args += 'Running'
$jobs += Start-Job -ScriptBlock { Get-Service | Where-Object { $_.Status -eq "$($args[0])" } } -ArgumentList $args

Finally, I will demonstrate how to initialize the job with a script. This is often used to pre-load functions to be used in the script block. Let us say that I’ve created a function and saved it as a scriptblock.

[scriptblock]$func = {
function Get-RunningService
Get-Service | Where-Object { $_.Status -eq "Running" }

Then we can create our job like so.

$jobs += Start-Job -ScriptBlock { Get-RunningService } `
-InitializationScript $func

When we bring this all together we have an easy to remember asynchronous process.

$jobs = @();
$sb = [scriptblock] { write-host "args = $($args[0])"; Invoke-Command -ComputerName ($args[0]) -ScriptBlock { Get-RunningService } }

foreach ($svr in ($servers.Name))
$args = @($svr);
$jobs += Start-Job -ScriptBlock $sb `
-InitializationScript $func `
-ArgumentList $args
$jobs | Wait-Job | Receive-Job
$jobs | Remove-Job

This article has 2 comments

  1. […] Understanding Cross-Database Transactions in SQL Server The Read Uncommitted Isolation Level PowerShell Start-Job Forced parameterization to the rescue SQL Server Query Performance After Index Maintenance for […]

  2. […] is very important, especially if your server list is quite large. To achieve this, we will use the Start-Job […]

Leave a Reply