powershellpsexec

PsExec returning only the first line of stdout inside of a BackgroundJob


I am having an issue with the stdout of PsExec being different inside of a BackgroundJob.

Some information about the environment I am working in:

I have a large amount of Windows XP machines that do not have WinRM and I am unable to use WMI calls, however PsExec works. I need to get the stdout of a command from all of the XP machines. I was able to get the stdout I expected outside of a BackgroundJob, however when I attempt to PsExec inside of a BackgroundJob I only receive the first line of the stdout.

Here is an example of the difference I am seeing:

# 1.2.3.4 = IP Address of an XP Machines
# 123.123.123.123 = IP of a server I want to compare the time against.

$output = & "path\to\psexec\psexec.exe" "\\1.2.3.4" -accepteula -u "$($creds.Username)" -p "$($creds.GetNetworkCredential().Password)" w32tm /stripchart /computer:123.123.123.123 /samples:1 /dataonly

Write-Host $output

This code outputs: Tracking 123.123.123.123 [123.123.123.123]. Collecting 1 samples. The current time is 6/30/2021 2:41:51 PM (local time). 14:41:51, +00.0059682s

However, this code outputs something different:


Start-Job -Name "1.2.3.4" -ScriptBlock {
    # 1.2.3.4 = IP Address of an XP Machines
    # 123.123.123.123 = IP of a server I want to compare the time against.

    $output = & "path\to\psexec\psexec.exe" "\\1.2.3.4" -accepteula -u "$($using:creds.Username)" -p "$($using:creds.GetNetworkCredential().Password)" w32tm /stripchart /computer:123.123.123.123 /samples:1 /dataonly
    
    return $output
}

$returnValue = Get-Job "1.2.3.4" | Receive-Job -Wait -AutoRemoveJob

Write-Host $returnValue

This outputs: Tracking 123.123.123.123 [123.123.123.123]. Which is the first line of the stdout of w32tm /stripchart /computer:123.123.123.123 /samples:1 /dataonly.

Am I doing this incorrectly? My understanding is that there should not be a difference in the stdout inside and outside of a BackgroundJob.

I am rather new to PowerShell. If my understanding is incorrect, please enlighten me. Any information or advice you can give me would be helpful.

Thank you!

Update: After reading through Iconiu's Answer on this StackOverflow Thread

It appears that this is a limitation of PowerShell. I am currently working on a workaround for this. I will update the question again when I have a working one.

Update2: I have found a workaround that works for my purpose and I figured I would share it.


$ip = '1.2.3.4' # IP of an XP Machine
$otherip = '123.123.123.123' # Machine I want use for w32tm

# Create the temp Folder outside of the job if it does not exist.
if(!(Test-Path "\\$ip\c`$\temp\")) {
    MKDIR "\\$ip\c`$\temp\"
    Start-Sleep -Seconds 1 # Sleep for one second to allow for network lag.
}

Start-Job -Name $ip -ScriptBlock {
    $ip = $using:ip
    $otherip = $using:otherip
    $creds = $using:creds 
    & "path\to\psexec\psexec.exe" \\$ip -accepteula -u $creds.Username -p $creds.GetNetworkCredential().Password cmd /c "w32tm /stripchart /computer:$otherip /samples:1 /dataonly > C:\temp\w32tmstripchart.txt"
    if(Test-Path "\\$ip\c`$\temp\w32tmstripchart.txt") {
        $stdout = Get-Content -Path "\\$ip\c`$\temp\w32tmstripchart.txt" -Raw
        return $stdout
    } else {
        return "ERROR"
    
}

$retVal = Get-Job -Name $ip | Receieve-Job -Wait -AutoRemoveJob

Write-Host $retVal


If you would like to capture stderr as well as stdout from the command add 2>&1 after directing the output to a file. For example, w32tm /stripchart /computer:123.123.123.123 /samples:1 /dataonly > w32tmstripchart.txt 2>&1

If you would prefer the stdout to be an array of strings instead of a single string, remove the -Raw switch from the Get-Content command.

Hope this is helpful to anyone in the future facing a similar problem.


Solution

  • Indeed there seems to be a problem with Start-Job reporting only the first stdout line from psexec's output.

    Start-Job creates child-process-based background jobs, but there is a lighter-weight, generally preferable thread-based alternative, Start-ThreadJob, which does not have the same problem.

    Therefore, try the following, which uses Start-ThreadJob:

    Start-ThreadJob -Name 1.2.3.4 -ScriptBlock {
      & 'path\to\psexec\psexec.exe' -nobanner \\1.2.3.4 -accepteula -u $using:creds.Username -p ($using:creds).GetNetworkCredential().Password w32tm /stripchart /computer:123.123.123.123 /samples:1 /dataonly 2>$null
    }
    

    If you also want to capture w32tm's stderr output:

    Start-ThreadJob -Name 1.2.3.4 -ScriptBlock {
      & 'path\to\psexec\psexec.exe' -nobanner \\1.2.3.4 -accepteula -u $using:creds.Username -p ($using:creds).GetNetworkCredential().Password cmd /c 'w32tm /stripchart /computer:123.123.123.123 /samples:1 /dataonly 2>&1' 2>$null
    }