The syntax below absolutely works to stop a process on a remote computer:
$hostname = 'PC1'
$process = 'program1*'
$Session = New-PSSession $Hostname
Invoke-Command -Session $Session -ScriptBlock {param($process) Stop-Process -ProcessName $process -Force} -ArgumentList $process
$Session | Remove-PSSession
However, in Jenkins, I parameterized hostname and process, so the user enters the input hostname and process, and Jenkins creates the two variables $env:hostname and $env:process. This is not working well, the argument is not being passed onto Stop-Process:
$session = New-PSSession $env:hostname
Invoke-Command -Session $session -ScriptBlock {param($env:process) Stop-Process -ProcessName $env:process -Force} -ArgumentList $env:process
$Session | Remove-PSSession
The error I'm getting is
Cannot bind argument to parameter 'Name' because it is null.
At C:\Users\user.name\AppData\Local\Temp\jenkins10480966582412717483.ps1:25 char:1
+ Invoke-Command -Session $session -ScriptBlock {param($env:process) St ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Stop-Process], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.StopProcess
Command
+ PSComputerName : pc1
Build step 'PowerShell' marked build as failure
Finished: FAILURE
I know this has something to do with quotes, please give me a hand, thank you!
Don't change anything about your Invoke-Command
call except the input argument, i.e. what you pass to -ArgumentList
:
Invoke-Command `
-Session $Session `
-ScriptBlock { param($process) Stop-Process -ProcessName $process -Force } `
-ArgumentList $env:process
Don't ever use an environment-variable reference to define a parameter variable: In fact, PowerShell should not even allow such parameter declarations, but does as of 7.2.x - see GitHub issue #18401.param($env:process)
The name of the parameter variable, as declared inside the param(...)
block, is unrelated to whatever value you pass to it via -ArgumentList
(i.e., it is unrelated to what the name of the variable is that you happen to be using to pass that value, and whether that variable is a regular (shell-only) variable or an environment variable), and inside the -ScriptBlock
argument you must only refer to the value via the parameter variable.
See the relevant section of the conceptual about_Functions help topic, which, with respect to the params(...)
method of declaring parameters, applies equally to script blocks ({ ... }
).
Note: An alternative to passing values as arguments to a remotely executing script block, via Invoke-Command
's -ArgumentList
parameter, is to use the $using:
scope to refer to the values of variables in the caller's scope directly in the script block, as shown in Dennis' answer.
In PowerShell (Core) 7+, you can apply the $using:
scope directly to an environment-variable reference, which - unfortunately - doesn't work in Windows PowerShell, due to a bug:[1]
# PS 7+ alternative.
# In Windows PowerShell, stick with -ArgumentList or use Dennis'
# approach via an intermediate, regular variable.
Invoke-Command `
-Session $Session `
-ScriptBlock { Stop-Process -ProcessName $using:env:process -Force }
[1] It works with Start-Job
, but curiously not with remoting Invoke-Command
calls.