Using PowerShell from managed code

by Hannes de Jager on 4 October 2011 · 5 comments

in PowerShell

PowerShell… the gracious gift to Sys-Admins from Jeffrey Snover and his team at Microsoft… It needs no introduction and is certainly one of those tools that, once you start using it, you can’t imagine life without it anymore.

But hey, why should Sys-Admins have all the fun with this mighty automation framework and developers be left out in the cold? After all Windows PowerShell provides a hosting mechanism with which the Windows PowerShell runtime can be embedded inside other applications. In fact, this is what the Exchange Management Console does in 2007 and 2010 and also System Center Virtual Machine Manager – It sits on top of Powershell (as dictated by the Microsoft’s Common Engineering Criteria for server products) and delegates all the earth shaking stuff to PowerShell.

Pulling the Powershell engine into your managed code not only allows your application to leverage all of its built-in super-powers and all the extended functionality available from providers like Exchange, MS SQL and SCVMM but it also allows you to leverage all the knowledge and investments made by your system administrator – his code becomes your toolbox!!

So lets see how this can be done:

Pick your favorite .NET language (I’ll be using C#) and add a reference to the System.Management.Automation assembly in your project.

using System;
using System.Collections.ObjectModel;
using System.Management.Automation;

On my machine this assembly was located at C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\

Now create an empty PowerShell pipeline by instantiating the System.Management.Automation.PowerShell class:

PowerShell powershell = PowerShell.Create();

Add some commands to the pipeline:

powershell.AddCommand("Get-Process");

or a more elaborate script:

powershell.AddScript("Get-Process | where { $_.CPU -gt 30 }");

Call the Invoke method to run the commands of the pipeline:

Collection<PSObject> result = powershell.Invoke();

and do something with the result:

foreach (PSObject process in result)
{
  Console.WriteLine("Name: {0}, ID: {1}",
    process.Members["ProcessName"].Value,
    process.Members["Id"].Value
  );
}

Its that simple!

If you want to see if something went wrong:

var errors = powershell.Streams.Error;
if (errors.Count > 0)
{
  var e = errors[0].Exception;
  Console.WriteLine(
    "Hmmm, something went wrong. PowerShell gave me this: {0}",
    e.Message
  );
}

And if you want to execute some more PowerShell with the same object:

powershell.Streams.ClearStreams();
powershell.Commands.Clear();
// ... Go again here...

And there we have the basics that enable us to build a little wrapper class for our reuse:

public class PowershellSession
{
    protected readonly PowerShell powershell;

    public PowershellSession()
    {
        powershell = PowerShell.Create();
    }

    public void RunScript(string scriptText,
        Action<Collection<PSObject>> handler)
    {
        powershell.Streams.ClearStreams();
        powershell.Commands.Clear();
        powershell.AddScript(scriptText, true);
        var results = powershell.Invoke();
        ThrowIfError();
        if (handler != null)
        {
           handler(results);
        }
    }

    public string RunScript(string scriptText)
    {
        var result = new StringBuilder();
        RunScript(scriptText, results =>
        {
            foreach (var line in results)
            {
                if (line != null)
                {
                    result.AppendLine(line.ToString());
                }
            }
        });
        return result.ToString().TrimEnd();
    }

    private void ThrowIfError()
    {
        var errors = powershell.Streams.Error;
        if (errors.Count > 0)
        {
            var e = errors[0].Exception;
            powershell.Streams.ClearStreams();
            throw e;
        }
    }
}

Add some syntactic sugar through extension methods:

public static class PowershellExtentionMethods
{
    private static readonly PowershellSession powerShellSession
       = new PowershellSession();

    public static string PowershellExec(this string commands)
    {
        return powerShellSession.RunScript(commands);
    }

    public static void PowershellExec(this string commands,
        Action<Collection<PSObject>> handler)
    {
        powerShellSession.RunScript(commands, handler);
    }
}

… and we have ourselves a framework for playing with PowerShell like so:

var outlookPid =
    @"$id = Get-Process | where { $_.ProcessName -eq 'OUTLOOK' } | Select id
      $id.Id"
    .PowershellExec();
Console.WriteLine("Process ID for Outlook: {0}", outlookPid);

or how about something a little bit more involved:

Process explorerProcess = null;
"Get-Process | where { $_.ProcessName -eq 'explorer' }"
    .PowershellExec(result =>
        explorerProcess = (Process)result[0].BaseObject);
Console.WriteLine("Killing {0} process with PID {1}...",
    explorerProcess.ProcessName,
    explorerProcess.Id);
explorerProcess.Kill();
A Senior Software Developer aspiring to be a Prototype Developer and/or a Software Architect. Living in Stellenbosch South Africa. I love ideas and complexities that can be combined in unexpected ways to make our life a little simpler. According to Keirsey I’m a Rational Inventor: http://www.keirsey.com/4temps/inventor.asp, which probably means I seem a bit crazy ;-)
//
Hannes de Jager
Share this post:
  • Twitter
  • muti
  • laaik.it
  • Facebook
  • Posterous
  • del.icio.us
  • Digg
  • Live
  • LinkedIn
  • Slashdot
  • StumbleUpon
  • Suggest to Techmeme via Twitter
  • Fark
  • email

Related posts:

  1. Scripting Diskpart with PowerShell
  2. Book review: Microsoft Exchange 2010 PowerShell Cookbook #Microsoft #Exchange #Exchange2010
  3. Using PowerShell to Bulk Create Exchange users with unique passwords
  4. Exchange 2010 PowerShell cookbook–Mike Pfeiffer
  5. PowerShell 3 previews available for download

{ 4 comments… read them below or add one }

Riaan van den Dool October 5, 2011 at 6:02 am

Great article, thanks. I am still waiting for PowerShell to support generics in a more intuitive way, though.

R

Reply

Hannes de Jager October 5, 2011 at 11:20 am

Thanks Riaan. Can’t remember the syntax for generics at the moment though. Can you share with us?

Reply

Riaan van den Dool October 12, 2011 at 8:35 am
Hannes de Jager October 12, 2011 at 9:47 am

Aha! Thanks Riaan!

Reply

Leave a Comment

*

{ 1 trackback }

Previous post:

Next post: