PowerShell exit code - Calling from MSBuild
Asked Answered
B

3

13

I'm invoking a PowerShell script from MSBuild. MSBuild is able to capture the output returned, but thinks the project is built successfully.

The problem is that the exit code from PowerShell is not passed to the command in MSBuild. Has someone tried this before and been able to throw an exit code to MSBuild?

testmsbuild.proj

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <ScriptLocation>c:\scripts\test.ps1</ScriptLocation>
  </PropertyGroup>

  <Target Name="AfterDropBuild" >
        <Exec Command="powershell.exe -NoProfile -Noninteractive -command &quot;&amp; { $(ScriptLocation)%3Bexit $LASTEXITCODE }&quot; " >
        <Output TaskParameter="ExitCode" PropertyName="ErrorCode"/>
        </Exec>
   </Target>    

 </Project>

test.ps1 (of course this is going to error out)

function CallFromMSBuild {

   Invoke-command {Powershell.exe C:\a.ps1} -computername $computers
} 

When MSBuild project is triggered, it should have caught the issue and the build should have failed (considers build succeeded instead)

When I call from MSBuild

C:\Scripts>msbuild testmsbuild.proj /t:AfterDropBuild
Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.225]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 4/28/2011 2:46:29 PM.
Project "C:\scripts\testmsbuild.proj" on node 1 (AfterDropBuild target(s)).
AfterDropBuild:
  powershell.exe -NoProfile -Noninteractive -command "& { c:\scripts\test.ps1;e
  xit $LASTEXITCODE }"
  Invoke-Command : Cannot validate argument on parameter 'ComputerName'. The argu
  ment is null or empty. Supply an argument that is not null or empty and then tr
  y the command again.
  At C:\install\test.ps1:3 char:58
  +    Invoke-command {Powershell.exe C:\a.ps1} -computername <<<<  $computers
      + CategoryInfo          : InvalidData: (:) [Invoke-Command], ParameterBind
     ingValidationException
      + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.Power
     Shell.Commands.InvokeCommandCommand

Done Building Project "C:\scripts\testmsbuild.proj" (AfterDropBuild target(s)).


Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.04
Bounteous answered 28/4, 2011 at 19:57 Comment(1)
related: #78569Zapateado
N
4

Add exit $lastexitcode to test.ps1

After comment:

Try this in test.ps1:

trap {Write-Host -foreground red $_.Exception.Message; exit 1; continue} Invoke-command   {Powershell.exe C:\a.ps1} -computername $computers

Basically, I don't think MSBuild is at fault here.

I tried your wrong Invoke-Command and the $lastexitcode was set to 0 even though the Invoke-Command had failed! You can test whether this works or not from cmd itself by doing echo %errorlevel% and see you get 1.

November answered 28/4, 2011 at 20:31 Comment(5)
No luck! Still the same. The exit code is 255 when ran from a powershell window directlyBounteous
Thanks for the update. trap { } does the trick! Now the MSbuild says "Build Failed". The catch is it doesn't show the $._Exception.Message thought. All it shows is 'code' C:\scripts\testmsbuild.proj(12,3): error MSB3073: The command "powershell.exe c:\scripts\test.ps1" exited with code 1 'code'. Half the battle is over though :)Bounteous
Can you try without the Write-Host and see...like trap {$_.Exception.Message; exit 1; continue}November
Also I would add the other changes I had to make to get this working. '<Exec Command="powershell.exe -NoProfile -Noninteractive -command &quot;&amp; { $(ScriptLocation) }&quot; " > ' code in testmsproj.proj had to be replaced with '<Exec Command="powershell.exe $(ScriptLocation)" >'Bounteous
What is about geekswithblogs.net/dwdii/archive/2011/05/27/… ?Neutron
Z
15

