bloginvoke

Using the Exchange Management Shell from C#

Hannes de Jager exchangec-sharp

I’m wanting to build on my previous post titled “Using PowerShell from managed code” to show how to invoke the Exchange 2010 Management Shell from managed code.

Since Exchange 2007, the primary management tool for Exchange is the Exchange Management Shell (EMS). The EMS is really just a PowerShell session with Exchange specific extensions loaded. So in addition to the normal commands like Get-ChildItem and Get-WmiObject we now also get things like Get-Mailbox and Get-ExchangeServer when the EMS is loaded.

So we’ve seen from my previous post that PowerShell can be invoked from within managed (C#). Question is: Can we invoke the Exchange Management Shell from managed code? Affirmative! We do as follows:

Initialize a new C# project as described in: “Using PowerShell from managed code”

Create an empty PowerShell pipeline:

PowerShell powershell = PowerShell.Create();

Load the Exchange Management Shell snap-in into the session:

PSSnapInException ex;
powershell
  .Runspace
  .RunspaceConfiguration
  .AddPSSnapIn(
     "Microsoft.Exchange.Management.PowerShell.E2010", 
     out ex);
if (ex != null)
  throw ex;

Execute the RemoteExchange.ps1 script in the Exchange installation’s bin directory through a dot source:

powershell.AddScript(
  ". $env:ExchangeInstallPath\\bin\\RemoteExchange.ps1");

Create a remote powershell session to the local Exchange server through the Connection-ExchangeServer cmdlet:

powershell.AddScript("Connect-ExchangeServer -auto");

Invoke the added commands and prepare the PowerShell session for further use:

powershell.Invoke();
powershell.Streams.ClearStreams();
powershell.Commands.Clear();

At this point we are ready to execute Exchange Management Shell commands:

powershell.AddScript("Get-Mailbox");

Now before we start writing lots of code lets first build a small wrapper class again:

public class EmsSession
{
    protected readonly PowerShell powershell;
 
    public EmsSession()
    {
        powershell = PowerShell.Create();
        PSSnapInException ex;
        PSSnapInException ex;
        powershell
          .Runspace
          .RunspaceConfiguration
          .AddPSSnapIn(
             "Microsoft.Exchange.Management.PowerShell.E2010", 
             out ex);
        if (ex != null)
          throw ex;
        powershell.AddScript(
          ". $env:ExchangeInstallPath\\bin\\RemoteExchange.ps1");
        powershell.AddScript("Connect-ExchangeServer -auto");
        powershell.Invoke();
        powershell.Streams.ClearStreams();
        powershell.Commands.Clear();
    }
 
    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 EmsExtentionMethods
{
    private static readonly EmsSession emsSession 
       = new EmsSession();
 
    public static string EmsExec(this string commands)
    {
        return emsSession.RunScript(commands);
    }
 
    public static void EmsExec(this string commands,
        Action<Collection<PSObject>> handler)
    {
        emsSession.RunScript(commands, handler);
    }
}

… and enjoy playing with the Exchange Management Shell like so:

var mailBoxGuid = 
    @"$guid = Get-Mailbox tom | select Guid
      $guid.Guid"
    .EmsExec();
Console.WriteLine("GUID for Tom's mailbox: {0}", mailBoxGuid);
Hannes de Jager
Software Builder