5

Anyone know of any good MSBuild tasks that will execute a PowerShell script and pass it different parameters?

I was able to find B# .NET Blog: Invoking PowerShell scripts from MSBuild, but I'm hoping for something that is a little more polished.

If I can't find anything I will of course just go ahead and polish my own using that blog post as a starter.

Chris Nielsen
  • 14,731
  • 7
  • 48
  • 54
Eric Schoonover
  • 47,184
  • 49
  • 157
  • 202
  • I'd use the blog post as a starter. We're working with a consultant from Microsoft and it's the post he referred us to. I tried the Powershell MSBuild task on codeplex, and it errors out on a Powershell script of mine that works in the Bart De Smet-authored task. – Scott Lawrence Jan 22 '10 at 14:11

4 Answers4

9

One could use http://powershellmsbuild.codeplex.com/ for 3.5. It'd be nice if there was a NuGet package for it that one could leverage via NuGet package restore.

4.0 has a Windows Powershell Task Factory which you can get in the code gallery has been rolled into MSBuild Extension Pack (one of the top task libraries - 400+ Tasks & recommended in Inside MSBuild) has PowerShellTaskFactory (download the help file from the download section of this example release to have a peek).

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • Nicer would be a Microsoft-supplied functional subset of PowerShell (wouldn't want unrestricted side effects to mess with the mostly-declarative nature of project files and msbuild's change detection). Existing [Property functions](https://learn.microsoft.com/en-us/visualstudio/msbuild/property-functions?view=vs-2017) seem to be a start on this (at least they have PowerShell syntax) – unbob Sep 17 '18 at 20:30
7

You might also want to look at Psake - a PowerShell based build environment.

Roman
  • 19,581
  • 6
  • 68
  • 84
Steven Murawski
  • 10,959
  • 41
  • 53
5

Duplicate Question and Answer I Posted, here for posterity for when it has been vote to closed. The key difference is that this question was constrained to being OOTB and my self-answer stays within that constraint.

Question

Powershell doesn't seem to have an easy way to trigger it with an arbitrary command and then bubble up parse and execution errors in a way that correctly interoperates with callers that are not PowerShell - e.g., cmd.exe, TeamCity etc.

My question is simple. What's the best way for me with OOTB MSBuild v4 and PowerShell v3 (open to suggestions-wouldnt rule out a suitably production ready MSBuild Task, but it would need to be a bit stronger than suggesting "it's easy - taking the PowerShell Task Factory sample and tweak it and/or becoming it's maintainer/parent") to run a command (either a small script segment, or (most commonly) an invocation of a .ps1 script.

I'm thinking it should be something normal like:

<Exec 
  IgnoreStandardErrorWarningFormat="true"
  Command="PowerShell &quot;$(ThingToDo)&quot;" />

That sadly doesn't work:-

  1. if ThingToDo fails to parse, it fails silently
  2. if ThingToDo is a script invocation that doesn't exist, it fails
  3. if you want to propagate an ERRORLEVEL based .cmd result, it gets hairy
  4. if you want to embed " quotes in the ThingToDo, it won't work

So, what is the bullet proof way of running PowerShell from MSBuild supposed to be? Is there something I can PsGet to make everything OK?

Answer

Weeeeelll, you could use something long winded like this until you find a better way:-

<PropertyGroup>
  <__PsInvokeCommand>powershell "Invoke-Command</__PsInvokeCommand>
  <__BlockBegin>-ScriptBlock { $errorActionPreference='Stop';</__BlockBegin>
  <__BlockEnd>; exit $LASTEXITCODE }</__BlockEnd>
  <_PsCmdStart>$(__PsInvokeCommand) $(__BlockBegin)</_PsCmdStart>
  <_PsCmdEnd>$(__BlockEnd)"</_PsCmdEnd>
</PropertyGroup>

And then 'all' you need to do is:

<Exec 
  IgnoreStandardErrorWarningFormat="true"
  Command="$(_PsCmdStart)$(ThingToDo)$(_PsCmdEnd)" />

The single redeeming feature of this (other than trapping all error types I could think of), is that it works OOTB with any PowerShell version and any MSBuild version.

I'll get my coat.

Community
  • 1
  • 1
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
3

With a bit of fun, I managed to come up with a fairly clean way of making this work:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- #1 Place this line at the top of any msbuild script (ie, csproj, etc) -->
  <PropertyGroup><PowerShell># 2>nul || type %~df0|find /v "setlocal"|find /v "errorlevel"|powershell.exe -noninteractive -&amp; exit %errorlevel% || #</PowerShell></PropertyGroup>

  <!-- #2 in any target you want to run a script -->
  <Target Name="default" >

    <PropertyGroup> <!-- #3 prefix your powershell script with the $(PowerShell) variable, then code as normal! -->
      <myscript>$(PowerShell)
      #
      # powershell script can do whatever you need.
      #
      dir ".\*.cs" -recurse |% {
        write-host Examining file named:  $_.FullName
        # do other stuff here...
      } 
      $answer = 2+5
      write-host Answer is $answer !
      </myscript>
    </PropertyGroup>

    <!-- #4 and execute the script like this -->
    <Exec Command="$(myscript)" EchoOff="true" /> 
  </Target>
</Project>

Notes:

  • You can still use the standard Exec Task features! (see: https://msdn.microsoft.com/en-us/library/x8zx72cd.aspx)
  • if your powershell script needs to use < > or & characters, just place the contents in a CDATA wrapper:

    <script2><![CDATA[  $(PowerShell)
      # your powershell code goes here!
      write-host "<<Hi mom!>>"
    ]]></script2>
    
  • if you want return items to the msbuild script you can get them:

    <script3>$(PowerShell)
      # your powershell code goes here!
      (dir "*.cs" -recurse).FullName
    </script3>
    
    <Exec Command="$(script3)" EchoOff="true" ConsoleToMSBuild="true"> 
        <Output TaskParameter="ConsoleOutput" PropertyName="items" />
    </Exec>
    <Touch Files="$(items)" /> 
    

See! then you can use those items with another msbuild Task :D

Garrett Serack
  • 943
  • 7
  • 17
  • I like it. I've seen [similar](https://dmitrysotnikov.wordpress.com/2008/06/27/powershell-script-in-a-bat-file/#comment-6701) [things](https://walid-toumi.blogspot.com/2011/04/inclure-du-code-ps-dans-un-fichier.html) done by Walid Toumi outside of msbuild (and used them to good effect). Thanks for taking the time to debug the nitpicky stuff. The concept is easy, I'm guessing that getting it to actually work might not've been (or you could be lucky or a genius:-) In my environment, I needed to add -executionpolicy remotesigned. – unbob Sep 17 '18 at 20:25