This question is the top answer on a major search engine. The best answer is this from James Kovacs (of psake fame - i.e., he's kinda FizzBinned on PowerShell and MSBuild integration).

In summary, in a ps1 file:

  1. stick a $ErrorActionPreference='Stop' at the top of your script so it's not the default, which is SilentlyContinue (the trap bit in the accepted answer has the same effect but is far more indirect and confusing)
  2. stick in his function exec {... (or use psake itself)
  3. wrap invocations of external EXEs in an exec { x.exe }
  4. don't need to do any explicit exit ... stuff

The default error handling propagates an exception up as an ERRORLEVEL of 1 from the powershell.exe myscript.ps1, i.e. in an MSBuild <Exec you don't need to do any trickery re telling it to ignore exit codes etc. (unless you want to do something conditional on the specific exit code, in which you want to do IgnoreExitCode="true" and capture it with an <Output element)

A final important thing to understand is that within PowerShell, there's a $? which is the outcome of the last expression (which is not relevant if you're in ErrorAction='Stop' mode) which changes with every thing you do, whereas $LastExitCode is the 'DOS' exit code of the last .exe triggered in the system. Details here - be sure to read the comments

Zapateado answered 1/5, 2011 at 22:41 Comment(4)
Sweet! Thanks for the information. I'm using "Exec function" code mentioned in the blog link to capture the error from another PS script(remoteinvoke.ps1) invoked in remote computers from myscript.ps1. Trap isn't working in those cases. I'll write a detailed post (and link it here) about my findings and how exec helped me solved the issues :DBounteous
@Sanjeev: Happy to hear it. Certainly a trap (on error goto :P) isnt much better than on error resume next in most cases. But it'll definitely be good to get another summary of the bits that are actually relevant as opposed to piling in lots of little things to force it to work...Zapateado
@RubenBartelink what is about geekswithblogs.net/dwdii/archive/2011/05/27/… ?Neutron
@Neutron Well... There are hardcoded C Drive paths and no error handling... Anyway, for me if you're taking that route, stick with a proven one, e.g. https://mcmap.net/q/263522/-any-good-powershell-msbuild-tasksZapateado
N
4

Add exit $lastexitcode to test.ps1

After comment:

Try this in test.ps1:

trap {Write-Host -foreground red $_.Exception.Message; exit 1; continue} Invoke-command   {Powershell.exe C:\a.ps1} -computername $computers

Basically, I don't think MSBuild is at fault here.

I tried your wrong Invoke-Command and the $lastexitcode was set to 0 even though the Invoke-Command had failed! You can test whether this works or not from cmd itself by doing echo %errorlevel% and see you get 1.

November answered 28/4, 2011 at 20:31 Comment(5)
No luck! Still the same. The exit code is 255 when ran from a powershell window directlyBounteous
Thanks for the update. trap { } does the trick! Now the MSbuild says "Build Failed". The catch is it doesn't show the $._Exception.Message thought. All it shows is 'code' C:\scripts\testmsbuild.proj(12,3): error MSB3073: The command "powershell.exe c:\scripts\test.ps1" exited with code 1 'code'. Half the battle is over though :)Bounteous
Can you try without the Write-Host and see...like trap {$_.Exception.Message; exit 1; continue}November
Also I would add the other changes I had to make to get this working. '<Exec Command="powershell.exe -NoProfile -Noninteractive -command &quot;&amp; { $(ScriptLocation) }&quot; " > ' code in testmsproj.proj had to be replaced with '<Exec Command="powershell.exe $(ScriptLocation)" >'Bounteous
What is about geekswithblogs.net/dwdii/archive/2011/05/27/… ?Neutron
B
0

I tried several options but none works for me.

To capture the powershell error on msbuild target, best way is to return the error code from powershell script like $host.SetShouldExit($remotelastexitcode).

e.g. test1.ps1

function CallFromMSBuild {

   Invoke-command {Powershell.exe C:\a.ps1} -computername $computers
   $host.SetShouldExit(1)
} 

<Target Name="AfterDropBuild" >
        <Exec Command="powershell.exe -NoProfile -Noninteractive -command &quot;&amp; { $(ScriptLocation)%3Bexit $LASTEXITCODE }&quot; " >
        <Output TaskParameter="ExitCode" PropertyName="ErrorCode"/>
        </Exec>
   </Target>   

Worth to have a look at https://snagify.wordpress.com/2008/04/06/teambuild-powershell-and-exit-codes/

Blackmarketeer answered 6/11, 2018 at 13:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.