Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PowerShell exit code - Calling from MSBuild

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
like image 236
Sanjeev Avatar asked Apr 28 '11 19:04

Sanjeev


2 Answers

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

like image 147
Ruben Bartelink Avatar answered Sep 21 '22 13:09

Ruben Bartelink


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.

like image 21
manojlds Avatar answered Sep 23 '22 13:09

manojlds