27

I'm using Jenkins PowerShell plugin to build a project.

However, I found that Jenkins always considers my build successful no matter what I type inside Windows PowerShell command.

Here's an example:

enter image description here

As you can see, asdf isn't a legal command. Jenkins should give me FAILURE after the build.

But the console output gives me:

Started by user admin
Building in workspace C:\Users\Administrator\.jenkins\jobs\Test\workspace
[workspace] $ powershell.exe -NonInteractive -ExecutionPolicy ByPass "& 'C:\Users\ADMINI~1\AppData\Local\Temp\hudson2092642221832331776.ps1'"
The term 'asdf' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\ADMINI~1\AppData\Local\Temp\hudson2092642221832331776.ps1:1 char:5
+ asdf <<<< 
    + CategoryInfo          : ObjectNotFound: (asdf:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Finished: SUCCESS

I think the execution result of PowerShell should depend on $lastexitcode.

Is this a bug of PowerShell plugin?

Brian
  • 12,145
  • 20
  • 90
  • 153
  • 1
    In your powershell script when an error occurs use "exit x" where x is a non-zero number. See if Jenkins considers it failed then. – DanL Dec 18 '15 at 07:45
  • Yes, Jenkins considers `exit 1` as failed. – Brian Dec 18 '15 at 07:49
  • 1
    Then change your scripts to exit with a non-zero error code if an error occurs. If you want it to depend only on $lastexitcode then at the bottom of your script check $lastexitcode and use exit 1 if applicable. – DanL Dec 18 '15 at 07:51
  • 2
    Finally, I add `exit $LastExitCode` at the end of my script. – Brian Dec 21 '15 at 01:54

7 Answers7

16

As of 1.3, the plugin will not handle exceptions such as those from missing commands. You can do this yourself with try/catch:

try
{
    asdf
}
catch
{
    write-host "Caught an exception"
    exit 1
}

See MSDN for more.

Chris Nelson
  • 3,519
  • 7
  • 40
  • 51
  • 1
    Thanks for this answer! To get the actual error out to the build console I do `write-host $error` within the catch. – David Peters Aug 07 '18 at 17:56
10

For me, I wanted the script to stop and fail in Jenkins soon as it hit an error. This was accomplished by adding this to the start of the script:

$ErrorActionPreference = "Stop"

This is discussed here: [How to stop a PowerShell script on the first error?][1]

[1]: How to stop a PowerShell script on the first error?. ..................

Community
  • 1
  • 1
Robert Patterson
  • 509
  • 7
  • 12
  • This is the only solution that worked for me. I'm injecting variables into the script and had everything working with the exception of errors. This finally marks the build as failure when there's an error in the PS script. Thank you! – rspring1975 Feb 22 '17 at 23:40
  • This doesn't work for me in a declarative pipeline: script: `$ErrorActionPreference = "Stop" asdf`fails inside powershell, but doesn't fail the pipeline. Pipeline: `steps { powershell(returnStatus: true, script: 'psscripts/psScript1.ps1') }` I have to put it in a script block and test the return value to get it to fail. – Max Cascone Apr 23 '20 at 17:52
5

Per the latest version of the plugin (Version 1.3 Sept 18 2015), you must use $LastExitCode to fail a build.

Version 1.3 (Sept 18 2015)

  • PowerShell now runs in Non-Interactive mode to prevent interactive prompts from hanging the build
  • PowerShell now runs with ExcecutionPolicy set to "Bypass" to avoid execution policy issues
  • Scripts now exit with $LastExitCode, causing non-zero exit codes to mark a build as failed
  • Added help and list of available environment variables (including English and French translations)
rrirower
  • 4,338
  • 4
  • 27
  • 45
  • This answer merely echoes the release notes from the plugin which don't seem to accurately reflect the operation of the plugin. The notes says "scripts now exit with $LastExitCode" which I read as "When the plugin copies your commands to a `.ps1` file to execute, it adds `exit $LastExitCode` to the end of the generated script so that Jenkins will capture and react to the failure." This is exactly what happens so manually adding that `exit` line is not needed. However, neither does an invalid command cause the build to fail, even with the generated `exit` in place. – Chris Nelson May 20 '16 at 14:12
  • @ChrisNelson From your link you posted.... _"To trap this exit code utilize the $LastExitCode PowerShell variable."_ And, yet, my response was accepted as an answer. I disagree with the need to add the 'exit' command. – rrirower May 20 '16 at 14:52
  • I disagree with the need to add the `exit`, too. The plugin does that. The OP wondered how to handle `asdf` and I note that a command that can't be found doesn't set `$LastExitCode`, it throws an exception. – Chris Nelson May 22 '16 at 02:13
  • @ChrisNelson I think you may have misunderstood the context of the original question. You should try to run a script within Jenkins and try to get the exit code. I think you'll find that your assumption is incorrect. – rrirower May 22 '16 at 15:52
  • I'm confused about what I may not be understanding. If you put just `asdf` into the Command area and do the build, the plugin emits a two-line script with `asfd` and `exit $LastExitCode`. Since `asdf` throws an exception and does not set `$LastExitCode`, the build succeeds. Adding an explicit `exit $LastExitCode` results in an emitted script with `asdf` on the first line followed by two `exit` lines. I do see that if I provide a valid command which fails, `$LastExitCode` is set but that doesn't seem to address the OP's example. – Chris Nelson May 24 '16 at 17:31
  • @ChrisNelson Have you tried to run the script in Jenkins? – rrirower May 24 '16 at 17:32
1

I want to add here that I just ran into a quirk: you must have the powershell script end with exit and not return.

My jenkins pipe looked like:

script {
  result = powershell(returnStatus: true, script: '''...if(error condition) { return 1 }''')
  
  if(result) { error }
}

I was using if(error condition) { return 1 } and while the 1 was showing up as the return value in the jenkins console, it was not failing the build. When i used if(error condition) { exit 1 }, the build failed as expected.

I think this is a helpful addition to this thread - the need to use exit and not return. But I don't understand this part: the pipe is checking for result to be non-zero. What is the difference between exit and return in a powershell directive that makes if(result) { error } not work as expected when using return?

Update Feb-16-2021: Coming back to this to add some more notes from my experience: I think what I was doing and what a lot of people do that confuses things, is to use returnStdout or returnStatus and not check them and/or not fully understand what's coming back.

If you use either of those params, Jenkins will not do anything for you based on their value. You have to check them yourself and act accordingly. On the other hand, if you don't use them, Jenkins will recognize a failure code and fail the pipe if one comes back.

Think about it: If you set returnStatus, you're getting the exit code back from the step as a return value and not as something for Jenkins itself to worry about. If you set returnStdout, you're getting the stdout stream - and not any error codes or anything from stderr. So you must check the return for what you want or you will not get the behavior you are expecting.

What I've been doing for a while is actually not setting either of those params and making sure to set $ErrorActionPreference = 'Stop' at the start of any and all PowerShell scripts running in my pipes. That way any powershell failures automatically fail the pipe as expected, without having to check it.

Max Cascone
  • 648
  • 9
  • 25
  • Answering my own question, i think the difference is `return` comes back through `stdout`, while `exit` sets an exit code of the script. Also, `exit 1` means "i failed", while `return 1` means "here's a 1 for you". Lastly, `returnStatus` is looking for the success/failure of the embedded script (specifically, $LASTEXITCODE). So, `returnStatus` with an `exit 1` will report a failure. If i had thought to use `returnStdout`, _then_ the `return 1` would have worked, because it would come through `stdout`. – Max Cascone Nov 06 '20 at 21:09
0

This is how I implemented RRIROWER's solution. Hope it helps.

<yourscript>.ps1; exit $lastexitcode

Make sure your powershell scripts does exit with the desired value.
Run "exit <value>" as the last line.

0

Ultimately, I had to resort to the following configuration in Jenkins as none of the solutions here worked for me. Chris Nelson's answer got me on the right track. We're invoking chef-client remotely so we had to do a little magic to get the remote PS session to talk the local and then pass status on to Jenkins.

  • $res gives the output of chef-client.
  • $lastsuccess is true or false according to PS rules of engagment.

Of course, you'll have to supply your own evironment variables! :)

 Write-host "Deploying $env:Computer with $env:Databag data bag... "
 $secstr = ConvertTo-SecureString $env:Password -AsPlainText -Force 
 $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $env:User, $secstr
 $s = New-PSSession -ComputerName $env:Computer  -Credential $cred
 $res = Invoke-Command -Session $s -ScriptBlock { try {chef-client} catch {exit 1}}
 $lastsuccess = Invoke-Command -Session $s -ScriptBlock {$?}
 Remove-PSSession $s
 write-host " --- "
 write-host $res
 write-host " --- "
 if($lastsuccess)
 {
  write-host "chef deployment completed"
  exit 0
 }
 write-host "chef deployment had errors"
 exit 1
neoscribe
  • 2,203
  • 1
  • 21
  • 18
0

The following example shows that Powershell cmdlets do not set $LASTEXITCODE. Therefore, it is only useful if you're running an executable.

$LASTEXITCODE = 0 # Success
try {
  CommandDoesntExist
} catch {
  # Exit code is unchanged
  Write-Error $Error[0]
  Write-Host "Exit Code $LASTEXITCODE"
}

>> Error Message...
>> Exit Code 0

For pure Powershell you need to handle the error and provide the exit code yourself. To avoid nesting the entire script in a try/catch you can use a trap instead. Local try/catch can still be used for handling blocks you expect to fail.

There is a "Gotcha" if ErrorActionPreference = 'Stop' is set and you use Write-Error instead of Write-Output the trap would stop before getting to the exit.

# Global error handling avoids nesting all
# code in a try/catch/finally.
trap { Write-Host $Error[0] -f Red; exit 1 }

# Block specific error handling
try {
  throw "Worst case scenario"
} catch {
  write-host "Handled error doesn't trigger trap"
}

# Finally example
try {
  throw "Worst case scenario"
} finally {
  write-host "I still cleanup"
}

exit 0
RiverHeart
  • 549
  • 6
  • 15