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 "& { $(ScriptLocation)%3Bexit $LASTEXITCODE }" " >
<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
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:
$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)function exec {...
(or use psake itself)exec { x.exe }
exit ...
stuffThe 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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With