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.
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.
Start-ThreadJob
comes with PowerShell (Core) v6+, and can be installed on demand in Windows PowerShell v3+ - see this answer.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
}