Howto: Calling Exchange Powershell from an impersonated thread.
There are not many examples on doing doing impersonation for Exchange Powershell and non of the ones I see currently cover a few of the important gotccha’s. So, I put this together and wish to share.
Impersonation and Exchange PowerShell:
In order to do impersonation on a thread and have it work with PowerShell for Exchange, you need to be sure the following is covered:
1) Be sure that Rollup 1 for Exchange SP1 is installed. This has a fix allowing impersonation work with powershell for Exchange. See KB 943937...
How to obtain the latest service pack or update rollup for Exchange 2007
https://support.microsoft.com/kb/937052
At the time of this article, Rollup 3 is out, so why not install it (it includes Rollup 1)?
Description of Update Rollup 3 for Exchange Server 2007 Service Pack 1
https://support.microsoft.com/kb/949870/
2) Specify the -DomainController parameter when doing the call. This is also covered in KB 943937.
Ex: AP1111XX.mydomain.net
Here is how to build the sample:
1) Install the Exchange management tools on the box if they are already not installed.
2) Install Powershell if your not running code on an Exchange Server.
How to Download Windows PowerShell 1.0
https://www.microsoft.com/windowsserver2003/technologies/management/powershell/download.mspx
3) Install the PowerShell SDK…
How to Install Windows PowerShell and Download the Windows PowerShell SDK
https://msdn2.microsoft.com/en-us/library/Bb204630.aspx
4) Create a C# winform application.
5) Add the following controls:
Text boxes:
tbImpersonateUsername name of acct to impersonate.
tbImpersonatePassword password for acct to impoersonate
tbSamAccountName server\acct name of user to mail enable.
tbServerDatabase database where mailbox will be.
Check Box:
chkUseImpersonation choose to impersonate or not…
Button:
btnEnableMailbox calls impersonation routines.
6) Add-in the code below.
7) Set a reference to the "System.Management.Automation.dll"
This is installed by the Windows Powershell SDK under:
C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0
Here is an environment I used for testing this code:
Setup:
Box1 is:
· Windows 2003.
· This is the DC (AP1111XX.mydomain.net). Domain is mydomain.net.
· Exchange 2007 SP1 is installed.
· Installed Latest SPs, rollups, updates and fixes for Windows and Exchange
· Created User TestX1 is a domain user and having no mailbox - used Active Directory Users and Computers to do this.
Box2 is :
· Windows 2003 under same domain (mydomain.net) as theExchange 2007 server.
· Visual Studio 2008 is installed.
· Installed Exchange 2007 admin tools.
· Installed Powershell SDK.
OK, here is the sample code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Security;
using System.Security.Principal;
using System.Runtime.InteropServices;
namespace PowerShell101
{
public partial class Form1 : Form
{
private IntPtr _userToken = IntPtr.Zero; // Holds the new impersonation token.
private WindowsImpersonationContext _impersonationContext = null;
public Form1()
{
InitializeComponent();
}
// WindowsIdentity..::.Impersonate Method (IntPtr)
// https://msdn.microsoft.com/en-us/library/chf6fbt4.aspx
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
// User clicked run button....
private void btnEnableMailbox_Click(object sender, EventArgs e)
{
// Use the current domain and domain controller
Domain thisDomain = Domain.GetCurrentDomain();
string domain = thisDomain.Name;
string domainController = thisDomain.PdcRoleOwner.Name;
try
{
this.Cursor = Cursors.WaitCursor;
EnableMailbox(domain, domainController);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
this.Cursor = Cursors.Default;
MessageBox.Show("Done");
}
}
private void EnableMailbox(string domain, string domainController)
{
RunspaceConfiguration config = RunspaceConfiguration.Create();
PSSnapInException warning;
// Load Exchange PowerShell snap-in.
config.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out warning);
if (warning != null) throw warning;
// Start impersonation?
if (chkUseImpersonation.Checked == true)
{
BeginImpersonation(domain);
}
using (Runspace thisRunspace = RunspaceFactory.CreateRunspace(config))
{
try
{
thisRunspace.Open();
using (Pipeline thisPipeline = thisRunspace.CreatePipeline())
{
thisPipeline.Commands.Add("Enable-Mailbox");
thisPipeline.Commands[0].Parameters.Add("Identity", tbSamAccountName.Text);
thisPipeline.Commands[0].Parameters.Add("Database", tbServerDatabase.Text);
thisPipeline.Commands[0].Parameters.Add("DomainController", domainController); // Need for impersonation
// Need above line when impersonating. KB943937. Need rollup 1 for sp1.
try
{
thisPipeline.Invoke( );
}
catch (Exception exx)
{
MessageBox.Show("Error: " + exx.ToString(), "Error");
}
// Check for errors in the pipeline and throw an exception if necessary.
if (thisPipeline.Error != null && thisPipeline.Error.Count > 0)
{
StringBuilder pipelineError = new StringBuilder();
pipelineError.AppendFormat("Error calling Enable-Mailbox.");
foreach (object item in thisPipeline.Error.ReadToEnd())
{
pipelineError.AppendFormat("{0}\n", item.ToString());
}
throw new Exception(pipelineError.ToString());
}
}
}
finally
{
thisRunspace.Close();
if (chkUseImpersonation.Checked == true)
{
EndImpersonation();
}
}
}
}
private void BeginImpersonation(string domain)
{
EndImpersonation();
if (tbImpersonateUsername.Text.Length > 0)
{
System.Diagnostics.Debug.WriteLine("User Before Impersonation: " + WindowsIdentity.GetCurrent().Name);
bool success = LogonUser(
tbImpersonateUsername.Text,
domain,
tbImpersonatePassword.Text,
2,
0,
ref _userToken);
// Did it work?
if (!success) throw new Exception(string.Format("LogonUser returned error {0}", System.Runtime.InteropServices.Marshal.GetLastWin32Error()));
WindowsIdentity fakeId = new WindowsIdentity(_userToken);
_impersonationContext = fakeId.Impersonate();
System.Diagnostics.Debug.WriteLine("User After Impersonation: " + WindowsIdentity.GetCurrent().Name);
}
}
private void EndImpersonation()
{
if (_impersonationContext != null)
{
try
{
_impersonationContext.Undo();
}
finally
{
_impersonationContext.Dispose();
_impersonationContext = null;
}
}
}
}
}
Example of what I would enter when the code is run:
Text boxes:
tbImpersonateUsername Administrator
tbImpersonatePassword My1AdminPassword
tbSamAccountName mydomain \ TestX1
tbServerDatabase AP1111XX\First Storage Group\Mailbox Database
Check Box:
chkUseImpersonation checked
See what happens with and without impersonation.
About Mailboxes and Exchange 2007:
What is true for the Exchange 2007 tools should be true for Exchange PowerShell since the tools use PowerShell.
· Exchange 2007 mailboxes must be managed with Exchange 2007 management console or shell.Exchange 2007 mailboxes MUST NOT be managed with Exchange 2003 tools. Note that this is not blocked, but mailboxes managed from Exchange 2003 ADUC will not be fully functional.
· Exchange 2003 mailboxes can be edited or removed with Exchange 2007 tools, but cannot be created by Exchange 2007 tools.
· Exchange 2003 mailboxes can be managed with Exchange 2003 tools.
· Both Exchange 2003 and Exchange 2007 mailboxes can be moved (in either direction) with the Exchange 2007 tools. Exchange 2003 move mailbox cannot be used to move mailboxes to or from Exchange 2007 mailbox server.
For further information on PowerShell, please visit the link below:
Links on Common PowerShell Automation Questions
Comments
Anonymous
September 26, 2008
I've put together a list of articles which cover common questions on PowerShell automation. These linksAnonymous
October 03, 2008
We are working to get official public documentation on this subject, I will update this post once weAnonymous
November 18, 2008
Great! It really works. But I am not quite sure how to understand support current installed Exchange Client Tools this functionality or not. How can I understand it (e.g. analyze dll’s version or something also) It’s very important for me Thank you, DmitryAnonymous
December 14, 2008
Hi! Any information on how to do impersonation on Win 2008 x64 server? I cannot get the impersonation running on IIS 7. Using Exchange 2007 SP1 Rollup 5. Getting the usual "Access to the address list service on all Exchange 2007 servers has been denied" error among others.Anonymous
December 29, 2008
Has anybody gotten impersonation to work under ASP.NET and running permission specific Exchange commands (such as “Enable-Mailbox”)? Thanks!Anonymous
January 08, 2009
The comment has been removedAnonymous
January 12, 2009
Thank you Dan for sharing your code, it fills a big gap in the documentation and code examples - in fact this is the only working code in regard to using enable-mailbox in conjuction with impersonation - it resolved a realy big isues for me. But how about move-mailbox? I'm setting the -DomainControler parameter but without any sucess - it kills the app and logs in the event log an event with M.E.D.D.ConnectionPoolManager.BlockImpersonatedCallers in it (naturally with all paches applied on all boxes).Anonymous
March 25, 2009
The comment has been removedAnonymous
March 25, 2009
The comment has been removedAnonymous
April 21, 2009
The comment has been removedAnonymous
December 10, 2009
With Exchange 2010, you should be using Remote Powershell. Below are some articles to help get you started. How to call Exchange 2010 cmdlet's using Remote Powershell in code http://blogs.msdn.com/dvespa/archive/2009/10/22/how-to-call-exchange-2010-cmdlet-s-using-remote-powershell-in-code.aspx Remote Powershell Sample Explained... http://blogs.msdn.com/dvespa/archive/2009/10/22/remote-powershell-sample-explained.aspx Programmatic Access via Remote PowerShell in Exchange Server 2010 http://msexchangeteam.com/archive/2009/11/02/453016.aspx Connect Remote Exchange Management Shell to an Exchange Server http://technet.microsoft.com/en-us/library/dd297932.aspx Install Windows Management Framework http://technet.microsoft.com/en-us/library/dd335147.aspxAnonymous
September 01, 2010
Do you know is there a differences between different cmdlets? As I'm able to use "Enable-Mailbox", "Disable-Mailbox", "New-DistributionGroup" etc... but not able to use e.g.: "Add-MailboxPermission". It kills the application pool and writes on the event log: M.E.D.D.ConnectionPoolManager.BlockImpersonatedCallers So are some cmdlets more against impersonating than others?Anonymous
October 19, 2010
I don't know what might be causing that. Did you enable impersonation as described on 4/21?Anonymous
October 19, 2010
Note that using the snap-in method may or may not work with Exchange 2010 - its not supported. You really need to use Remote PowerShell against Exchange 2010 if you want reliability.Anonymous
November 04, 2010
The comment has been removedAnonymous
July 31, 2011
I don't think impersonation is supported in this area as I recall. However, it would be be best to open a support case on this.Anonymous
July 31, 2011
I don't think impersonation is supported in this area as I recall. However, it would be be best to open a support case on this.