powershellredirectstandardoutput

How do you write all output streams to a variable in PowerShell?


In PowerShell, I'm unable to write error stream output to a variable.

Running the command without saving the output to a variable will result in output from any stream being visible, but alas, I require the output to be written to a variable.

& terraform $action $arguments

Initially I started using this. However, only the success stream is written to the variable (as expected).

$res = & terraform $action $arguments

So I consulted the docs for about_Redirection, but the trouble is, when I redirect the error stream (or all streams) to the success stream, I still only see the success stream written to the variable. I've made several attempts, all of which failed.

$res = & terraform $action $arguments 2>&1
$res = & terraform $action $arguments *>&1
$res = & terraform $action $arguments *>&1 | ForEach-Object { $_.ToString() }

However, of if I redirect the error stream to a file then the stream is written as expected.

$res = & terraform $action $arguments 2>> terraform-errors.log

How can I write the output from all streams to a variable?


Solution

  • I've finally found the answer to this, and the issue is due to the setting $ErrorActionPreference = Stop.

    When a terminating occurs, the script is killed before the output is redirected. To get around this, the error action preference must be changed and then errors and output can be sent to to variables using Invoke-Expression and some common parameters.

    $ErrorActionPreference = "SilentlyContinue"
    Invoke-Expression "& terraform $action $arguments 2>&1" -ErrorVariable terraformError -OutVariable terraformState | Out-Null
    $ErrorActionPreference = "Stop"
    
    ### Check if there was an error.
    if ($terraformError) {
        throw $terraformError
    }
    

    Why not use -ErrorAction to change the error action preference?

    Sadly -ErrorAction only works with non-terminating errors. Because the error action preference for the script is Stop, this means that the Invoke-Expression command will always trigger a terminating error regardless of the value of the -ErrorAction common parameter.

    To get around this, the error action preference must be changed before the Invoke-Expression command is run, and then changed back after it's finished.

    Why is output piped to Out-Null?

    Despite saving errors and output to variables, output would also be streamed to the console without piping it to Out-Null.

    Obviously this is optional as in some cases in may be acceptable/expected that output would also be streamed to the console.