Introduction
As Exchange 2007 upgrades proliferate through enterprise environments, you may find yourself a developer or systems administrator who is still working with Exchange 2003. In fact, you may even have plans to stick with Exchange 2003 for the foreseeable future. Regardless of the situation, there is typically not a reason to avoid scripting with Microsoft Powershell in your Exchange messaging environment. If you have yet to delve into Powershell, it’s advised you first download it from the Microsoft website and look up a quick tutorial before progressing forward.
Most of the scripting you’ve done or are looking to do with Exchange will involve some form of mail enablement. Although you can use ADSI or System.DirectoryServices as an interface to creating contacts or users, when it comes to programming for mail enablement you’ll be faced with using Collaboration Data Objects for Exchange Management—commonly referred to as CDOEXM. If you’ve tried to translate some common CDEXM programming samples available on the web, you probably have gotten an error message such as “Cannot load COM.” Don’t worry; you’ll be shown how to get through this through this by using reflection and late binding.
Writing a Powershell Mail Forwarding Creation Script
The beginning of the script starts with a simple header that displays a brief note of what the program does, its intended usage, and a set of default variables used for connecting to the domain. At this point, it’s worthwhile to note that you’ll likely want to do some customization for your domain aside from updating the script’s domain username and password. When doing so, it’s a good idea to add in a function or two for validating your inputs, such as confirming the email address provided is syntactically valid. The code here forgoes that step for the purpose of highlighting the mail enablement key concept.
# # File: CreateMailForward.ps1 # Note: Powershell program for creating mail forwarding contacts # on Exchange 2003 using CDOEXM # Use: CreateMailForward.ps1 {sAMAccountname} {valid email address} # # # Domain Variables # $Domain = "LDAP://ad.example.com:389" $Domain_Base = "DC=ad,DC=example,DC=com" $Domain_User = "XXXXXXXXXXX" $Domain_Pass = "XXXXXXXXXXX" $Domain_Mail = "OU=Forwards,OU=People,DC=ad,DC=example,DC=com""
Next is the CDOEXM usage setup where you load the CDOEXM.dll, assign the IMailRecipient assembly type to a variable, and set the binding flags for invoking member methods. This is the setup work required to call the MailEnable method of the IMailRecipient interface.
# # Assembly Information # $Assembly = [Reflection.Assembly]:: LoadFile("C:pscriptsInterop.CDOEXM.dll") $Assembly_Type = $Assembly.GetType("CDOEXM.IMailRecipient") $Binding_Flags = [Reflection.BindingFlags] "invokemethod,public"
Before you create the contact, make sure the sAMAccountname provided as the first argument to the script does indeed exist on the domain. If it does not, you’ll report a warning and exit. If it does, you’ll get the user’s directory entry from the search result and then move on to creating the contact.
# # Place the command line arguments into distinct varibles # $Account = $args[0]; $Email = $args[1]; # # Find the user which is having the mail forward created # $Domain_Filter = "(&(objectClass=user)(sAMAccountName=$Account))" $Domain_Searcher = new-Object System.DirectoryServices.DirectorySearcher $Domain_Searcher.searchroot = New-Object System.DirectoryServices.DirectoryEntry ("$Domain/$Domain_Base", $Domain_User, $Domain_Pass) $Domain_Searcher.pagesize = 1000 $Domain_Searcher.searchscope = "subtree" $Domain_Searcher.filter = $Domain_Filter $Domain_Result = $Domain_Searcher.findone() # check for a valid, non-null search result if ($Domain_Result -eq $null){ Write-warning "Invalid account name: $Account" exit } # get the user's directory object $Person = $Domain_Result.GetDirectoryEntry()
Now that you have a valid user directory entry, go ahead and create the contact. In this case, you are making a new domain connection to a container that holds mail-related objects and defining a limited number of properties on the new contact. You’ll likely change this for your domain configuration.
# connect to the container which holds mail related objects and # create the contact $Forward_Container = New-Object System.DirectoryServices.DirectoryEntry ("$Domain/$Domain_Mail", $Domain_User, $Domain_Pass) $Contact = $Forward_Container.psbase.get_children().add("CN=$Account", "Contact") # set default values and commit the changes $Contact.psbase.invokeset("givenname", "$Account Mail Forward") $Contact.psbase.invokeset("displayname","$Account Mail Forward") $Contact.psbase.invokeset("description","$Account Mail Forward") $Contact.psbase.CommitChanges()
Finally, the big moment arrives; you are now invoking the “MailEnable” member of the CDOEXM assembly. The five-parameter call to invokemember is simple and powerful. Against the assembly you are calling “MailEnable”, with the binding flags declared earlier in the script, a null reference to the binder that results in this invocation using the default binding, the native object to which “MailEnable” is being applied, and lastly any arguments that are used during the invocation. Once this step is completed, you modify the user’s directory entry by updating the ‘altrecipient’ and ‘forwardingaddress’ properties. Finally, upon committing the changes, you’ll have a user whose mail will be forwarded according to the mail-enabled contact.
# call the assembly which takes the native object and performs # MailEnable and commit the change $Assembly_Type.invokemember("MailEnable",$Binding_Flags,$null, $Contact.psbase.nativeobject, "SMTP:$email") $Contact.psbase.CommitChanges() # set the contact as the mail forward $Person.psbase.invokeset("altrecipient", $Contact.psbase.invokeget('distinguishedName')) $Person.psbase.invokeset("forwardingaddress", $Contact.psbase.invokeget('distinguishedName')) $Person.psbase.CommitChanges() #output a message stating what has been done Write-Host "Email for $Account will be forwarded to $email" #close connections $Domain_Searcher.dispose | out-null $Forward_Container.dispose | out-null
Running this script will produce the following output on the Powershell console, as shown in Figure 1.
Figure 1: Output on the Powershell console
Summary
As your Exchange messaging environment grows and through the course of time, you’ll no doubt be asked to diversify mail settings for your current users or ones who no longer have accounts. In either case, this article goes to show how you can use Powershell to interface with some of the more advanced concepts of scripting your Exchange 2003 environment. If you build further on what is presented here, let the author know by emailing him at jonATshobergDOTnet.
About the Author
Jon Shoberg is a developer for The Ohio State University. He has extensive experience in developing enterprise systems in e-commerce, web, and search domains on the LAMP, Java, and .NET platforms. Jon has previously contributed to books and industry publications as a technical reviewer and coauthor, respectively.