Running Exchange 2010 Management Shell Commands (PowerShell) with C#

The goal is to be able to run C# code on the Exchange server and show that we’ve have all the Microsoft Exchange 2010 PowerShell commands (cmdlets) available.

If what you want to do is execute the C# code on a client machine that executes cmdlets on the Exchange server, check out this post.

This is the code that we want to run on the server:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace PowerShellTest
{
    class Program
    {
        static void Main(string[] args)
        {

            var rsConfig = RunspaceConfiguration.Create();
            PSSnapInException snapInException;

            // NOTE 1: The project's platform target must match the server's hardware architecture (x64 in my case)
            var snapinInfo = rsConfig.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010", out snapInException);

            var runspace = RunspaceFactory.CreateRunspace(rsConfig);

            runspace.Open();
            var pipeline = runspace.CreatePipeline();
            var command = new Command("get-command");
            pipeline.Commands.Add(command);

            // NOTE 2: Your code cannot be running the .NET Framework 4 .  3.5 or lower is ok.
            var results = pipeline.Invoke();

            foreach (var cmd in results)
            {
                string cmdletName = cmd.Properties["Name"].Value.ToString();
                Console.WriteLine(cmdletName);
            }

            runspace.Dispose();

        }
    }
}

Output:

%
?
A:
ac
Add-ADPermission
Add-AvailabilityAddressSpace
Add-Computer
Add-Content
Add-ContentFilterPhrase
Add-DatabaseAvailabilityGroupServer
Add-DistributionGroupMember
Add-FederatedDomain
Add-History
...
Enable-Mailbox
New-Mailbox
...

Now to the details on how to make the code work:

You first must install the Windows PowerShell 2.0 SDK, mostly to be able to reference the System.Management.Automation.dll . For testing purposes, I installed Visual Studio on the server running Exchange (which is a test server). My ultimate goal (and maybe future post) is to run the C# code on a remote machine.

If you already have PowerShell 2.0 on your machine (which Windows 7 and Windows Sever 2008 R2 already do) I ran into some posts that said you could manually edit your .csproj file and add a reference to System.Management.Automation . This is how I initially did my tests but then hell broke loose (not sure if this was the cause of the problems) so I followed the formal rules and installed the SDK.

Then create the Console Application in Visual Studio and add a reference to the System.Management.Automation.dll . If you installed the SDK with the default options, you will find the dll here:

image

Copy paste the C# code above into your Program.cs file. If you build and run the code, you will most likely run into two strange errors; they will both happen right below my comment lines that start with NOTE. I use Visual Studio .NET 2010 which by default uses the .NET Framework 4 and for some reason the default project’s platform target is 32 bit (x86). Both of these defaults turned out to be a problem.

NOTE 1: 

I was getting the a “No snap-ins have been registered for Windows PowerShell version 2” error on that line:

System.Management.Automation.PSArgumentException was unhandled
  Message=No snap-ins have been registered for Windows PowerShell version 2.
  Source=System.Management.Automation
  ParamName=psVersion
  StackTrace:
       at System.Management.Automation.PSSnapInReader.GetMshSnapinRootKey(RegistryKey versionRootKey, String psVersion)
       at System.Management.Automation.PSSnapInReader.Read(String psVersion, String mshsnapinId)
       at System.Management.Automation.Runspaces.MshConsoleInfo.AddPSSnapIn(String mshSnapInID)
       at System.Management.Automation.Runspaces.RunspaceConfigForSingleShell.DoAddPSSnapIn(String name, PSSnapInException& warning)
       at System.Management.Automation.Runspaces.RunspaceConfiguration.AddPSSnapIn(String name, PSSnapInException& warning)
       at PowerShellTest.Program.Main(String[] args) in C:\Users\Administrator\Desktop\PowerShellTest\PowerShellTest\blog.cs:line 20
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

To fix it I just made the project’s Platform Target for All Configurations be x64.

image

NOTE 2:

I was getting a “Mixed mode assembly is built against version ‘v2.0.50727’ of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.” error on that line:

