Launching a process with a different user identity invariably launches the process in a new window on Windows, which means that you cannot directly capture that process' output.
You have two options:
Use the System.Diagnostics.Process
API directly, which allows you to capture the process' output in memory, as text, via the .RedirectStandardOutput
(and .RedirectStandardError
) properties of the System.Diagnostics.ProcessStartInfo
class.
This approach is nontrivial, especially if you want to also capture stderr output without the risk of deadlocks; a stdout-output-only-capturing solution can be found in this answer.
It precludes running the process hidden, because the use of another user's credentials (.UserName
and .PassWord
properties) requires that .UseShellExecute
be $false
, which in turn means that the .WindowStyle
property is ignored.
Use Start-Process
if you want (the option) to run the process hidden, although that requires the use of a temporary file to capture the output:
Pitfalls: You must ensure that the target user has permission:
- to access the working directory of the newly launched process, which requires passing a suitable directory to
-WorkingDirectory
- to write to the (temporary) file path passed to
-RedirectStandardOutput
/ -RedirectStandardError
.
Note that -RedirectStandardOutput
/ -RedirectStandardError
capture the output from the named streams separately, and must be separate files. If you want to merge the streams and capture them interleaved in a single file, you must launch your process via a shell (such as PowerShell or cmd.exe
) and use its stream-merging features, typically 2>&1
.
The following Start-Process
-based solution shows how to run the process hidden and capture stdout output (only):
# Prompt for the target user's credentials.
$cred = Get-Credential
# Determine the working directory and temporary file path.
# IMPORTANT: THE TARGET USER MUST HAVE PERMISSION TO
# * ACCESS the working directory
# * access and WRITE TO the temp file.
$workingDir = "$env:SystemRoot\Temp"
$tmpFile = Join-Path "$env:SystemRoot\Temp" "~$PID.tmp"
# Launch the process as the target user, hidden, save its stdout
# to the temporary file, and wait for it to terminate.
(Start-Process -WindowStyle Hidden -PassThru -WorkingDirectory $workingDir -RedirectStandardOutput $tmpFile -Credential $cred powershell.exe @'
-noprofile -command
"[array] (wsl -- ip -o -4 -json addr list eth0 | ConvertFrom-Json).addr_info.local -ne ''"
'@).WaitForExit()
# Get the captured output and remove the temp. file.
$output = Get-Content $tmpFile; Remove-Item $tmpFile
# Print the captured result
$output
Note:
The powershell.exe
CLI call above uses a streamlined version of the command in your question.
While Start-Process
has a dedicated -Wait
switch to await the launched process' termination, as of PowerShell Core 7.2 it appears to be incompatible with -Credential
, i.e. with running as a different user (resulting in an Access denied
error while still launching the process, albeit asynchronously).
- The workaround is to use
-PassThru
, in order to make the normally output-less Start-Process
emit a System.Diagnostics.Process
instance describing the launched process, and call .WaitForExit()
on that instance.