This seems to be  a never ending story but, believe me, there  are so many ways to achieve the same result! This time we are going to use another simple technique for running our powershell scripts without invoking powershell.exe.

Introducing MSBuild.exe…

MSBuild is a tool included in .Net frameworks for automating the processes of creating software products, including compiling the source code, packaging, testing , deployment and creating documentation.

Msbuild relies on .csproj files which have an XML syntax and contain all the instructions for successfully building a .Net assembly (think of it as the equivalent of the “make” utility in unix)

It’s interesting to note that you can include taks in your .csproj file which will be executed during the build process and even more interesting fact, given the intrinsic characteristics of .Net,  is that these tasks can be implemented using C# code.

This is the right place to inject our PS-less code!

So this is our test.csproj file:

<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="MSPSLess">
</Target>
<PSLess/>
<UsingTask TaskName="PSLess" TaskFactory="CodeTaskFactory"
AssemblyFile=
"C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
<Task>
<Code Type="Class" Lang="cs">
<![CDATA[
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Runtime.InteropServices;
using System.IO;
using System;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class PSLess :  Task
{
  public override bool Execute()
  {
   string buffer = "";
   try
   {
     buffer = File.ReadAllText("c:/test/test.ps1");
   }
   catch (Exception e)
   {
    Console.WriteLine("error:" +e.Message);
    Environment.Exit(2);
   }
   buffer=RunScript(buffer);
   Console.WriteLine(buffer);
   return true;
 }
 private static string RunScript(string script)
  {
    Runspace MyRunspace = RunspaceFactory.CreateRunspace();
    MyRunspace.Open();
    Pipeline MyPipeline = MyRunspace.CreatePipeline();
    MyPipeline.Commands.AddScript(script);
    MyPipeline.Commands.Add("Out-String");
    Collection<PSObject> outputs = MyPipeline.Invoke();
    MyRunspace.Close();
    StringBuilder sb = new StringBuilder();
    foreach (PSObject pobject in outputs)
    {
      sb.AppendLine(pobject.ToString());
    }
    return sb.ToString();
   }
}

]]>
< / Code>
</Task>
</UsingTask>
</Project>

We will go to exploit the task defined as “PSLess”,  a custom inline task.

“CodeTaskFactory” is the class which implements the inline tasks and is  stored in the assembly file  “Microsoft.Build.Tasks.v4.0.dll”. Please note that the location can vary because it depends on your .Net Framework version.

We tell  MSBuild that our task is written in C# and , as you can see, the core C# program  looks like the one in part 3.  In this case our “PSLess” class is inherited from “Task” class and we will override the Execute() method for calling  our C# code.

Time to  test it, we will again use our simple “test.ps1” script.

echo "hello from powershell-less"
echo "this is your pid:$PID"
$psversiontable

 

csproj

Again, it worked!

A much more sophisticated example with a fully interactive powershell written by my friend @cneelis can be found here: https://github.com/Cn33liz/MSBuildShell

That’s all 😉

Leave a comment