When invoking remote commands, one often wants to use local values as arguments to the remote command. This post explains how to do that.

Hardcoded values

Let's start with a simple example. Below you can see how Get-Process is invoked in a remote session when the Id parameter is set to 0:

PS> $s = New-PSSession -Computer localhost
PS> Invoke-Command -Session $s -Script { Get-Process -Id 0 }
 
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName     PSComputerName
-------  ------    -----      ----- -----   ------     -- -----------     --------------
      0       0        0         24     0               0 Idle            localhost

Passing local values

What if we want to calculate the value of the Id parameter programmatically, instead of hard-coding it to a particular integer? Well, script blocks can declare parameters and the param block can be used to pass a value to the command that is being invoked. In the following example, we declare the $processId parameter in the script block and then use the Args parameter of Invoke-Command to set the value of $processId to $pid, which is the process ID of the current Windows PowerShell process.

PS> $s = New-PSSession -ComputerName localhost
PS> Invoke-Command -Session $s -Script { param($processId) Get-Process -Id $processId } -Args $pid
 
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName     PSComputerName
-------  ------    -----      ----- -----   ------     -- -----------     --------------
    533      13    46160      37456   198     6.07   9488 powershell      localhost

The example above demonstrates that Get-Process was invoked with a process id of the local Windows PowerShell console.  Invoke-Command takes whatever you pass in the Args parameter and binds it to script block's parameters in the remote session. Note that above I simply passed in a value of a local $pid variable, but any local expression (not just a simple variable dereference) can be used instead.

Referring to remote variables

A different behavior takes place when you don't declare script block parameters using the "param" keyword:

PS> $s = New-PSSession -ComputerName localhost
PS> Invoke-Command -Session $s -Script { Get-Process -Id $pid }
 
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName     PSComputerName
-------  ------    -----      ----- -----   ------     -- -----------     --------------
    329      10    21216      33524   154     7.63   6596 wsmprovhost     localhost

In this case $pid is an unbound variable and Windows PowerShell assumes that it refers to the $pid variable in the remote session. Therefore, as you can see Get-Process returns the process that hosts the remote session.

Magic $args variable

What happens when you use the Args parameter of Invoke-Command cmdlet, but don't declare any parameters in the script block? Local values are still passed to the remote session and in this case are bound to the $args variable. This can save some typing:
PS> $s = New-PSSession -ComputerName localhost
PS> Invoke-Command -Session $s -Script { Get-Process -Id $args[0] } -Args $pid

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName                    PSComputerName
-------  ------    -----      ----- -----   ------     -- -----------                    --------------
    808      46    76840      81876   596     5.05   7824 powershell                     localhost

Thanks,

Lukasz Anforowicz [MSFT]
Windows PowerShell Developer
Microsoft Corporation