Some background information, an overview of the existing, helpful answers, plus an additional solution:
The automatic $LASTEXITCODE
variable, which reflects the exit code reported by the most recently executed external program or *.ps1
script (assuming it uses exit
), is only available in a given session, and therefore not directly available to the caller in the context of executing code with Invoke-Command
in a remote session.
- As an aside: The same problem applies to background jobs, which also don't offer a direct way to query the background session's
$LASTEXITCODE
from the outside.
GitHub issue #5422 suggests adding a .LastExitProperty
to job objects to address this limitation, but such a solution wouldn't work directly with Invoke-Command
, which only returns command output by default; it does, however, have an -AsJob
switch that returns a job object too, so Invoke-Command
could indirectly benefit from this improvement (requiring a change in invocation logic and output collection, however).
It is possible to make a remote session output its $LASTEXITCODE
value as data, but that "pollutes" the stream of output objects from that session.
The existing answers work around that as follows:
Jules Clements' helpful answer offers a pragmatic, single-call solution that uses a throw
statement to convey the exit code via an error that must be caught.
Paul Williams' helpful answer, which builds on Joey's, offers a workaround based on explicitly creating a remote session and then making two Invoke-Command
calls in that session: one to retrieve the command output, and the second to query the $LASTEXITCODE
value.
Another single-call option, which deals with the "pollution" problem differently:
Output the remote session's $LASTEXITCODE
value at the very end, as an additional output object following the actual command output.
On the caller side, split the collected output into the last object and all objects before it, which gives you the exit code as well as the "unpolluted" command output.
# Invoke a remote command and capture all its output.
# (This example uses "loopback" remoting, targeting the local machine (".")
# The local machine must be set up for remoting AND you must run from an
# ELEVATED session.)
$output = Invoke-Command -ComputerName . {
# Sample commands that produce output and
# also cause $LASTEXITCODE to be set.
Get-Date
Get-Item /
cmd /c 'exit 5' # Sets $LASTEXITCODE to 5
# Finally, also output the $LASTEXITCODE value.
$LASTEXITCODE
}
# Now split the collection of output objects into the actual command
# output and the added $LASTEXITCODE value.
# Afterwards $output contains the actual output,
# and $exitCode the exit code.
$output, $exitCode = $output[0..($output.Count-2)], $output[-1]
Note the relatively cumbersome collection-splitting code - $output, $exitCode = $output[0..($output.Count-2)], $output[-1]
, because, as of PowerShell 7.3.4, there's no array-slicing support for all-but-the-last-N logic.
GitHub issue #7940 proposes adding such support, which would simply the expression to something like:
$output, $exitCode = $output[0..@-2], $output[-1]