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