PowerShell exit code is always 0 in TeamCity 8 build step - powershell

We are using TeamCity Enterprise 8.0.5.
I have a TeamCity build step which runs a PowerShell (.ps1) script, which looks like this:
try
{
# Break something
$a = 1 / 0
}
catch
{
Exit 1
}
Despite this, in the build log, the step succeeds and exits with code 0.
[10:02:18][Step 2/3] Process exited with code 0
I want the step to fail if there are any failures in the script. How can I make this happen?

I have just discovered this post:
PowerShell runner - script fails but the build succeeds - 'Process exited with code 0'
There is a bug in TeamCity which means that non-zero PowerShell return codes are not picked up.
The solution suggested is to create a build failure condition on detection of certain text output into the build log.
However, my solution involved something different.
In the catch block you only have to use the Write-Error cmdlet:
catch
{
Write-Error -Exception $_.Exception
}
Then you must ensure that two things are set correctly in TeamCity:
Firstly, the build step Error Output should be set to error, and not warning:
Secondly, in the build failure conditions screen, make sure an error message is logged by build runner is checked:

Suppose you are using psake and your goal is to fail your build if your build script fails. The script which imports the psake module and invokes the build script does not fail, so TeamCity takes it as a successful build.
Add this code into your first script to fail your build if the second script fails.
Import-Module .\psake\psake.psm1
Invoke-Psake .\build-steps.ps1 #args
if($psake.build_success -eq $false){
Write-Host "There was an error running psake script"
exit 1
}
Remove-Module psake
Do not remove the module before the if statement.

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.

Why does TeamCity's PowerShell Build Runner display a stack trace instead of error output?

