Saturday, June 9, 2012

Using PowerShell in a .NET installer for a SharePoint App

powershell_2This is not a post about how to invoke PowerShell from within .NET or why you should do it. It is a post about second guessing why you write code the way you do.

At Puzzlepart we have created a time tracking application and we have an .exe installer to provision a site, and create all lists and content types needed. Some weeks ago we decided to have the installer deploy the WSP as well, and not only to post-deploy work.

I already had a PowerShell script at hand which deployed the WSP with some extra functionality like uninstalling a previous version and waiting to make sure the deploy timer job finished and that the WSP was properly deployed and installed before ending. Just the nifty script I needed for the job.

I fired up Visual Studio and in no time I had embedded the .ps1 script as a resource and was invoking it runtime from .NET.

var asm = Assembly.GetExecutingAssembly();
Stream stream = asm.GetManifestResourceStream("Pzl.Did.Installer.wspdeploy.ps1");

string script;
using (StreamReader reader = new StreamReader(stream))
{
    script = reader.ReadToEnd();
    script = script.Replace("$webAppUrl", webAppUrl);
}

RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();

RunspaceInvoke rsi = new RunspaceInvoke();
ScriptBlock scriptblock = rsi.Invoke("{" + script + "}")[0].BaseObject as ScriptBlock;
var objects = scriptblock.Invoke();
foreach (PSObject psObject in objects)
{
    var solution = psObject.BaseObject as SPSolution;
    if (solution != null && solution.Deployed)
    {
        Console.WriteLine("WSP Deployed");
    }
    Console.WriteLine(psObject);
}

Now, let’s step back for a second. Being a Reflector addict I’m actually very aware that SharePoint PowerShell cmdlet’s are using the regular SharePoint API’s internally. In this case for example the SPSolution class. So why on earth did I hack in invoking a PowerShell script instead of using the native SharePoint API directly?

I had made the existing code more complex by:
  • Embedded a resource
  • Added a new DSL
  • Added code not easily read
The answer is of course that I had the script and figured I’d just throw it in there as a quickie with no thought about maintainability and readability.

Lucky for me my colleague Mads (@madsnissen) do code review and sent me an e-mail with the quote:
What is it that your PowerShell script does which the native API can’t provide?
As I know Mads pretty well the understatement hit me right in the gut, and of course I didn’t have a good answer, as there is none. I had  as a coder made a stupid mistake (or “bæsjet på leggen” as we say in Norwegian).

Using .NET to create new cmdlet’s is  good idea, invoking PowerShell from .NET is usually not. Take the code from the cmdlet and invoke it native instead.

Lesson learned

I will be more conscious about the code I write, and why I write it. And I will at all costs try to avoid the ridicule I’ve gotten the past week for my PowerShell skills Winking smile