Summary

How to utilize local variables in an invoke-command statement.

Issue

Invoke-Command is a great way of executing code small pieces of code on remote devices; however this is often required as part of an iterative loop executing variations of the same code i.e. using parameters/variables.

If you run the following code that utilizes local variables on a remote machine you will be surprised by the result

$var1 = "test1"
$var2 = "test2"
$var3 = "test3"
$computerName = $env:COMPUTERNAME
invoke-command -computername $computerName -ScriptBlock{
write-host "Variable 1 is:" $var1
write-host "Variable 2 is: "$var2
write-host "Variable 3 is: "$var3
}

All of the variables remain blank.
Variable 1 is:
Variable 2 is:
Variable 3 is:

This is because the script is looking for local instances of the variables declared i.e. $var1, var2, var3.

We can validate this by checking the properties of these variables

$var1 = "test1"
$computerName = $env:COMPUTERNAME
invoke-command -computername $computerName -ScriptBlock{
$var1 | Get-Member
}

This returns an error indicating the variable does not exist on the remote system:

You must specify an object for the Get-Member cmdlet.
+ CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException
+ FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand
+ PSComputerName : CONFIGMONKEYTESTVM

Resolution

So what do we do if we want to pass local variables to a remote system?
Digging into the Microsoft documentation we use the parameter -ArgumentList
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-6

-ArgumentList
Supplies the values of local variables in the command. The variables in the command are replaced by these values before the command is run on the remote computer. Enter the values in a comma-separated list. Values are associated with variables in the order that they are listed. The alias for ArgumentList is Args.

The above shows we can pass any variable to Invoke-Command under the parameter -ArgumentList; and it is referenced using an array of “Args” using an alias $Args.

Re-writing the code passing arguments will look as below:
$var1 = "test1"
$var2 = "test2"
$var3 = "test3"
$computerName = $env:COMPUTERNAME
invoke-command -computername $computerName -ScriptBlock{
write-host "Variable 1 is:" $args[0]
write-host "Variable 2 is:" $args[1]
write-host "Variable 3 is:" $args[2]
} -ArgumentList $var1, $var2, $var3

Running the code now outputs the expected result:

Variable 1 is: test1
Variable 2 is: test2
Variable 3 is: test3