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

https://blogs.msdn.com/webdav_101/archive/2008/09/26/links-on-common-powershell-automation-questions.aspx