MSBuild Task Factories: guest starring Windows Powershell

Andrew Arnott

One of the cool new features of MSBuild 4.0 is the extensible task factory.  Task factories allow you to include scripts directly in your project file (or an imported .targets file) and have those scripts parsed and executed by your favorite interpreter.  Those scripts might even be C# or VB.NET code snippets that get compiled into assemblies and executed on-the-fly during the build.  This significantly lowers the bar to doing specialized work in your build for which there is no built-in MSBuild Task, since you don’t have to check into your source control repository a precompiled .dll with your custom task any more.  You may still want to do that for performance reasons or for the design-time experience of writing MSBuild tasks in C# and get all the C# Intellisense that you only get in a C# project, but for short tasks, “inline tasks” that are built and executed on-the-fly may be just the ticket you need.

MSBuild 4.0 ships with C#, VB.NET and XAML task factories built-in, so you can define a custom task in C# or VB.NET inline today.  But writing your own task factory will allow you to write inline tasks in Perl, Python, or in this case… Windows Powershell script.

Consider, for the sake of example, that there is no MSBuild task that sends an email at the conclusion of a build.  Fixing that with a custom MSBuild task, compiled into a .dll, seems a bit heavyweight.  But defining that task right there in your project file is easy.  Here’s how you might write a Powershell inline task using the Windows Powershell task factory.  Notice below how we first define the task in terms of the code it executes and its input and output parameters.  Then in a standard MSBuild Target we invoke that inline task as we would any other MSBuild task. 

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="">
      <From Required="true" ParameterType="System.String" />
      <Recipients Required="true" ParameterType="System.String" />
      <Subject Required="true" ParameterType="System.String" />
      <Body Required="true" ParameterType="System.String" />
      <RecipientCount Output="true" />
        $smtp = New-Object System.Net.Mail.SmtpClient
        $smtp.Host = ""
        $smtp.Credentials = [System.Net.CredentialCache]::DefaultCredentials
        $smtp.Send($From, $Recipients, $Subject, $Body)
        $RecipientCount = $Recipients.Split(';').Length
        $log.LogMessage([Microsoft.Build.Framework.MessageImportance]"High", "Send mail to {0} recipients.", $recipientCount)
  <Target Name="Build">
    <SendMail From="$(BuildLabEmail)" Recipients="$(BuildRecipients)" Subject="Build status" Body="Build completed">
      <Output TaskParameter="RecipientCount" PropertyName="RecipientCount" />

This project would build, and send a built completion email to recipients determined by the project itself.  The only assembly you need is the Windows Powershell task factory itself, which can be reused for all your projects that include inline Windows Powershell scripts.

Note that the inline task above will not run without the task factory .dll that it points to, which in this case is not shipped with VS2010.  The Windows Powershell task factory is available as a sample task factory on the MSDN Code Gallery.  Feel free to check it out, and give feedback.  It’s just a sample, so all the “no guarantees for fitness to any particular task” limitations apply.  But you get the source code, so you can tweak it or file bugs if you wish.


Discussion is closed.

Feedback usabilla icon