Skip to content

Instantly share code, notes, and snippets.

@stimpy77
Last active August 29, 2015 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stimpy77/6b0626480e18f9206b98 to your computer and use it in GitHub Desktop.
Save stimpy77/6b0626480e18f9206b98 to your computer and use it in GitHub Desktop.
How to richly enable a PowerShell script in a C# project to run in MSBuild

#How to richly enable a PowerShell script in a C# project to run in MSBuild

  1. Open the .csproj file in Notepad++ (or, right-click the project, choose "Unload Project", then right-click it again and choose "Edit")

  2. Paste the following before the </Project> tag at the end:

     <UsingTask TaskName="InvokePowerShell" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll">
       <ParameterGroup>
           <ScriptFile ParameterType="System.String" Required="true" />
         </ParameterGroup>
         <Task>
           <Reference Include="Microsoft.Build" />
           <Reference Include="System.Management.Automation" />
           <Reference Include="System.Xml" />
           <Using Namespace="System.Management.Automation" />
           <Using Namespace="System.Management.Automation.Runspaces" />
           <Using Namespace="Microsoft.Build.Evaluation" />
           <Code Type="Fragment" Language="cs"><![CDATA[
           if (!ScriptFile.ToLower().EndsWith(".ps1")) return true;
         Project project = ProjectCollection.GlobalProjectCollection.GetLoadedProjects(BuildEngine.ProjectFileOfTaskNode).FirstOrDefault()
             ?? new Project(BuildEngine.ProjectFileOfTaskNode);
         if (!ScriptFile.Contains("\\")) ScriptFile = project.DirectoryPath + "\\" + ScriptFile;
         var runspaceConfig = RunspaceConfiguration.Create();
         using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfig)) 
         { 
             runspace.Open();
             var vars = new System.Text.StringBuilder();
             foreach (ProjectProperty evaluatedProperty in project.AllEvaluatedProperties)
             {
     	        if (!evaluatedProperty.IsEnvironmentProperty)
     	        {
     		        var name = evaluatedProperty.Name;
     		        var value = evaluatedProperty.EvaluatedValue;
     		        if (!string.IsNullOrWhiteSpace(name))
     		        {
     			        if (!string.IsNullOrWhiteSpace(value))
     			        {
     				        if (value.ToLower() == "true" || value.ToLower() == "false") {
     					        vars.AppendLine("$" + name + " = $" + value);
     				        } else {
     					        vars.AppendLine("$" + name + " = @\"\r\n" + value.Replace("\"", "\"\"") + "\r\n\"@");
     				        }
     			        }
     		        }
     	        }
             }
             using (RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace)) 
             { 
                 using (Pipeline pipeline = runspace.CreatePipeline()) {
     		        scriptInvoker.Invoke(vars.ToString()); 
     		        BuildEngine.LogMessageEvent(new BuildMessageEventArgs(ScriptFile, "", "", MessageImportance.High));
     		        pipeline.Commands.AddScript("& \"" + ScriptFile + "\""); 
     		        try {
     			        var results = pipeline.Invoke();
     			        string soutput = "";
     			        foreach (var result in results)
     			        {
     				        soutput += result.ToString();
     			        }
     			        BuildEngine.LogMessageEvent(new BuildMessageEventArgs(soutput, "", "", MessageImportance.High));
     		        } catch (Exception e) {
     			        BuildEngine.LogErrorEvent(new BuildErrorEventArgs("", "", ScriptFile, 0, 0, 0, 0, e.ToString(), "", "", DateTime.Now));
     			        throw;
     		        }
     	        }
             } 
         }
         return true;
     ]]></Code>
         </Task>
       </UsingTask>
       <ItemGroup>
         <AvailableItemName Include="InvokeBefore">
           <Visible>false</Visible>
         </AvailableItemName>
         <AvailableItemName Include="InvokeAfter">
           <Visible>false</Visible>
         </AvailableItemName>
       </ItemGroup>
       <Target Name="PSScriptsBefore" BeforeTargets="Build">
         <InvokePowerShell ScriptFile="%(InvokeBefore.Identity)" Condition="'@(InvokeBefore)' != ''" />
       </Target>
       <Target Name="PSScriptsAfter" AfterTargets="Build">
         <InvokePowerShell ScriptFile="%(InvokeAfter.Identity)" Condition="'@(InvokeAfter)' != ''" />
       </Target>
    
  3. Reload the project

  4. Add a .ps1 file to the project (tip: add a text file as "MyScript.txt", then rename it to "MyScript.ps1")

  5. Right-click on the file, choose Properties, and set the Build Action to "InvokeBefore" or "InvokeAfter".

  6. Open the file and enter echo "$Configuration $AssemblyName $MSBuildProjectDirectory" .. note that this means that ALL MSBuild properties are exposed to you.

@stimpy77
Copy link
Author

FYI a similar strategy is planned for .js files to execute in Node.js

@stimpy77
Copy link
Author

@stimpy77
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment