Posts
49
Comments
83
Trackbacks
0
Sample Code: Calling Exchange cmdlets from .Net code

Now that CDOEXM and WMI providers are gone from Exchange 2007, many custom application developers are probably wondering: "what do I use instead?". The answer is the same as it is for admins who want to move from using CDOEXM/WMI to PowerShell, which is: use the Exchange cmdlets from code.

The PowerShell SDK provides a great set of APIs that will let you host an instance of the PowerShell engine and run cmds / scripts through it. So let's talk about how to code against PowerShell. Think of the PowerShell engine as being similar to the SQL engine. Both engines do specialized jobs, which require a dedicated processing engine. In SQL's case, the processing engine (the database) does all kinds of complicated stuff like joins, sorts, merges, relational queries etc. Well, to access all this good stuff, you need to invoke the database engine---basically you use SQL Command to interact with the engine. PowerShell is very similar: it has lots of specialized logic (pipelines, warning streams, progress streams, errors and so on) that can only be available through the PowerShell engine. Since all the Exchange cmdlets use this good stuff, you need to invoke the PowerShell engine when calling our cmdlets.

Enough talk, here is the sample app (very lightly tested, please use this is a template only).

Important Things to Note:  

You'll note that I don't have any references to Exchange namespaces, except adding the snapin. This is the recommended pattern, see "dealing with PSobject" below. You can also see in the sample that I don't treat the returned data as strong typed data. I deal with everything using "PSObject", which is a generic property bag that PowerShell keeps internally. This is again the recommended pattern. Another reason for dealing with MSHObjects and not casting to a strong type, is that Exchange has not interesting methods on the output classes. That's right---there are no methods on our output classes. The pattern is to use the property collection to find props and set/get them and to use cmdlets to achieve actions (like get/set/add/remove/mount/dismount etc.). The sample assumes an Exchange 2007 installation. Exchange 2007 beta 2 uses Monad RC0, whilst the SDK link above is the newly renamed PowerShell RC1, both are functionally equivalent, however that means that whereever you see PS in the name of a class or type, substitute MSH :)
Caveats:
You may see a 'path cannot be a null' exception when you pass in bad args to the app (and thus to the shell), this is a red herring, its our watson generating code messing up. Fixed in post beta 2 builds. Make sure to quote arguments properly when passing them to the app (and thus to the shell). For example: expshtest 'get-mailbox \"some user with space in their name\" '. Or expshtest 'get-mailboxdatabase \"server1\test db\" ' Make sure to pass in -confirm:$false to Exchange cmdlets that are destructive, or allow the user to pass in that option. Otherwise you'll get a 'host does not support operation' exception from PowerShell (actually right now you get the path cannot be null error. yuck!). This is a super simple app, so make sure to test for null args and so on. I stripped out all the basic stuff to keep the code small.

Sample: 

using System; using System.IO;
using System.Reflection; 
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Host;

namespace Test
{
 class TestOne
 {
  static void TestCallCmdlet(string args)
  {
   try
   {
           RunspaceConfiguration rc = RunspaceConfiguration.Create();            
          
MshSnapInException snapEx = null;

           MshSnapInInfo info = rc.AddMshSnapIn("Microsoft.Exchange.Management.Msh.Admin", out snapEx);

           Runspace r = RunspaceFactory.CreateRunspace(rc);
           r.Open();
           RunspaceInvoke ri = new RunspaceInvoke(r);
           ICollection results = ri.Invoke( args );
           List mailboxList = new List();

           foreach (MshObject mo in results)
           {
             MshPropertyInfo prop = (MshPropertyInfo)mo.Properties["Name"];
             if (prop != null)
                {
                    Console.WriteLine("Cmdlet: " + args + ",
val: " + prop.Value);
                    mailboxList.Add(prop.Value.ToString());
                }
           }
           ri.Dispose();
           r.Close();
   }
   catch (Exception ex)
   {
    Console.WriteLine(ex.Message.ToString() );
   }
  }

  static void Main(string[] args)
  {
   TestCallCmdlet(args[0]);
  }
 }
}

And here is the step to compile this sample (adjust it to match your install points):   

csc expshtest.cs /r:"c:\Program Files\Microsoft Command Shell\v1\System.Management.Automation.dll"

posted on Thursday, July 27, 2006 8:07 AM Print
Comments
Gravatar
# 
Bill Ashcraft
8/22/2006 6:08 AM
  
Can we see this in VB?
Gravatar
# 
sraddhan
11/1/2006 11:11 PM
  
can we remotely access to exchange server ?
Gravatar
# 
Steve
1/16/2008 7:01 PM
  
If you installed powershell
System.Management.Automation.dll Assembly is at
C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0
Gravatar
# 
Raffee
7/23/2007 3:07 AM
  
how can I get the System.Management.Automation dll?
Gravatar
# 
Márcio Martins
8/27/2007 10:08 AM
  
I used this approach to add a reference to System.Management.Automation:

- Extract System.Management.Automation.dll from the GAC running the command in a "cmd" window: copy C:\windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll C:\

Then opened the references window in VS2005 and browsed to the path which I extracted the dll.

It worked ;)
Gravatar
# 
Mark
10/16/2007 11:10 AM
  
How can we extract the value of a multi-valued property? e.g. In Exchange 2007 SP1, StandbyMachines is a MultiValuedProperty. It is a list of lists. We need to extract the value of NodeName from it.
Gravatar
# 
M.Laeeq
10/27/2007 7:10 AM
  
Can we add a reference to System.Management.Automation in VS2003? I tried it but failed to do so (an error occured). But when I added it to VS 2005, it was added successfully. Does it mean that PowerShell API are only accesible via VS 2005 or later.?
Gravatar
# Password problem
J. Tornell
3/11/2008 8:07 AM
  
Hi, how do you set a mailbox password through the invoke? Passwords in Exchange 2007 have to be securestring, as far as I can see, which you can´t combine with a string value.
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
Vivek
8/19/2008 9:18 PM
  
You can create a secure string inside C#. Look at .Net securestring class, it has methods to append(). So what you can do is go through a regular string and append() it 1 by 1 to an empty securestring.
Gravatar
# Ankara nakliyat
ankara nakliyat
10/25/2008 2:40 AM
  
very nice article.
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
Anil
11/24/2008 2:51 PM
  
Should the application run from server installed with exchange server or can be run from any remote server?
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
Vivek
11/24/2008 8:06 PM
  
Anil:
The application can run on any machine--the prerequisite is that you must also have the Exchange management tools installed. But you don't need to install the entire Exchange server binaries.
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
Kumar
12/16/2008 2:09 PM
  
Very nice article.
If I use this code in web service, what permissions does
a user need to have to be set has runas "identity" for application pool?
Or it should run under identity of Local System?
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
Vivek
12/25/2008 4:40 PM
  
Whatever the account you want to run as, it must have some or all Exchange Admin permissions (Org Admin, View Only Admin etc.) for most Exchange cmdlets to work.

Other than that, there is no restriction---you can run under a custom account, network account or local system. I would look up MSDN for guidelines on other asp apps and follow suit.
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
Buddhika
5/24/2009 11:09 PM
  
Can we call this services from Remote PC or by web serives..
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
Vivek
6/28/2009 4:38 PM
  
Re: Buddhika: Yes, as long as you have powershell installed on your client machine.
Gravatar
# Return codes
Petri
7/10/2009 2:06 AM
  
And if something goes wrong when the PS part is run, how do you get that? And how do you prepare for the exceptions?

I assume the try - catch doesn't help you catch problems in PS commands?

Starting the Exchange Management shell takes years, every time you run this, do have to wait until PS is starting on background?
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
winning roulette tricks
7/14/2009 5:13 AM
  
the PowerShell engine as being similar to the SQL engine. Both engines do specialized jobs, which require a dedicated processing engine. In SQL's case, the processing engine (the database) does all kinds of complicated stuff like joins, sorts, merges, relational queries etc. Well, to access all this good stuff, you need to invoke the database engine---basically you use SQL Command to interact with the engine.
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
Vivek
7/14/2009 9:57 AM
  
re: Petri
You don't have to restart the runspace for each cmdlet invocation---you can keep reusing the same runspace over and over again. What I mean is, you can pool cmdlet invocations by doing open() and close() only once per app instance.

Yes you can use try/catch to deal with engine failures. for Cmdlet failures, I suggest looking at the return errors (either by looking at the variable called $error, or looking at the last invocation status of the runspace).

For more info, try http://msdn.microsoft.com/powershell
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
SyntaxError
9/7/2009 1:12 AM
  
Thanks for that nice article.
I was wondering if there were a way to set personal mailbox folder permissions of any exchange mailbox owner ?
Maybe something more simple than what I read here : www.codeproject.com/.../setexchfolderperms.aspx

Thanks in advance for your answer.

Post Comment

Title *
Name *
Email
Url
Comment *  
Please add 3 and 8 and type the answer here:
News
A little slow these days as I'm busy working on exchangelabs.com. I will try and post tidbits when I get some time. Enjoy the older posts till then!