Is it possible to get different results on `$?` on the same command? - powershell

I have a powershell script where I'm executing a node command which is meant to be executed by a TFS 2013 Build:
node "$Env:TF_BUILD_SOURCESDIRECTORY\app\Proj\App_Build\r.js" -o "$Env:TF_BUILD_SOURCESDIRECTORY\app\Proj\App_Build\build-styles.js"
$success = $?
if (!$success){
exit 1
}
When I run this script manually and the command fails $success is false and the script exits 1, but when the build executes the script and the node command fails, $success (and $?) is true.
What can change the behavior of powershell? I have no idea what else to try. So far I eliminated the following:
Changed the Build Service user to the same Admin user that executes the script manually
Tried executing the command with cmd /c node ...
Tried executing the command with Start-Process node...
Ran the Build Service interactively
Ran the build with both VSO Build Controller and an on premise Build Controller
Executed the script manually with the same command used by TFS (per the Build Log)
Thoughts?

Can we restructure this a bit so we have a better feel for what is happening? I tend to avoid $? because it is harder to debug and test with.
try
{
Write-Host "TF_BUILD_SOURCESDIRECTORY = $Env:TF_BUILD_SOURCESDIRECTORY"
$result = node "$Env:TF_BUILD_SOURCESDIRECTORY\app\Proj\App_Build\r.js" -o "$Env:TF_BUILD_SOURCESDIRECTORY\app\Proj\App_Build\build-styles.js"
Write-Host "Result = $result"
}
catch
{
Write-Error "Command failed"
Exit 1
}
Sometimes I wrap my command in a Start-Process -NoNewWindow -Wait just to see if that generates a different error message.
In your case, I would also try Enter-PSSession to get a non-interactive prompt on the TFS server. I have seen cases where powershell acts diferently when the shell is not interactive.

Related

How can I propagate the failure to the caller from an invoked script in powershell

I am using jenkins to drive a powershell script. The script is build.ps1. In the Jenkins pipeline I specify
powershell './build.ps1'
I also tried
powershell './build.ps1; exit $LastExitCode'
I have a typo in my build.ps1, or I also get build failures, but Jenkins marks it as success anyway. The reason is that errors are apparently not propagated from the build.ps1 execution to the powershell spawned by jenkins. $LastExitCode is always zero. I verified this in a regular PS prompt with a broken script
./broken.ps1; echo $LastExitCode
gives zero even if test.ps1 throws an error.
I also tried to invoke with &. Same effect.
Try adding the following to your Build.ps1
$ErrorActionPreference = 'Stop'
#
# YOUR CODE
#
trap{
echo "Error while building, error: $($Error[0] | select *)"
exit 1
}
That should trap all errors, and exit with an error code 1.
Also if you can add a screenshot of your Jenkins config when you call the script, just to be sure I think it is what it is.
Hope it helps, good luck.

Returning success and then restarting computer

I'm integrating Jenkins with a bunch of stuff and im using powershell to do this. I have a script on a remote machine that is executed after a build is successful on jenkins. This script does a bunch of stuff and then will restart the machine.
What i need to do is:
Return to jenkins that the script was successful (meaning that it will end the job as SUCCESS)
Then restart the machine
So far i have not managed to send 'EXIT 0' to jenkins and then restart the machine. There's anyway to do this?
Thanks in advance.
Code example:
Write-Host "Code example"
Exit 0 #for jenkins success
Restart-Computer -Force
This will host a seperate command prompt that runs async from the powershell script and restarts the computer in 3 seconds, enough time for powershell to return the exit code to jenkins.
Start-Process -FilePath "cmd.exe" -ArgumentList '/c "timeout /t 3 /nobreak && shutdown -r -f -t 0"' -WindowStyle Hidden
Exit 0
As noted in a comment by #Avshalom, your problem is that the Exit statement will unconditionally exit your script without ever executing the Restart-Computer command placed after it.
Restart-Computer, when executed locally, is invariably asynchronous, so your script will continue to execute, at least for a while.
You can therefore try to call Restart-Computer first, and exit 0 afterwards:
Write-Host "Code example"
Restart-Computer -Force
exit 0 # for Jenkins success
However, there's no guarantee that control will return to Jenkins in time and that Jenkins itself will have time to process the successful exit before it is shut down itself.
You can improve the likelihood of that with a delay, via a separate, asynchronously launched PowerShell instance[1], similar to the approach in Evilcat's answer:
Write-Host "Code example"
# Asynchronously start a separate, hidden PowerShell instance
# that sleeps for 5 seconds before initiating the shutdown.
Start-Process -WindowStyle Hidden powershell.exe -Args '-command',
'Start-Sleep 5; Restart-Computer -Force'
exit 0 # for Jenkins success
This still isn't a fully robust solution, however; a fully robust solution requires changing your approach:
Let your script indicate success only, without initiating a restart.
Make Jenkins test for success and, if so, call another script that unconditionally initiates a shutdown.
[1] As Evilcat points out, using a background job with Start-Job does not work, because on exiting the calling PowerShell session with exit the background jobs are terminated too.

WILDFLY Starting Server through Powershell script

