We are working to get official public documentation on this subject, I will update this post once we get a KB published... Symptoms When you try to execute Exchange Powershell cmdlets from an application which is impersonating a user. You get the following error: "Access to the address list service on all Exchange 2007 servers has been denied" Cause Exchange Management Shell currently (Exchagen 2007 RTM) actively blocks calls made with impersonated credentails. This is typically seen in an ASP.NET application which impersonates the client's user credentials and attempts call an Exchange Powershell cmdlet such as New-Mailbox. Resolution You will need to execute Exchange cmdlets inside another process running with evelated permissions. You can use COM+ or .NET Remoting to accomplish this. Understanding Enterprise Services (COM+) in .NET http://msdn2.microsoft.com/en-us/library/ms973847.aspx .NET Framework Remoting Overview http://msdn2.microsoft.com/en-us/library/kwdt6w2k.aspx More Information This is very similar to limitations with CDOEXM and impersonation in Exchange 2000 and 2003. As seen in the following KB article: Recommendations for using Exchange system management features through a Web interface that uses CDO for Exchange Management http://support.microsoft.com/kb/900230
This works now, Dan has the update…
I’m glad to see that many of you are picking up Exchange 2007 and starting to write applications against it! Many of the early cases I have been working on regarding Exchange 2007 are related to automating Powershell. To me this makes sense that it would be an early call generator for two reasons: 1) it is vastly different from any other “API” interaction we have had in the past and 2) Powershell is a replacement for CDOEXM and WMI which were removed completely from Exchange 2007’s API set.
There are four main “development” tasks related to Powershell including: creating cmdlets, creating snap-ins, creating Powershell providers, and automating existing cmdlets in providers. My first two introductory posts focus solely on automating the Exchange cmdlets provided by the Exchange Management Shell snap-in.
This first post will serve as a primer to get you thinking about Powershell and understanding the concepts involved in automation. The second post will provide some sample code and more specific discussion of issues related to Powershell automation with Exchange 2007…
Get to know Powershell in general
In order to understand what considerations to take in your application that will automate Powershell, you must have a reference for the architecture and operations behind it. Here is a great place to start…
How Windows PowerShell Works
http://msdn2.microsoft.com/en-us/library/ms714658.aspx
Windows Powershell Getting Started Guide
http://msdn2.microsoft.com/en-us/library/aa973757.aspx
Use cmdlets in Exchange Management Shell first
The first steps in automating Powershell are to understand what cmdlet it is you want to automate, the parameters you need to pass it, and the output it is going to give you back. The easiest way to do this is to get all this working in the shell first and then work it into your automation code. Here are some helpful links to get you started using the Exchange Management Shell and the Exchange cmdlets available via the Exchange snap-in…
Using the Exchange Management Shell
http://www.microsoft.com/technet/prodtechnol/exchange/e2k7help/925ad66f-2f05-4269-9923-c353d9c19312.mspx?mfr=true
http://technet.microsoft.com/en-us/library/bb123778.aspx
Exchange 2007 Cmdlet List
http://technet.microsoft.com/en-us/library/bb123703.aspx
Exchange Management Shell Quick Reference
http://www.microsoft.com/downloads/details.aspx?FamilyId=01A441B9-4099-4C0F-B8E0-0831D4A2CA86&displaylang=en
Understand the automation objects
Now that you know what cmdlets you want to automate and what input and output you expect to work with, the next step is to understand the automation objects. The bulk of these objects are found in the System.Management.Automation namespace. Here are links to the SDK references for Powershell…
Windows Powershell SDK
http://msdn2.microsoft.com/en-us/library/ms714469.aspx
Powershell Managed Reference
http://msdn2.microsoft.com/en-us/library/aa717491.aspx
The best way to understand how to use these classes is to just see some code. Vivek Sharma is an Exchange PM focusing on Exchange Powershell; his blog is a great resource. He provides an example of using the System.Management.Automation namespace for invoking cmdlets from .NET code…
Sample Code: Calling Exchange cmdlets from .NET code
http://www.viveksharma.com/techlog/2006/07/27/sample-code-calling-exchange-cmdlets-from-net-code
http://www.viveksharma.com/TECHLOG/archive/2006/07/27/sample-code-calling-exchange-cmdlets-from-net-code.aspx
There are a couple important things to notice in his sample code…
First, this code was written in the Exchange 2007 Beta timeframe so Powershell was still being referred to as “Monad” so instead of PS (Powershell) objects you see references to MSH (Monad Scripting Host) objects. Most of the time you can just exchange PS for MSH in the object names, my examples will always references the RTM objects with PS in the object names so you won’t have to translate them.
Second, look to what his code has to do as compared to what you now know about Powershell. The System.Management namespace is generic and relates to Windows Powershell, there is nothing specific here to Exchange. Therefore, just like if you were using the shell, you have to load the Exchange snap-in to get access to the Exchange specific cmdlets.
On to Part 2…
This should be a good framework for understanding Powershell and how we will use it to automate the management of Exchange 2007. In Part 2 I will get into more specifics about common issues that we have identified so far and some more sample code.
Other References
Windows Powershell Team Blog
http://blogs.msdn.com/powershell/
Exchange 2007 Powershell Scriptacular Demo Pack
http://www.viveksharma.com/techlog/2006/12/21/announcing-the-exchange-2007-powershell-scriptacular-demo-pack/
ExchangeNinjas: Powershell Scripts
http://www.exchangeninjas.com/PSSCategories
Updated 1/22/2009 – Fixed a broken link.
Naturally the first case I had was how to create a mailbox using Powershell cmdlets from ASP.NET given that CDOEXM is no longer available. From there we have had other bridges to cross as customers are upgrading 32-bit applications to work on Exchange 2007 (which is 64-bit) and replacing old CDOEXM code with Exchange Management Shell cmdlet automation. Through these early cases I identified a couple common themes and created some sample code that I thought might be helpful to share here…
Exchange Cmdlets and Impersonation
Much like we saw with CDOEXM, currently we don’t recommend or support using Exchange Powershell cmdlets with impersonation. In the case of creating a mailbox in an ASP.NET application we would recommend that you use COM+ or .NET Remoting to host your Powershell code, have that code run as a service account with permissions to execute the Powershell cmdlets and then implement authorization logic to evaluate if the caller has permission to execute the code. Click here for more information.
Exchange Management Shell Snap-in and 32-bit Applications
This typically isn’t a problem when writing an application in VB.NET or C# because the projects default to 64-bit on 64-bit machines but if you are trying to load the Exchange Management Shell Snap-in in a 32-bit instance of Powershell you will get the following error…
"No Windows PowerShell Snap-ins are available for version 1"
…This is because Exchange 2007 is a 64-bit application and therefore the snap-in is also 64-bit and cannot be loaded into a 32-bit instance of the shell. Click here for more information.
C# Sample – New-Mailbox
Here is sample of a function which takes input to create a new mailbox using the New-Mailbox cmdlet…
public static void CreateUserMailbox(string domain, string ou,
string database, string alias, string name,
string displayName, SecureString password)
{
string upn = string.Format("{0}@{1}", alias, domain);
ICollection<PSObject> results;
// Create a runspace. We can't use the RunspaceInvoke class this time
// because we need to get at the underlying runspace to explicitly
// add the commands.
RunspaceConfiguration rc = RunspaceConfiguration.Create();
PSSnapInException snapEx = null;
PSSnapInInfo info = rc.AddPSSnapIn(
"Microsoft.Exchange.Management.PowerShell.Admin",
out snapEx);
Runspace myRunSpace = RunspaceFactory.CreateRunspace(rc);
myRunSpace.Open();
// Create a pipeline...
Pipeline pipeLine = myRunSpace.CreatePipeline();
using (pipeLine)
// Create a command object so we can set some parameters
// for this command.
Command newMbx = new Command("new-mailbox");
newMbx.Parameters.Add("alias", alias);
newMbx.Parameters.Add("database", database);
newMbx.Parameters.Add("password", password);
newMbx.Parameters.Add("Name", name);
newMbx.Parameters.Add("DisplayName", displayName);
newMbx.Parameters.Add("UserPrincipalName", upn);
newMbx.Parameters.Add("OrganizationalUnit", ou);
// Add the command we've constructed
pipeLine.Commands.Add(newMbx);
// Execute the pipeline and save the objects returned.
results = pipeLine.Invoke();
// Print out any errors in the pipeline execution
// NOTE: These error are NOT thrown as exceptions!
// Be sure to check this to ensure that no errors
// happened while executing the command.
if (pipeLine.Error != null && pipeLine.Error.Count > 0)
Trace.WriteLine("ERROR: There were pipeline errors...\n");
foreach (object item in pipeLine.Error.ReadToEnd())
Trace.WriteLine("Error: " + item.ToString() + "\n");
}
// Print out the results of the pipeline execution
if (results != null && results.Count > 0)
Trace.WriteLine("MAILBOXES CREATED: Created the following mailboxes...\n");
foreach (PSObject ps in results)
if (ps.Members["UserPrincipalName"].Value != null)
Trace.WriteLine("UserPrincipalName: "
+ ps.Members["UserPrincipalName"].Value + "\n");
pipeLine = null;
myRunSpace.Close();
myRunSpace = null;
Notice that I use a Pipeline instead of RunspaceInvoke (like Vivek did). RunspaceInvoke works fine in a lot of cases but the advantage I found with the Pipeline is that it is quite easy to add non-string parameters like the SecureString object to the cmdlet call. Also, note that while the Invoke method returns a result array of PSObjects there is also an Error property which has an array of exceptions. This error information is generally going to be validation type errors, for instance in the case of New-Mailbox I might get an error that the mailbox already in this property but I would get an exception if I misspelled New-Mailbox.
Sample Code on the Web
Vivek Sharma: Sample Code: Calling Exchange cmdlets from .NET Code
Glen Scales: Quick Create Mailbox Powershell Form for Exchange 2007
http://gsexdev.blogspot.com/2006/12/quick-create-mailbox-powershell-form.html
Glen Scales: Scripting Exchange Web Services (2007) with VBS and Powershell
http://gsexdev.blogspot.com/2006/12/scripting-exchange-web-services-2007.html
Glen Scales: Exchange SMTP Log file DNS Test tool Powershell script
http://gsexdev.blogspot.com/2006/12/exchange-smtp-log-file-dns-test-tool.html
…this isn’t necessarily related but I had to include it because it is just so cool…
Glen Scales: Geolocating Exchange Message Tracking with Powershell (Exchange 2000/2003) (Seeing what countries your messages/Spam are coming from)
http://gsexdev.blogspot.com/2006/11/geolocationing-exchange-message.html
We are working to get offical public documentation on this subject, I will update this post once we get a KB published...
Problem Description
When writing .NET code which uses the System.Management.Automation namespace or using Windows Powershell you may receive the following error when you attempt to load the Exchange Management Shell snap-in.
Resolution
Exchange 2007 is only supported on 64-bit Windows and is, itself, a 64-bit application, therefore many of the components including the shell extensions are 64-bit.
On a 64 bit version of Windows with Powershell installed there are two versions of Powershell.exe. One is the 32-bit version (found at C:\WINNT\syswow64\windowspowershell\v1.0\powershell.exe) and the other is the 64-bit version (found at C:\WINNT\system32\windowspowershell\v1.0\powershell.exe). The Exchange Management Shell snap-in will only load into the 64-bit Powershell. If you try to load it into the 32-bit Powershell.exe, you get the error message above.
Likewise, if you are automating Powershell from an application it must be compiled for 64-bit in order to load the Exchange Management Shell snap-in.
Here are some Exchange 2007 Web Services resources from around the web...
Microsoft Resources
VIDEO: Karim Batthish, an Exchange Program Manager, talks at Tech Ed 2006 about Exchange 2007 Web Services. http://msexchangeteam.com/videos/9/programmability/entry428207.aspx PPT: "Exchange 2007 Web Services: 42 APIs is not the Answer" from Tech Ed 2006 New Zealand. As a person who has to maintain working knowledge of those 42 APIs, I love the idea of simplifying the landscape. I love the title it is so true and something I'm sure every Exchange developer is happy for us to recognize. http://www.microsoft.com/nz/events/teched/download/results/searchResults.aspx?co=Exchange+2007+Web+Services%3A+42+APIs+is+Not+the+Answer Microsoft Blogs
CODE: Former Microsoftee, Eric Lee provides Exchange Server 2007 Web Service API for Developers! He uses a lot of pictures which always makes things more interesting. The other neat thing about this post is that Eric is a Web Services developer with no Exchange experience. The information in this post is from a Web Services enthusiast point of view, not of an Exchange developer... http://blogs.msdn.com/ericlee/archive/2006/10/22/exchange-server-2007-for-developers.aspx
CODE: Stephen Griffin, Exchange Web Services and MAPI Props. The MAPI Guy is doing web services?! Very appropriately, Stephen details how to get at arbitrary MAPI properties using web services.
http://blogs.msdn.com/stephen_griffin/archive/2006/12/19/exchange-web-services-and-mapi-props.aspx
Other Blogs
CODE: Sending Attachments via the Exchange Web Services in Exchange 2007. The introduction of this code sample has some great points... (1) If you have used WebDAV to send an email with attachments, you will find Web Services to be easier. If you have used CDOSYS to send an email with attachments, it is much more involved. (2) CDOSYS has always been and still is a very valuable tool for parsing and creating MIME, even if you are not using for sending mail. http://gsexdev.blogspot.com/2007/01/sending-attachments-via-exchange-web.html
CODE: Glen Scales, Scripting Exchange Web Services with VBS and Powershell
Like I said before, Glen is awesome…
Exchange SDK Code Samples Online
Getting User Availability, http://msdn2.microsoft.com/en-us/library/aa494212.aspx
Setting OOF Message, http://msdn2.microsoft.com/en-us/library/aa563356.aspx
Creating Appointments, http://msdn2.microsoft.com/en-us/library/aa563060.aspx
Handling Meetings, http://msdn2.microsoft.com/en-us/library/aa494190.aspx
Creating Contacts, http://msdn2.microsoft.com/en-us/library/aa563318.aspx
Updating Contacts, http://msdn2.microsoft.com/en-us/library/aa493909.aspx
Adding Managed Folders, http://msdn2.microsoft.com/en-us/library/aa493865.aspx
Deleting Folders, http://msdn2.microsoft.com/en-us/library/aa563048.aspx
Creating Folders, http://msdn2.microsoft.com/en-us/library/aa563302.aspx
Creating E-mail, http://msdn2.microsoft.com/en-us/library/aa563009.aspx
Sending E-mail, http://msdn2.microsoft.com/en-us/library/aa563049.aspx
Deleting Items, http://msdn2.microsoft.com/en-us/library/bb204091.aspx
Finding Items, http://msdn2.microsoft.com/en-us/library/aa563373.aspx
Finding Folders, http://msdn2.microsoft.com/en-us/library/aa493892.aspx
Updating Tasks, http://msdn2.microsoft.com/en-us/library/aa563020.aspx
Creating Tasks, http://msdn2.microsoft.com/en-us/library/aa563029.aspx
Expanding Distribution Lists, http://msdn2.microsoft.com/en-us/library/aa563082.aspx
Using Name Resolution, http://msdn2.microsoft.com/en-us/library/aa493895.aspx
To replace Store Events and Advise Sinks there are Push and Pull notifications in Exchange Web Services…more on this later…
Using Pull Notifications, http://msdn2.microsoft.com/en-us/library/aa579617.aspx
To replace ICS there are synch objects in Exchange Web Services…more on this later…
Synchronizing Mailboxes, http://msdn2.microsoft.com/en-us/library/aa563026.aspx
Here is important information on working with impersonation and Exchange Web Services…more on this later..
Configuring Exchange Impersonation, http://msdn2.microsoft.com/en-us/library/bb204095.aspx
...that is enough for now, I will continue to add to this list as I find more...
Updated 1/22/2009 – Fixed broken link.
There was a time when you could, in fact, use a script debugger with Exchange Event Service agent scripts. However, in this age of higher security the need for more scalable solutions, the days of using a script debugger with agent script is long since past. The following Exchange 5.5 documentation still exists on MSDN to make it seem possible...
Microsoft Exchange Event Scripting Agent http://msdn2.microsoft.com/en-us/library/ms998510.aspx
Using the Microsoft Script Debugger http://msdn2.microsoft.com/en-us/library/aa484635.aspx
The problem was that having unhandled errors launch the debugger blocks the event service and can be very problematic for a production server. So we disabled the ability to debug it much like is described in the following article...
How to catch run-time errors in an ActiveX Script Host http://support.microsoft.com/default.aspx?scid=kb;EN-US;232394
"...the script engine will then call IActiveScriptSiteDebug::GetApplication() to establish the debugging facilities for the scripting session. If IActiveScriptSiteDebug::GetApplication() fails, the script engine will conclude that debugging is not available on the machine, and revert to IActiveScriptSite::OnScriptError() for all error handling."
...In fact this is exactly what Exchange does, GetApplication() always returns E_FAIL thus disabling any attempt at using a script debugger with the Exchange Event Service...
Most people who support Exchange Event Service scripts never relied on the debugger anyway, rather they used logging to debug their scripts. In reality these scripts should not be so complex that they can't be debugged by some simple logging. If you are running Exchange 2000 or 2003 you should be developing with or migrating to Exchange Event Sinks anyway. In Exchange 2007 the event service itself is gone.
Updated 1/22/2009: The Exchange 5.5 SDK has been pulled from MSDN so these links don’t work, you can read more here.
With new versions of the product come new KB articles, make sure to take a look at this as you begin Outlook 2007 development...
929592 Known issues with developing Office Outlook 2007 form regionshttp://support.microsoft.com/default.aspx?scid=kb;EN-US;929592
929593 The known issues with the Microsoft Office Outlook 2007 object modelhttp://support.microsoft.com/default.aspx?scid=kb;EN-US;929593
Update 1-15-2007...
Here is another KB article related to Outlook 2007 development...
929591 Known issues in Office Outlook 2007 when you use custom forms that were created by using earlier versions of Outlookhttp://support.microsoft.com/default.aspx?scid=kb;EN-US;929591
Update 1-17-2007...
Here is another...
929590 Known issues with the Office Outlook 2007 development platformhttp://support.microsoft.com/default.aspx?scid=kb;EN-US;929590
...I'm back from a happy holiday vacation; I hope yours was happy too. Below is one of my favorite questions about CDO 1.21 because it gets asked a fair amount and the answer is simple, frustrating, and a little embarrassing for me as an MS employee. I haven't yet delivered this answer without a sheepish smile on my face...
The question is...
"I'm trying to access UserB's mailbox via CDO 1.21 using the following code in a process running as UserA...
Dim objSession
Set objSession = CreateObject("MAPI.Session")
objSession.Logon , "password", false, true, , true, "serverName" + vbLf + "UserB"
...however, I keep getting a password challenge to which I supply the same password in the code above and only then does it access UserB's mailbox. What gives?!"
And here is the answer...
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/2e5e5030-9478-46e8-8271-27266ac44ec6.asp
“profilePassword
Optional. String. Specifies the profile password. To prompt the user to enter a profile password, omit profilePassword and set showDialog to True. The default value is an empty string. The profilePassword parameter is ignored on all Win32 platforms.”
While this parameter still exists and is documented and looks like what you want, the last sentence spells out in so many words that this parameter is no good for you anymore.
Resolution...
You need to run your process as an account that has permission to the mailboxes you wish to access. In other words if you want to access UserB’s mailbox using CDO 1.21 then the process needs to run as UserB or some kind of service account with permissions to UserB’s mailbox and then use…
objSession.Logon , , false, true, , true, "serverName" + vbLf + "UserB"