Introduction
This article shows you have you can use LDAP and the System.DirectoryServices
namespace to mail enable an Active Directory user account when you are using Exchange 2003 and 2007.
Background
Exchange 2007 and a lot of new products of Microsoft rely heavily on Powershell to script and automate tasks. Due to the fact that we have built several connectors using LDAP to automate our user management, we have invested some time to research why our current Exchange 2003 connections wouldn't work with the new Exchange 2007 environment. When performing the same routines as we did in Exchange 2003, we ended up with Legacy Mailboxes that we had to migrate to full 2007 compliance using Powershell. Turns out that to fulfill the 2007 specs, we had to populate five other Active Directory properties in order for Exchange 2007 to accept the mail enabling of the AD User account.
Using the code
To use the code, you supply the MailEnable
method with two parameters. One parameter is the LDAP path to the user object in the Active Directory, and the other parameter is the LDAP path to the Exchange Mailbox Store.
Exchange part
To find the paths to the mailbox stores, we have made three methods that can retrieve the Exchange Servers, Exchange Mailbox Storage Groups, and the Exchange Mailbox Stores. Because we code in VB.NET and C#, the code snippets will be in one or the other language. These snippets are part of a huge library, so I can't supply you with all the code that is necessary to fully run it.
Protected Function Get_RootDSEProperty (ByVal PropertyName As String) _
As String
Dim _DirectoryEnTry As New System.DirectoryServices.DirectoryEntry
_DirectoryEnTry.Path = Protocol + Server + "RootDSE"
Return CType(_DirectoryEnTry.Properties(PropertyName)(0), String)
End Function
In the above function, Protocol
is a property filled with the string value "LDAP://". Server
is a property specifying a Domain Controller (e.g., "UM-DC.LiQuick.net/"). This function will return the "roots" of the User/Groups/Computers/Contacts part of Active Directory (defaultNamingContext
), which you can view with the Active Directory User and Computers MMC, and the root in which the Configuration settings of your domain is stored (configurationNamingContext
). The configuration root is used to retrieve the Exchange servers, storage groups, and stores.
Public Function ExchangeServer_PathsGet() As System.DirectoryServices.SearchResultCollection
Dim _configurationNamingContext As String = _
Protocol + Server + Me.Get_RootDSEProperty("configurationNamingContext")
Dim LDAPConditions() As String = New String(0) {}
LDAPConditions(0) = "objectCategory=msExchExchangeServer"
Return Find_DirectoryEntries(LDAPConditions, _
SearchScope.Subtree, _configurationNamingContext)
End Function
The above function returns the LDAP paths of all the Exchange servers you have made a member of your domain. Find_DirectoryEntries
uses the System.DirectoryServices.DirectorySearcher
namespace to find the objects that correspond to the LDAP query (&(objectCategory=msExchExchangeServer)
).
Public Function ExchangeServer_RetrieveStorageGroups( _
ByVal directoryEntryExchangeServer As DirectoryEntry _
) As System.DirectoryServices.SearchResultCollection
Dim LDAPConditions() As String = New String(0) {}
LDAPConditions(0) = "objectCategory=msExchStorageGroup"
Return Find_DirectoryEntries(LDAPConditions, _
SearchScope.Subtree, directoryEntryExchangeServer)
End Function
Specifying the above function with a DirectoryEntry
, having an LDAP path to an Exchange server will provide you with the storage groups that reside on that particular Exchange server.
Public Function ExchangeServer_RetrieveStores( _
ByVal directoryEntryExchangeStorageGroup As DirectoryEntry _
) As System.DirectoryServices.SearchResultCollection
Dim LDAPConditions() As String = New String(0) {}
LDAPConditions(0) = "objectCategory=msExchPrivateMDB"
Return Find_DirectoryEntries(LDAPConditions, SearchScope.Subtree, _
directoryEntryExchangeStorageGroup)
End Function
After choosing a storage group which you have retrieved with the function ExchangeServer_RetrieveStorageGroups
, you can finally retrieve the much desired Mailbox Store Paths.
The above functions use the Find_DirectoryEntries
function below. It takes a string array of conditions (for example, "objectCategory=msExchPrivateMDB
") and forms it into an LDAP query (e.g., "(&(objectCategory=msExchPrivateMDB))
"). The rest is explained in the MSDN libraries.
Public Function Find_DirectoryEntries( _
ByVal Conditions() As String, _
ByVal Scope As System.DirectoryServices.SearchScope, _
ByVal FromDirectoryEntry As System.DirectoryServices.DirectoryEntry _
) As System.DirectoryServices.SearchResultCollection
Dim Filter As String
Filter = CreateFilterAND(Conditions)
Return Find_DirectoryEntries(Filter, Scope, FromDirectoryEntry)
End Function
Public Function Find_DirectoryEntries( _
ByVal Filter As String, _
ByVal Scope As System.DirectoryServices.SearchScope, _
ByVal FromDirectoryEnTry As System.DirectoryServices.DirectoryEntry, _
Optional ByVal PageSize As Integer = 0 _
) As System.DirectoryServices.SearchResultCollection
Dim _DirectorySearcher As New System.DirectoryServices.DirectorySearcher
Dim _SearchResultCollection As System.DirectoryServices.SearchResultCollection
_DirectorySearcher.SearchRoot = FromDirectoryEnTry
_DirectorySearcher.Filter = Filter
_DirectorySearcher.SearchScope = Scope
If PageSize > 0 Then
_DirectorySearcher.PageSize = PageSize
End If
Try
_SearchResultCollection = _DirectorySearcher.FindAll()
Catch ex As Exception
_SearchResultCollection = Nothing
End Try
Return _SearchResultCollection
End Function
Public Function CreateFilterAND( _
ByVal Conditions() As String, _
Optional ByVal StringPreFilter As String = "" _
) As String
Dim Filter As String = "(&"
Filter += CreateFilter(Conditions, StringPreFilter)
Filter += ")"
Return Filter
End Function
Public Function CreateFilter( _
ByVal Conditions() As String, _
Optional ByVal StringPreFilter As String = "" _
) As String
Dim Filter As String = ""
Dim Condition As String
For Each Condition In Conditions
Filter += "(" + Condition + ") "
Next
Filter += StringPreFilter
Return Filter
End Function
User part
I will not go into how you can retrieve the LDAP path to a user object in Active Directory, there are numerous articles about how you can do that. The following code will actually mail enable the AD user account for an Exchange 2003 and 2007 environment. The only other thing you have to do which is not covered in this code is populating the proxy addresses (e-mail address of the user object). This code is taken from our WebService that provides several Active Directory manipulation methods.
public void User_MailEnable(string path, string pathMailStore)
{
try
{
System.DirectoryServices.DirectoryEntry directoryEntryUser =
new System.DirectoryServices.DirectoryEntry(path);
string userName = (string) directoryEntryUser.Properties["samaccountname"][0];
if (directoryEntryUser.Properties.Contains("msExchMailboxGuid") == false)
{
if (directoryEntryUser.Properties.Contains("displayName") == false)
{
directoryEntryUser.Properties["displayName"].Add(userName);
}
System.DirectoryServices.DirectoryEntry directoryEntryMailStore =
new System.DirectoryServices.DirectoryEntry(pathMailStore);
string homeMDB =
(string) directoryEntryMailStore.Properties["distinguishedName"][0];
string msExchHomeServerNamePath = _AD.Protocol + _AD.Server +
(string) directoryEntryMailStore.Properties["msExchOwningServer"][0];
System.DirectoryServices.DirectoryEntry directoryEntryMailServer =
new System.DirectoryServices.DirectoryEntry(msExchHomeServerNamePath);
string msExchHomeServerName =
(string) directoryEntryMailServer.Properties["legacyExchangeDN"][0];
string legacyExchangeDN = msExchHomeServerName.Substring(0,
msExchHomeServerName.IndexOf("cn=")) + "cn=Recipients/cn=" + userName;
directoryEntryUser.Properties["homemdb"].Value= homeMDB;
directoryEntryUser.Properties["msExchHomeServerName"].Value =
msExchHomeServerName;
directoryEntryUser.Properties["mailNickName"].Value = userName;
directoryEntryUser.Properties["legacyExchangeDN"].Value = legacyExchangeDN;
if (directoryEntryMailServer.Properties.Contains("msExchVersion"))
{
directoryEntryUser.Properties["msExchVersion"].Value =
directoryEntryMailServer.Properties["msExchVersion"][0];
directoryEntryUser.Properties["msExchRecipientDisplayType"].Value =
1073741824;
directoryEntryUser.Properties["msExchRecipientTypeDetails"].Value = 1;
}
Guid guid = Guid.NewGuid();
directoryEntryUser.Properties["msExchMailboxGuid"].Add(guid.ToByteArray());
}
directoryEntryUser.CommitChanges();
}
catch (Exception e)
{
}
}
Extra information
In this paragraph, I intend to answer most questions I received by e-mail or the comments below this article.
"When I look at the new mailbox in the Exchange Console, the "Alias" on the General Tab is not set." The property you are looking for is called mailNickname
. Below, I'll show you the AD_ObjectPropertySet
function I use to set most text properties in AD. In this example, I would use AD_ObjectPropertySet(myDirectoryEntry, "mailNickname", "LiQuick", false)
.
"...and the E-Mail Addresses tab is blank." To fill the e-mail addresses, you should do two things. First, you should know that you have to set the property proxyAddresses
. This is a multi-valued property in AD and you could use the below method AD_ObjectPropertySet
. When you look closely at the e-mail addresses in Exchange, you will see that the standard e-mail addresses start with smtp:. The second thing you should know is that the primary e-mail address starts with uppercase, SMTP:. Furthermore, I set the primary e-mail address in the single-valued mail
property of the AD DirectoryEntry
. Below is an example of how I would set the e-mail addresses:
AD_ObjectPropertySet(dirEntryAD, "proxyAddresses", "SMTP:R.Samulski@LiQuick.net", false);
AD_ObjectPropertySet(dirEntryAD, "proxyAddresses",
"smtp:IWishIWasTheBoss@LiQuick.net" , true);
AD_ObjectPropertySet(dirEntryAD, "mail", "R.Samulski@LiQuick.net", false);
public static void AD_ObjectPropertySet(
System.DirectoryServices.DirectoryEntry directoryEntry,
string propertyName, string propertyValue, bool IsMultiValued)
{
if (propertyValue == "")
{
if (directoryEntry.Properties.Contains(propertyName) == true)
{
directoryEntry.Properties[propertyName].Clear();
}
}
else
{
if (IsMultiValued == true)
{
try
{
directoryEntry.Properties[propertyName].Add(propertyValue);
}
catch
{
directoryEntry.Properties[propertyName].Value = propertyValue;
}
}
else
{
if (directoryEntry.Properties.Contains(propertyName) == true)
{
if (directoryEntry.Properties[propertyName][0].ToString() != propertyValue)
{
directoryEntry.Properties[propertyName].Clear();
directoryEntry.Properties[propertyName].Value = propertyValue;
}
else
{
return;
}
}
else
{
directoryEntry.Properties[propertyName].Value = propertyValue;
}
}
}
directoryEntry.CommitChanges();
}
Points of interest
As I pointed out: the above code is not all you need to mail enable an AD user account, but I hope I have provided you with enough information to give you a push into the right direction.
History
- 2008-10-09: First attempt to write the article.
- 2011-04-23: Answered an e-mails question by adding some information.