We are trying to Start the WILDFLY Server using the powershell script. Here is the code
cmd.exe /c $env:JBOSS_HOME\bin\standalone.bat
write-host "Before Condition Check"
if ($?)
{
write-host "WILDFLY Server STARTED....."
}
else
{
$JBossResult = "FAILED"
write-host "Error While Starting WILDFLY Server"
}
The server is getting started successfully without any issue, but thing is that it is not coming out of the terminal, hence my next part of the code is not getting executed.
Is there anyway to come out of the terminal without stopping the server, so that I continue to my next step.
Replace this:
cmd.exe /c $env:JBOSS_HOME\bin\standalone.bat
with this:
start-process -filepath "$env:JBOSS_HOME\bin\standalone.bat"
Launching cmd.exe directly causes the script to wait for it to exit before continuing.

How to continue executing a Powershell script after starting a web server?

I have a script which calls two other scripts.
script0.ps1
Invoke-Expression C:\script1.ps1
Invoke-Expression C:\script2.ps1
The first script starts a web server:
script1.ps1
./activate anaconda_env
cd C:\webserver
python api_server.py
The second script starts a ngrok service:
script2.ps1
./activate anaconda_env
cd c:\ngrok
./ngrok -subdomain=mysd 8000
The problem is that the script0.ps1 only executes script1.ps1. At this point the web server starts running in the console and so the second command of script0.ps1 is not executed.
How to make write the scripts so both commands are executed? Or, how to write just one script to execute all commands but in two separate consoles?
The final result should be:
1) a web server running in a console with activated anaconda environment
2) a ngrok service running in a console with with activated anaconda environment
Change Script1.ps1 to launch python as a job:
./activate anaconda_env
cd C:\webserver
Invoke-Command -ScriptBlock {.\python.exe api_server.py} -AsJob -ComputerName .
I don't have the specific script you're using, so I tested this with turtle.py which ships with 3.43 and it seems to work.
You don't need to use Invoke-Expression to run a Powershell script from another script. Just run it as if you're on the command line
c:\script1.ps1
c:\script2.ps1
Now if script1.ps1 starts a process that doesn't exit, it will halt execution for the next statements in the script, and thus also prevent the second script from running.
In most cases this sequential execution is exactly what you want.
In your case you can start the scripts asynchronously by using Start-Process.
So your main script becomes something like:
start-process c:\script1.ps1
start-process c:\script2.ps1
Start-Process basically starts a new command shell to run the statement in. Check out the docs for more info. There's a bunch of parameters you can use to tweak how this happens.
To not have invoke-expression close your script you can pipe the output to Out-Null. Your code above would look like:
Invoke-Expression C:\script1.ps1 | Out-Null
Invoke-Expression C:\script2.ps1 | Out-Null

How to return an exit code from a Powershell script only when run non-interactively

I have a lot of scripts that are running as scheduled tasks. So they do a $host.setshouldexit(1) on any failure, which shows up in the task scheduler as the return code.
I also want to be able to run these scripts interactively while debugging and testing. So the $host.setshouldexit() kills my powershell or ISE session.
My question is: how can I detect if a script is running non-interactively? If it is, then I'll use setshouldexit, otherwise it will print the error code or something nondestructive. (Note that I don't want to use [environment]::userinteractive because these scripts are not always running in what the OS thinks is a non-interactive session.)
There is a -noninteractive switch that I'm using for the scheduled tasks. Is there some way I can query that from powershell?
The $Host.SetShouldExit method should not be necessary, and is actually inconsistent, depending on how you are calling your scripts. Using the keyword exit should get you your exit status.
Using powershell -F script.ps1:
exit - works
SetShouldExit - ignored
Using powershell -c '.\script.ps1':
exit - status reduced to 0 or 1, for success or failure of the script, respectively.
SetShouldExit - exits with correct status, but remaining lines in script are still run.
Using powershell -c '.\script.ps1; exit $LASTEXITCODE' [1]:
exit - works
SetShouldExit - exits with status == 0, and remaining lines in script are still run.
Calling directly from powershell (> .\script.ps1):
exit - works
SetShouldExit - terminates calling powershell host with given exit status
Why not just have it take a parameter "testing" which sets the right behavior during your tests? You have a history buffer so it will be hardly any more typing to run.
I had the same issue. The following works for me:
# Exit with Return Code when NOT using PowerShell ISE
if ($psise -eq $Null)
{
$host.SetShouldExit(1)
}
Upon finding your question I have taken the issue a bit further and found $MyInvocation.MyCommand.Name. This is False in both the command interpreter and the ISE command interpreter. And when a script (mine is jest.ps1) containing just the line: Write-Host $MyInvocation.MyCommand.Name is run from cmd.exe call to powershell.exe as:
%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy RemoteSigned -NoExit -NonInteractive -NoProfile -File "M:\WindowsPowerShell\Jest.ps1"
Output is simply:
Jest.ps1
Check $Host.Name. If your script is running outside of an IDE, it will return a value of ConsoleHost. Otherwise it will return a reference to the IDE such as Windows PowerShell ISE Host or PowerGUIScriptEditorHost.
I have to use SetShouldExit because the scheduler that runs my scripts usually ignores other methods of indicating a failure. I add a function to allow SetShouldExit when $Host.Name is ConsoleHost. It saves a lot of IDE crashes during testing.
This directly answers the question poster had about whether or not you can query if the powershell console was launched with -NonInteractive switch.
Function Test-IsPowerShellConsoleNonInteractive { [Boolean]([Environment]::GetCommandLineArgs() -Match '-NonInteractive') }
$IsPSConsoleNonInteractive = Test-IsPowerShellConsoleNonInteractive
If ($IsPSConsoleNonInteractive) { $Host.SetShouldExit(2) }