A background job is an asynchronous execution unit that runs a command or script in the background without blocking the current interactive session. PowerShell's *-Job cmdlets let you start, monitor, retrieve results from, and clean up background jobs. Background jobs enable concurrent script execution, allowing long-running tasks such as network scans, file transfers, or bulk administrative queries to process in parallel while the primary console remains responsive.
Starting a Job with Start-Job
The Start-Job cmdlet launches a script block or external script as an independent, non-interactive background job on the local system. It immediately returns control to the console.
Start-Job -ScriptBlock { Get-Process }where -ScriptBlock is the code to execute in the background job.
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
7 Job7 BackgroundJob Running True localhost Get-ProcessStarting a Job with a Name
Assign a friendly name using -Name to make jobs easier to identify later.
Start-Job -Name "ProcessSnapshot" -ScriptBlock { Get-Process }where -Name is a custom identifier for the job, used instead of the auto-generated Job1, Job2, etc.
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
9 ProcessSnapshot BackgroundJob Running True localhost Get-ProcessStarting a Job That Runs a Script File
Use -FilePath to run an entire .ps1 script as a background job.
Start-Job -Name "BackupJob" -FilePath "C:\Scripts\backup.ps1"where -FilePath is the path to the .ps1 script to execute as a background job.
Detailed Example
$job = Start-Job -Name "DataProcessor" -ScriptBlock {
Start-Sleep -Seconds 3
Get-Service -Name "wuauserv" | Select-Object -Property Name, Status, StartType
}
$job | Select-Object -Property Id, Name, State, HasMoreData, Locationwhere:
Start-Jobis the core cmdlet that launches commands into a background session.-Nameassigns a user-friendly identifier to the job instance.-ScriptBlockcontains the code statements to be executed asynchronously.Idis the system-assigned sequential integer identifier for the job.Stateis the current operational state (e.g.,NotStarted,Running,Completed,Failed).HasMoreDatais a boolean indicating whether the job holds unread output streams.Locationspecifies the host where the job is executing (localhostfor local jobs).
./start-job-demo.ps1Id : 11
Name : DataProcessor
State : Running
HasMoreData : True
Location : localhostMonitoring Jobs with Get-Job
The Get-Job cmdlet lists all jobs in the current session along with their state.
Get-JobId Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
1 ProcessSnapshot Completed True localhost Get-Process
2 BackupJob Running True localhost C:\Scripts\backup.ps1where:
Stateis the job's current status:NotStarted,Running,Completed,Failed, orStopped.HasMoreDataindicates whether the job has output waiting to be collected.
Waiting for a Job with Wait-Job
The Wait-Job cmdlet pauses script execution until a specified job (or jobs) finishes running.
Wait-Job -Name "BackupJob"where -Name is the name of the job to wait for.
Wait-Job -Name "BackupJob" -Timeout 60where -Timeout is the maximum number of seconds to wait before returning control, even if the job hasn't finished.
Waiting for Multiple Jobs
The Wait-Job cmdlet can monitor several jobs at once. By default, it waits for all specified jobs to conclude. Supplying the -Any switch instructs Wait-Job to resume execution as soon as the first job in the list finishes.
$job1 = Start-Job -Name "WorkerA" -ScriptBlock { Start-Sleep -Seconds 2; "Worker A Finished" }
$job2 = Start-Job -Name "WorkerB" -ScriptBlock { Start-Sleep -Seconds 4; "Worker B Finished" }
Write-Output "Waiting for background workers to finish..."
$completed = Wait-Job -Job $job1, $job2 -Timeout 10
$results = Receive-Job -Job $completed
Write-Output $resultswhere:
Wait-Jobblocks the parent shell until the monitored jobs finish execution.-Jobspecifies one or more job objects or IDs to monitor.-Timeoutis the maximum duration (in seconds) the shell will wait before returning control, preventing endless blocking if a background worker hangs.Receive-Jobemits the results collected across all completed job objects passed to it.
./wait-job-demo.ps1Waiting for background workers to finish...
Worker A Finished
Worker B FinishedRetrieving Job Results with Receive-Job
The Receive-Job cmdlet collects the output produced by a background job.
$job = Start-Job -ScriptBlock { Get-Process | Where-Object { $_.WS -gt 100MB } }
Wait-Job -Job $job
Receive-Job -Job $jobwhere:
$jobstores the job object returned byStart-Job.Wait-Job -Job $jobblocks until the job finishes.Receive-Job -Job $jobretrieves and displays the job's output.
./run-and-receive-job.ps1Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
21 Job21 BackgroundJob Completed True localhost Get-Process | Where-O...
Id : 3636
Handles : 2015
CPU : 7.625
Name : explorer
Id : 1760
Handles : 0
CPU :
Name : Memory Compression
Id : 10092
Handles : 892
CPU :
Name : MsMpEng
Id : 3572
Handles : 913
CPU : 5.25
Name : powershellJob Lifecycle Example
Get-Job -Name "DataProcessor"
$result = Receive-Job -Name "DataProcessor" -Keep
$result
Remove-Job -Name "DataProcessor"where:
Get-Joblists all background jobs initiated in the current session and displays their current states.Receive-Jobfetches the standard output, warning, error, and verbose streams generated inside the background script block.-Keeppreserves the output cache in memory. By default,Receive-Jobclears the job's internal buffer upon retrieval; adding-Keepallows data to be read multiple times.Remove-Jobdeletes the job entry and its associated runtime memory from the current shell session.
./job-lifecycle.ps1Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
11 DataProcessor BackgroundJob Completed True localhost ...
Name : wuauserv
Status : Running
StartType : Manual
RunspaceId : 09822064-e095-4343-b975-703ec467b9b6Removing Jobs with Remove-Job
The Remove-Job cmdlet deletes a job object from the session once it's no longer needed.
Remove-Job -Name "BackupJob"where -Name is the name of the job to remove.
To clean up all completed jobs at once:
Get-Job | Where-Object { $_.State -eq "Completed" } | Remove-JobPractical Usage: Running Multiple Tasks in Parallel
A common use case for jobs is running several independent, time-consuming tasks concurrently instead of sequentially.
$servers = "SRV01", "SRV02", "SRV03"
foreach ($server in $servers) {
Start-Job -Name $server -ScriptBlock {
param($computer)
Test-Connection -ComputerName $computer -Count 2 -Quiet
} -ArgumentList $server
}
Get-Job | Wait-Job
foreach ($server in $servers) {
$result = Receive-Job -Name $server
Write-Output "$server reachable: $result"
}
Get-Job | Remove-Jobwhere:
-ArgumentListpasses external values into the job's script block as parameters.Get-Job | Wait-Jobwaits for all started jobs to complete before proceeding.Receive-Job -Name $serverretrieves the result for each individual server's job.
./parallel-server-check.ps1SRV01 reachable: True
SRV02 reachable: True
SRV03 reachable: FalsePractical Administration: Parallel Connectivity Sweep
Administrators often need to probe multiple remote endpoints simultaneously. Running connection tests sequentially causes severe delays, whereas jobs allow checking all targets in parallel.
$servers = @("127.0.0.1", "10.0.0.1", "localhost")
$jobs = foreach ($server in $servers) {
Start-Job -Name "Ping_$server" -ArgumentList $server -ScriptBlock {
param($target)
$ping = Test-Connection -ComputertName $target -Count 1 -Quiet
[PSCustomObject]@{
Node = $target
Online = $ping
CheckAt = (Get-Date -Format "HH:mm:ss")
}
}
}
$null = Wait-Job -Job $jobs -Timeout 5
$report = Receive-Job -Job $jobs
Remove-Job -Job $jobs -Force
$report | Format-Table -AutoSizewhere:
param($target)accepts inputs passed into the background script block from the parent scope.-ArgumentListmaps outer loop variables into the isolated job's parameter block.Test-Connection -Quietreturns a fast$true/$falseboolean indicating ICMP echo reply.$null = Wait-Jobsuppresses the raw job status table emitted byWait-Job, keeping the console output clean.Remove-Job -Forceremoves the jobs even if an individual job failed or remained active past the timeout threshold.
./parallel-network-audit.ps1Node Online CheckAt
---- ------ -------
127.0.0.1 True 10:24:15
10.0.0.1 False 10:24:18
localhost True 10:24:15Jobs vs Runspaces
Start-Job runs each job in a separate process, which provides strong isolation but comes with higher memory and startup overhead. For lighter-weight parallelism, PowerShell also supports runspaces and Start-ThreadJob (from the ThreadJob module), which run concurrently within the same process using threads instead of separate processes.
PowerShell supports multiple paradigms for concurrent and parallel execution. Understanding the architectural differences between traditional background jobs and runspaces determines script scalability:
| Feature | Standard Jobs (Start-Job) |
Thread Jobs / Runspaces (Start-ThreadJob / ForEach-Object -Parallel) |
|---|---|---|
| Execution Model | Separate OS Process (powershell.exe / pwsh.exe) |
Separate Threads within the same host process |
| Memory Footprint | High (~30-60 MB per job instance) | Minimal (~1-2 MB per thread) |
| Startup Latency | Slow (requires full shell environment initialization) | Near-instantaneous thread execution |
| Process Isolation | Full isolation; crashed child process won't kill parent | Shared process; unhandled memory faults can crash host |
| Variable Access | Must pass variables explicitly via ArgumentList or $using: |
Can directly read shared objects and state |