"Start-Process -NoNewWindow" within a Start-Job? - powershell

I'm having issues using Start-Process within a Start-Job, specifically when using -NoNewWindow. For example, this test code:
Start-Job -scriptblock {
Start-Process cmd -NoNewWindow -Wait -ArgumentList '/c', 'echo' | out-null
Start-Process cmd # We'll never get here
}
get-job | wait-job | receive-job
get-job | remove-job
Returns the following error, that apparently google hasnt heard of:
Receive-Job : There is an error processing data from the background process. Error reported: Cannot process an element with node type "Text". Only Element and EndElement node types are supported.
If I remove the -NoNewWindow everything works just fine. Am I doing something silly, or is there no way to start jobs containing Start-Process -NoNewWindow? Any good alternatives?

A little late, but for people still having issues with this specific error message, one fix for this example is to use -WindowStyle Hidden instead of -NoNewWindow, I've had -NoNewWindow appear to get ignored a lot of the time and cause it's own problems.
But for this specific error that seems to come from using Start-Process with various executables, I have found the solution that seems to work consistently is by redirecting the output, as it is the output that comes back appears to cause the problem. Unfortunately though that does result in writing to a temporary file and cleaning it up.
As an example;
Start-Job -ScriptBlock {
# Create a temporary file to redirect output to.
[String]$temporaryFilePath = [System.IO.Path]::GetTempFileName()
[HashTable]$parmeters = #{
'FilePath' = 'cmd';
'Wait' = $true;
'ArgumentList' = #('/c', 'echo');
'RedirectStandardOutput' = $temporaryFilePath;
}
Start-Process #parmeters | Out-Null
Start-Process -FilePath cmd
# Clean up the temporary file.
Remove-Item -Path $temporaryFilePath
}
Get-Job | Wait-Job | Receive-Job
Get-Job | Remove-Job
Hopefully this helps.

Related

Powershell wait-job gives error one or more jobs are blocked waiting for user interaction

I have below command, $a have 20 URLs:
$a = #('http://10.10.10.101/Login.aspx',
'http://10.10.10.101/user/customers')
$startFulltime = (Get-Date)
foreach($a1 in $a){
start-job -ArgumentList $a1 -ScriptBlock{
param($a1)
Invoke-WebRequest $a1 -Method post -DisableKeepAlive -UseBasicParsing
-ArgumentList $a1
}}
Get-Job | Wait-Job
Getting Below Errors:
Wait-Job : The Wait-Job cmdlet cannot finish working, because one or more jobs are blocked waiting for user interaction. Process interactive job
output by using the Receive-Job cmdlet, and then try again.
At line:13 char:11
+ Get-Job | Wait-Job
+ ~~~~~~~~
+ CategoryInfo : Deadlock detected: (System.Manageme...n.PSRemotingJob:PSRemotingJob) [Wait-Job], ArgumentException
+ FullyQualifiedErrorId : BlockedJobsDeadlockWithWaitJob,Microsoft.PowerShell.Commands.WaitJobCommand
Reference Link : input objects in powershell jobs
Please help me to resolve error, thanks In Advance!!
The only obvious problem with your code - which may be a posting artifact - is that there's a second, extraneous -ArgumentList $a1 inside your Start-Job script block - but that would cause a different error, though potentially preempted by the one you're getting.
It sounds like one of your jobs unexpectedly prompts for user input, as implied by the error message.
Use of Receive-Job, as suggested in the error message, will bring interactive prompts to the foreground, allowing you to see and respond to them.
This should help you diagnose which job causes the problem and why; here's a simplified example:
# Start a job with a command that asks the user for interactive input
# (which isn't a good idea).
$job = Start-Job { Read-Host 'Enter something' }
# !! Error, because the job is waiting for interactive user input.
$job | Wait-Job
# OK - Receive-Job "foregrounds" the Read-Host prompt.
# Once you respond to it, the job finishes
# (and is automatically removed here, thanks to -Wait -AutoRemoveJob).
$job | Receive-Job -Wait -AutoRemoveJob

