PowerShell jobs are an integral part of PowerShell. This handy feature allows administrators to run commands asynchronously. Asynchronous commands allow for parallel execution cutting down on time and fully leveraging compute power.
With version 7, PowerShell continues its support of jobs giving scripters an efficient way to build efficient, speedy code.
To demonstrate how jobs work in PowerShell 7, let’s start off with a simple example.
Perhaps you have a script that pings a remote server. If that server returns a ping response, you know it’s online and can then perform some other kind of action. For this example, let’s assume it’s simply placing a file on the remote system.
If you’ve performed a ping sweep on many remote servers at once, you’d know that some servers will return a response quickly while some will seemingly take forever. If running a script like this asynchronously, the quick-to-respond servers may have to wait for the slow-to-respond servers.
There’s no reason for one server to wait on another since there are no dependencies between the tasks. This is a perfect use case for jobs.
Let’s start with the example script below. This script allows the user to provide one or more server names as input via the ServerName
parameter. It then attempts to ping each one using the Test-Connection
cmdlet. If the ping is successful, it then creates a file called newfile.txt in the root of the C drive on the remote system.
param (
[Parameter(Mandatory)]
[string[]]$ServerName
)
$ServerName | ForEach-Object {
if (Test-Connection -ComputerName $_ -Quiet -Count 1) {
$null = New-Item -Path "\\$_\c$\newfile.txt" -ItemType File
}
}
When you run this script, it’s going to process each remote system one by one. This process may take a long time if you pass hundreds of remote system names to it.
If the script were called New-ServerFile.ps1, you could run the script like the example below.
PS7> .\New-ServerFile.ps1 -ServerName SRV1,SRV2,SRV3
Instead, let’s modify it to use PowerShell 7’s jobs feature.
The first step is to figure out what code needs to be run inside of each job and when to create a new job. Technically, you could encapsulate this entire script in a single job. That wouldn’t do much good because each task would still be performed asynchronously.
Instead, you should create a new job for each remote server. This will prevent the script from hanging up on any slow-to-respond servers. To do that, place the Start-Job
cmdlet inside of the Foreach loop to execute one job per server.
param (
[Parameter(Mandatory)]
[string[]]$ServerName
)
$ServerName | ForEach-Object {
$block = {
if (Test-Connection -ComputerName $_ -Quiet -Count 1) {
$null = New-Item -Path "\\$_\c$\newfile.txt" -ItemType File
}
}
Start-Job -Scriptblock $block
}
As-is, this code will not work though. When you create a job, you create a new thread that’s not aware of any variables created in the current thread. You must pass the server name to the job’s code. To do so, you’ll have to use the ArgumentList
parameter.
Along with the usage of ArgumentList
with the Start-Job
cmdlet, the script now uses $args[0]
instead of the local variable $_
. This is how PowerShell knows to look for the first value passed to the ArgumentList
parameter.
param (
[Parameter(Mandatory)]
[string[]]$ServerName
)
$ServerName | ForEach-Object {
$block = {
if (Test-Connection -ComputerName $args[0] -Quiet -Count 1) {
$null = New-Item -Path "\\$($args[0])\c$\newfile.txt" -ItemType File
}
}
Start-Job -Scriptblock $block -ArgumentList $_
}
When you run this script, you’ll now immediately see a few jobs returned depending on how many server names you’ve provided.
PS7> .\New-ServerFile.ps1 -ServerName SRV1,SRV2,SRV3
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Running True localhost ....
2 Job2 BackgroundJob Running True localhost ....
3 Job3 BackgroundJob Running True localhost ....
There will now be a job running for each server. During the running time, you can now use the Get-Job
cmdlet to inspect the status of each job.
PS> Get-Job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Completed True localhost ....
2 Job2 BackgroundJob Running True localhost ....
3 Job3 BackgroundJob Running True localhost ....
Each server will be processed as soon as PowerShell can bring up a new job.
In this example, the script built does not return any output. If it would have, you could have used the Receive-Job
cmdlet to return any output from the script.
PS> Get-Job | Receive-Job
In this article, you learned the basics of PowerShell jobs with a real-world use case. But, there’s a whole lot more to using jobs in PowerShell 7. Make sure to check out on PowerShell hub on Petri for more PowerShell tutorials.