I am using TeamCity's out-of-the-box PowerShell Build Runner to run script provided in the Build Step. The script is effectively:
$ErrorActionPreference = "Stop"
$match = select-string "%EwlProjectPackagesConfigPath%" -pattern "<package id=`"(Ewl)`" version=`"([0-9\.pr-]+)`".+/>"
$ewlPackage = $match.Matches.Captures.Groups[1]
$version = $match.Matches.Captures.Groups[2]
$devUtil = Get-ChildItem "%PackagesDirectory%\$ewlPackage.$version\Development Utility\EnterpriseWebLibrary.DevelopmentUtility.exe"
& $devUtil "%SolutionDirectory%" UpdateAllDependentLogic
if ($LastExitCode -ne 0) {
Throw "Process exit code: $LastExitCode";
The exe being called by the & operator is running into a problem. It outputs to Standard Error and returns code 1. Here is a screenshot of the output in TeamCity:
The output in TeamCity is only showing exactly one line of error output (6/26/2019 2:48:55 PM: Failed to update database logic.), and then a stack trace of the problem in the script.
I'm unable to reproduce this outside of TeamCity. If I save the script to my desktop and run the following command, which should be the same as what is being executed by TeamCity:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile
-NonInteractive -ExecutionPolicy ByPass -File "C:\Users\Administrator\Desktop\Test.ps1"
Then I receive about a hundred lines of error output (which I would like to be seeing in TeamCity), and I do not see a stack trace at all.
Why doesn't TeamCity capture the entire error output?
How does TeamCity display a stack trace and why do I not when I run the script in the same manner?

TFS 2017 Build: Cannot Run PowerShell

We are using TFS 2017 and it has several builds configured. A little while ago we started getting an error on the second step, which is to run a PowerShell Script (first step is Get Sources):
2018-06-28T19:58:59.4326443Z ##[command]. 'K:\_work\3\s\BuildScripts\MainPre.ps1' -env "test"
2018-06-28T19:58:59.6236482Z ##[error]Access is denied
2018-06-28T19:58:59.6266488Z ##[section]Finishing: PowerShell Script
A build 4 hours ago worked just fine. No changes were made to the file, or the filesystem. I am waiting to hear from the network team to see if they did anything to the build account.
What could cause this error suddenly and how do I fix it? Note: I have not yet tried to turn it off and on again.
Based on the error message "##[error]Access is denied", seems it's an permission issue.
Just try below items to narrow down the issue:
Enable Clean option in Get sources step: Set Clean to
True and select Sources Directory under Clean options.
Check if the agent service account has the correct permission to
access the script.
Try to change another account which has the correct permission to
access the agent _work foler as the service account, then queue build
again.
Deploy a new agent, try it again.
If that still not work, just turn on system.debug in variable tab (set to true) to capture the logs and share here for further troubleshooting.
It looks like the powershell task runs some sort of security check when executing scripts?
I ran the powershell task in DEBUG and you can see the task runs some security work implicitly here.
This does give me access denied when i run it:
##[debug]C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
-NoLogo -Sta -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command "try { $null = [System.Security.Cryptography.ProtectedData] } catch { Write-Verbose 'Adding assemly: System.Security' ; Add-Type -AssemblyName 'System.Security' ; $null = [System.Security.Cryptography.ProtectedData] ; $Error.Clear() } ; Invoke-Expression -Command ([System.Text.Encoding]::UTF8.GetString([System.Security.Cryptography.ProtectedData]::Unprotect([System.Convert]::FromBase64String('AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAARs9EULLEBU+ppaGEeISmGgAAAAACAAAAAAADZgAAwAAAABAAAABLYbw0iUTABtaCw2PJ5KrrAAAAAASAAACgAAAAEAAAAOg6VMmANxZJSRmKjPWauqRYAAAAqDSQVtB4LtvBaujeTs1GKn4CPFrW484weBNwtJ7aujcJLWV4wBLHD9n+IEVZ6z13oyIpyxUEceTtiMKnfuO8irwX9l5DoHqlMGU6mx1Q5kou2V6ITEcl0BQAAAD1h7qvkyE8+PcdKmVKLHVpqYO4mA=='), [System.Convert]::FromBase64String('8yTvn1ZlLZGC7M3ewDzbLw=='), [System.Security.Cryptography.DataProtectionScope]::CurrentUser))) ; if (!(Test-Path -LiteralPath variable:\LastExitCode)) { Write-Verbose 'Last exit code is not set.' } else { Write-Verbose ('$LastExitCode: {0}' -f $LastExitCode) ; exit $LastExitCode }"
2018-06-30T12:44:57.8488275Z ##
For us, MCafee was blocking the powershell. once an exception was added, we were good.
While checking through the server, I noticed that the Event Viewer says Symantec SONAR was blocking the power shell scripts. After our network team added an exception for the build processes, our builds were again working as expected.

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

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.

Powershell InitializeDefaultDrives Error always stops Team Build from performing a successful build

Whenever we start up Powershell on any machine on our network we instantly receive the following error in the console 'Attempting to perform the InitializeDefaultDrives operation on the 'FileSystem' provider failed.'
The reason for this is that upon start-up Powershell attempts to map all the drives it can find to Powershell objects, however, for some reason our Ops guys have configured all our network drives to appear as 'disconnected' even though they are fully accessible. Powershell sees these drives as 'disconnected' and throws an error. I've tried to get the Ops guys to change this but the behaviour seems to be set quite deeply in our infrastructure.
Under normal usage it isn't too much of an issue, however, we are trying to run a Powershell script (with Psake) as part of an automated build process via Team Build, and the error on start-up is picked up by the build process and causes our build process to only partially succeed, it's impossible for us to achieve a nice, green, successful build.
Our Psake-based Powershell scripts is kicked off from a simple batch file that looks like this -
cls
powershell -ExecutionPolicy Unrestricted -command "& \"%~dp0psake.ps1\"" %*
echo EXIT CODE - %ERRORLEVEL%
exit /b %ERRORLEVEL%
This batch file is called from an Invoke-Process workflow object in TeamBuild with the standard output and error output mapped to stdout and stderr respectively.
I can see a few potential areas we might be able to solve this
Find a way to stop Powershell from performing the InitializeDefaultDrives operation
Filter out that specific error in the batch wrapper somehow but still pass genuine errors back up to the build process
Parse errors in the Invoke Process workflow object so that particular error doesn't cause a failure, but all other errors still each the build process.
Any help GREATLY appreciated! : )
Eventually I found I was missing some error checking after my call to Powershell so my batch file should have looked like this...
cls
powershell -ExecutionPolicy Unrestricted -command "& \"%~dp0psake.ps1\"" %*; if ($psake.build_success -eq $false) { exit 1 } else { exit 0 }
echo EXIT CODE - %ERRORLEVEL%
exit /b %ERRORLEVEL%