Powershell pass variable to start-job
within powershell I'd like to learn the best way to call a variable to a start job so I don't have to edit the script for each server as it will be specific based on the client I've placed my script on.
$Servername = 'Server1'
$pingblock = {
pathping $servername | Out-File C:\client\PS\ServerPing.TXT
}
start-job $pingblock
when I run my code above I just get a file with the help as if I forgot the specify the $servername.
Use the -ArgumentList
parameter on Start-Job
e.g.:
Start-Job -Scriptblock {param($p) "`$p is $p"} -Arg 'Server1'
In your case:
$pingblock = {param($servername) pathping $servername | Out-File C:\...\ServerPing.txt}
Start-Job $pingblock -Arg Server1
To complement Keith Hill's helpful answer with a PSv3+ alternative:
The $using:
scope modifier can be used to reference the values of variables in the caller's scope inside the script block passed to Start-Job
, as an alternative to passing arguments (by default, a script block executed as a background job does not see any of the caller's variables or other definitions):
$Servername = 'Server1'
Start-Job { "Target: " + $using:ServerName } | Receive-Job -Wait -AutoRemoveJob
The above yields:
Target: Server1
Note:
-
The same technique can be used when passing a script block:
-
to
Invoke-Command
for remote execution - see this question. -
to
Start-ThreadJob
, available (by default) in PowerShell (Core) v6+ -
to
ForEach-Object -Parallel
, available in PowerShell (Core) v7+
-
-
Note that, as with
-ArgumentList
, it is only variable values that are being passed, not the variables themselves; that is, you cannot modify the caller's variables that way.[1]
[1] However, the thread-based concurrency features - Start-ThreadJob
and ForEach-Object Parallel
- permit indirect modification, namely if the variable value at hand happens to be an instance of a (mutable) .NET reference type, such as a hashtable. Note that taking advantage of that may require additional, nontrivial effort to make the concurrent modifications thread-safe.
Some other ways, $args and $input. This goes for invoke-command too, which I think uses the same mechanism. The $input method works in an unexpected way with arrays.
start-job { $args[0] } -args hi | receive-job -wait -auto
hi
echo hi | start-job { $input } | receive-job -wait -auto
hi
echo hi there | start-job { $input.gettype() } | receive-job -wait -auto
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
False False <GetReadEnumerator>d__20 System.Object
For arrays, it's probably better to use a foreach-object (%) loop instead, so it runs on each array item in parallel. See also start-threadjob or foreach-object -parallel in powershell 7. There's actually no -throttlelimit option to start-job, so use with care.
echo yahoo.com facebook.com |
% { $_ | start-job { test-netconnection $input } } |
receive-job -wait -auto | select * -exclude runspaceid,pssourcejob* | ft
ComputerName RemoteAddress ResolvedAddresses PingSucce
eded
------------ ------------- ----------------- ---------
yahoo.com 74.6.143.25 {74.6.143.25,...} True
facebook.com 31.13.71.36 {31.13.71.36} True