How can I run powershell in the background if I want execution policy Bypass?

I have this batch file which runs the powershell script.
I want to run it the background but if I run with "windowstyle hidden" still visible.
powershell -ExecutionPolicy ByPass -windowstyle hidden -File "C:\script.ps1"
You can run, e.g. long running scripts, as a jobs.
To start it you run
$job = Start-Job -ScriptBlock {Get-Process}
this will start the Get-Process cmdlet in the background. The script can be also some custom made script or a longer script, it doesn't need to be a one-liner.
You can check its status by running
$job | Get-Job
and to receive the output you run
$job | Receive-Job
just note that once the data is received, it's lost. You can only receive it once, after that it's up to you to save it in a variable or later processing.
Finally to remove the job from the queue you run
$job | Remove-Job
I use the following function:
function bg() {
Start-Process `
-WorkingDirectory (Get-Location) `
-NoNewWindow `
-FilePath "powershell" `
-ArgumentList "-NoProfile -command `"$args`" "
}
It starts a new powershell instance which is executed in background and allows the usage of cmdlets.
You call it like:
bg "Start-Sleep 2; get-location; write 'done' "

Code in Job's Script Block after Start-Process Does not Execute

When I create a automation script with PowerShell 5.1, I got an issue – in a script block of a job, the code after Start-Process will not get chance to execute. Here’s a simple repro:
Step 1 >> Prepare a .cmd file for Start-Process, the code in callee.cmd is:
#echo off
echo "Callee is executing ..."
exit /B 0
Step 2 >> Prepare the PowerShell code,
$scriptBlock = {
$res = Start-Process -FilePath "cmd.exe" -Wait -PassThru -NoNewWindow -ArgumentList "/c .\callee.cmd"
throw "ERROR!"
}
$job = Start-Job -ScriptBlock $scriptBlock
Wait-Job $job
Receive-Job $job
Write-Host($job.State)
Step 3 >> Run the PowerShell script, the output on screen is:
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Completed True localhost ...
Completed
The expected value should be “Failed”.
Does my code have problem or I’m using jobs in a wrong way?
Start-Job run job in separate PowerShell process in so-called server mode. In this mode PowerShell job process use standard input and output streams to exchange messages with the parent process.
-NoNewWindow parameter of Start-Process cmdlet instruct it to connect spawned console child process to the standard streams of its parent.
Thus, using Start-Process -NoNewWindow inside of PowerShell job, you connect spawned cmd.exe process to the same streams, which PowerShell job process use to exchange messages with its own parent process.
Now, when spawned cmd.exe write something into its standard output stream, it disturb normal message exchange between PowerShell job process and its parent, which leads to some unexpected behavior.
PetSerAl gave a great explanation of why it happens but it took me while to find a proper solution.
First thing - as some people mentioned don't use -NoNewWindow, use -WindowStyle Hidden instead.
Second, output results to file and handle them in your script block. There are two parameters, one for output and another for errors -RedirectStandardOutput and -RedirectStandardError.
For some reasons I was sure that first one will handle all output and my code worked well if there were no errors, but was failing with no output in case of exceptions in script block.
I created a function that also handles process status result:
function RunProcesSafe($pathToExe, $ArgumentList)
{
Write-Host "starting $pathToExe $ArgumentList"
$logFile = Join-Path $env:TEMP ([guid]::NewGuid())
$errorLogFile = Join-Path $env:TEMP ([guid]::NewGuid())
try
{
Write-Host "starting $pathToExe $ArgumentList"
$proc = Start-Process "$pathToExe" -Wait -PassThru -RedirectStandardError $errorLogFile -RedirectStandardOutput $logFile -WindowStyle Hidden -ArgumentList $ArgumentList
$handle = $proc.Handle
$proc.WaitForExit();
if ($proc.ExitCode -ne 0) {
Write-Host "FAILED with code:"+$proc.ExitCode
Throw "$_ exited with status code $($proc.ExitCode)"
}
}
Finally
{
Write-Host (Get-Content -Path $logFile)
Write-Host (Get-Content -Path $errorLogFile)
Remove-Item -Path $logFile -Force
Remove-Item -Path $errorLogFile -Force
}
}

Run Scriptblock in It's Own Instance and Return ExitCode to Parent Script in PowerShell

I am attempting to have a PowerShell script as a node inside of an XML file that either returns an exit code of 1 or 0. I would then like to run this script in an instance separate from the parent PS script but return its exit code back to the parent instance so that I can write an If statement based on the ExitCode.
Right now I made the XML PowerShell script simple (this seems to be working fine without any issues):
exit 1
Here's my code in the parent PS script:
#write XML script to string then convert string to scriptblock
[String]$installCheck_ScriptString = $package.installcheck_script
$installCheck_Script = [Scriptblock]::Create($installCheck_ScriptString)
#start new instance of powershell and run script from XML
$process = (Start-Process powershell.exe -ArgumentList "-command {$installCheck_Script} -PassThru -Wait")
$installCheck_ScriptResult = $process.ExitCode
If ($installCheck_ScriptResult -gt 0)
{
....
}
In playing around with the code, I seem to either get a message that Wait or Passthru are unexpected tokens or I don't get any ExitCode value. $LastExitCode always returns a 0.
-Wait and -PassThru are not valid parameters for powershell.exe. Did you mean to apply them to Start-Process like this?
$process = (Start-Process powershell.exe -ArgumentList "-command {$installCheck_Script}" -PassThru -Wait)
Note that you're going to have some issues with this approach. If $installCheck_Script contains any characters that need to be escaped you're going to be doing a lot of checks and replaces.
You could avoid that by using -EncodedCommand with powershell.exe and passing in the base64 encoded version of the script:
$encodedScript = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($installCheck_Script))
$process = (Start-Process powershell.exe -ArgumentList "-EncodedCommand",$encodedScript -PassThru -Wait)
But only do this if you're insistent on calling via shell.
A better (?) way:
As an alternative to what you're doing (shelling out), you might consider creating a job, and then instead of using an exit code, use an actual return value:
$installCheck_Script = " 1 " # for example
$sb = [ScriptBlock]::Create($installCheck_Script)
$job = Start-Job -ScriptBlock $sb
$job | Wait-Job
$code = $job | Receive-Job
If you wanted better performance you could do it in process with runspaces. This is made easier with the PoshRSJob Module which lets you use runspaces in a similar way to using jobs.

gpupdate with powershell

I want to perform a gpupdate as an -Action of a System.IO.FileSystemWatcher instance but gpupdate occasionally hangs completely when being called from PowerShell. I have tried the following way to handle an inresponsive gpupdate but it does not work as expected - the whole script still hangs when gpupdate goes wrong.
$command = "/target:computer"
$gp = Start-Process -FilePath gpupdate `
-ArgumentList $command -NoNewWindow -PassThru
wait-process -inputobject $gp -timeout 10
if( $gp.HasExited -ne $true )
{
stop-process -inputobject $gp -force
$errcode = 99
}
else
{
$errcode = $gp.exitcode
}
My questions are
Has anyone experienced similar problems with gpupdate being called from PowerShell (v2); what could be the reason for that?
Why does the above code snippet not do what I expect it to do (i.e. why does the wait-process-command not really shutdown the gpupdate process when the latter is inresponsive)?
UPDATE:
I have tried adding the /wait Parameter but it did not change anything. I have also tried the following code:
$gp = Invoke-WmiMethod -ComputerName "localhost"`
-EnableAllPrivileges`
-Path win32_process -Name "create"`
-ArgumentList "gpupdate /target:Computer /wait:5"
with $gp.ReturnValue as $errcode, but the problem remains. I still dont get a response from the script after several runs and the subscriber stops firing.
After trying the whole thing with start-job and wait-job which did not solve it either, I am at my wit's end.
Is there a better option to perform this task?
Is it worth to read up on powershell error handling?