Search code examples
powershellpowershell-4.0powershell-remotingexit-codewrite-host

Capture Write-Host output and exit code from Invoke-Command on a Remote System


I have a script located on a remote system.

On the server "web12" under C:\tmp\hgttg.ps1:

 Write-Host "Don't Panic"
 exit 42

I invoke this script from my local box (both systems running v4.0) using Invoke-Command:

$svr = "web12"
$cmd = "C:\tmp\hgttg.ps1"
$result = Invoke-Command -ComputerName $svr -ScriptBlock {& $using:cmd}

This causes the following output when executed:

> $result = Invoke-Command -ComputerName $svr -ScriptBlock {& $using:cmd}
Don't Panic
> $result
>

($result is not set, output goes straight to the console, no good!) After much web searching and troubleshooting, I have come to some improvement:

> $result = Invoke-Command -ComputerName $svr -ScriptBlock {& $using:cmd; $LASTEXITCODE}
Don't Panic
> $result
42
>

Now I'm able to capture the return code, but the output still goes straight to the console. This is as far as I can get. On the long list of things I have already attempted, none of the following have worked:

  • Instead of '&' used 'Invoke-Expression' with -OutVariable out; New-Object -TypeName PSCustomObject -Property @{code=$LASTEXITCODE; output=$out}

This produces the output:

> $result
code           : 42
output         : 
PSComputerName : web12
RunspaceId     : aaa00a00-d1fa-4dd6-123b-aa00a00000000
> 
  • Attempted both iterations above with '4>&1' and '*>&1' on the end of the inner and outer commands, no change.

  • Attempted each of:

    1. "& $using:cmd | Tee-Object -Variable out; $out"
    2. "& $using:cmd >$out; $out"
    3. "$out = & $using:cmd; $out"

    (Discarding the return code just to get output) Again, no change.

  • Also attempted: '& $using:cmd >> C:\tmp\output.log; $LASTEXITCODE'. The generated file was empty, and the text was still output to the local terminal.

I'm sure there's something obvious that I'm missing, but so far all I've hit are dead ends. Any suggestions?


Solution

  • On PSv4- (versions 4.x or below), you simply cannot capture Write-Host output - it invariably goes straight to the console.

    In PSv5+, Write-Host (too) writes to the newly introduced information stream that the newly introduced Write-Information cmdlet is designed to write to; its number is 6.

    Thus, if your target host runs PSv5+, you can use the following; note how *> captures all output streams by redirecting (&) them to the success stream (1), but you can use 6> to capture the information stream selectively):

    $result = Invoke-Command -ComputerName $svr -ScriptBlock {& $using:cmd *>&1; $LASTEXITCODE}
    $output = $result[0..($result.Count-2)]
    $exitCode = $result[-1]