System.Management.Automation.CmdletInvocationException was unhandled
  Message=The type initializer for 'Microsoft.Exchange.Configuration.Tasks.Task' threw an exception.
  Source=System.Management.Automation
  WasThrownFromThrowStatement=false
  StackTrace:
       at System.Management.Automation.CommandProcessor.Init(CmdletInfo cmdletInformation)
       at System.Management.Automation.CommandInfo.GetMergedCommandParameterMetdata()
       at System.Management.Automation.CommandInfo.get_ParameterSets()
       at Microsoft.PowerShell.Commands.GetCommandCommand.AccumulateMatchingCommands(Collection`1 commandNames)
       at System.Management.Automation.CommandProcessor.ProcessRecord()
  InnerException: System.TypeInitializationException
       Message=The type initializer for 'Microsoft.Exchange.Configuration.Tasks.Task' threw an exception.
       Source=Microsoft.Exchange.Configuration.ObjectModel
       TypeName=Microsoft.Exchange.Configuration.Tasks.Task
       StackTrace:
            at Microsoft.Exchange.Configuration.Tasks.Task.AssemblyResolveEventHandler(Object sender, ResolveEventArgs args)
            at System.AppDomain.OnAssemblyResolveEvent(RuntimeAssembly assembly, String assemblyFullName)
       InnerException: System.IO.FileLoadException
            Message=Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.
            Source=Microsoft.Exchange.Data.Directory
            StackTrace:
                 at Microsoft.Exchange.Data.Directory.DSAccessTopologyProvider..ctor(String machineName)
                 at Microsoft.Exchange.Data.Directory.DSAccessTopologyProvider..ctor()
                 at Microsoft.Exchange.Data.Directory.DirectoryServicesTopologyProvider.DiscoverConfigDC()
                 at Microsoft.Exchange.Data.Directory.DirectoryServicesTopologyProvider..ctor()
                 at Microsoft.Exchange.Data.Directory.TopologyProvider.InitializeInstance()
                 at Microsoft.Exchange.Data.Directory.TopologyProvider.GetInstance()
                 at Microsoft.Exchange.Data.Directory.ADSession.GetConnection(String preferredServer, Boolean isWriteOperation, Boolean isNotifyOperation, String optionalBaseDN, ADObjectId& rootId, ADScope scope)
                 at Microsoft.Exchange.Data.Directory.ADSession.GetReadConnection(String preferredServer, String optionalBaseDN, ADObjectId& rootId, ADRawEntry scopeDeteriminingObject)
                 at Microsoft.Exchange.Data.Directory.ADSession.Find(ADObjectId rootId, String optionalBaseDN, ADObjectId readId, QueryScope scope, QueryFilter filter, SortBy sortBy, Int32 maxResults, IEnumerable`1 properties, CreateObjectDelegate objectCreator, CreateObjectsDelegate arrayCreator, Boolean includeDeletedObjects)
                 at Microsoft.Exchange.Data.Directory.ADSession.Find(ADObjectId rootId, QueryScope scope, QueryFilter filter, SortBy sortBy, Int32 maxResults, IEnumerable`1 properties, CreateObjectDelegate objectCtor, CreateObjectsDelegate arrayCtor)
                 at Microsoft.Exchange.Data.Directory.ADSession.Find[TResult](ADObjectId rootId, QueryScope scope, QueryFilter filter, SortBy sortBy, Int32 maxResults, IEnumerable`1 properties)
                 at Microsoft.Exchange.Data.Directory.SystemConfiguration.ADSystemConfigurationSession.Find[TResult](ADObjectId rootId, QueryScope scope, QueryFilter filter, SortBy sortBy, Int32 maxResults)
                 at Microsoft.Exchange.Data.Directory.SystemConfiguration.ADSystemConfigurationSession.FindServerByFqdn(String serverFqdn)
                 at Microsoft.Exchange.Data.Directory.SystemConfiguration.ADSystemConfigurationSession.FindLocalServer()
                 at Microsoft.Exchange.Configuration.SQM.CmdletSqmSession.GetOptInStatus()
                 at Microsoft.Exchange.Configuration.SQM.SqmSession.UpdateData(Boolean flushToDisk, Boolean closing)
                 at Microsoft.Exchange.Configuration.SQM.SqmSession.OnCreate()
                 at Microsoft.Exchange.Configuration.SQM.SqmSession.Open()
                 at Microsoft.Exchange.Configuration.SQM.CmdletSqmSession..ctor()
                 at Microsoft.Exchange.Configuration.SQM.CmdletSqmSession.get_Instance()
                 at Microsoft.Exchange.Configuration.Tasks.Task..cctor()
            InnerException:

To fix it I made the project’s Target Framework be the .NET Framework 3.5.

image

I mostly followed the instructions on this article, but they were made for Exchange 2007.

Advertisements

9 thoughts on “Running Exchange 2010 Management Shell Commands (PowerShell) with C#

  1. Pingback: Creating an Exchange 2010 Mailbox from a remote C# program « Pedro Liska's Blog
  2. To fix the issue with “Mixed mode assembly is built against version ‘v2.0.50727” I added the following lines in my app.config :

    • Mo: WordPress supressed your XML. It was probably something like “run legacy dlls or something like that?” I would be interested posting it here somewhere. Want to email it to me at pedro at pedroliska dot com ? I’ll then figure out how to make wordpress happy. Thanks!

      • Dhaval: Mo didn’t get get back to me, so I’m not sure what he did. If you figure out let me know and I’ll post it here.

  3. Hi Pedro I am finding difficulties (errors similar to ones mentioned in this post) in executing exchange 2010 cmdlets through c# in a .net 4 runtime environment. Any other ideas to make it work that does not involve reverting back to .net 3.5?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s