Exchange Email Address Template Objects in PowerShell

Exchange has a very rich set of objects which are used and created by the various Exchange cmdlets. Unfortunately, these objects (excepting those used by Exchange Web Services) are poorly documented - and the documentation which is available is often incorrect or misleading.

I ran into that problem this past week.

To understand my particular issue, let's review a little history. Exchange 2000 and Exchange 2003 didn't have the concept of individual "accepted domains". Instead, you usually used Recipient Policies to identify the accepted domains. Recipient Policies were also used to create and manage Mailbox Manager policies.

In Exchange 2007, all of the capabilities of Recipient Policies were split into multiple features: Accepted Domains, E-mail Address Policies, and Managed Folder Mailbox policies. In Exchange 2010, Managed Folder Mailbox policies have been deprecated and replaced by Retention Policies.

However, some capabilities of these features are not available in the Exchange 2007/2010 GUI (but were in the Exchange 2000/2003 GUI). One of these hidden features is to have both enabled and disabled address templates in an E-Mail Address Policy. In the case of a disabled address template, a particular e-mail address domain can be manually assigned to a given Exchange object but that e-mail address domain will not be automatically assigned even if the object otherwise meets the requirements of the recipient filter assigned to the E-Mail Address Policy (such as a company name, a conditional attribute, group membership, etc.).

Whew.

So what happens when you need to disable an existing e-mail address template?

Well, you do it in PowerShell. (Long lead up to an obvious response, right?) And you do it with the obvious PowerShell cmdlet: Set-EmailAddressPolicy.

What would seem to make sense that the command would be something like:

Set-EmailAddressPolicy 'Default Policy' -Add -DisabledEmailAddressTemplates '@example.com'

...but that doesn't work. Aside from the fact that there is no Add or Remove parameter to Set-EmailAddressPolicy, you will eventually figure out that whatever value is provided to the EnabledEmailAddressTemplates and the DisabledEmailAddressTemplates parameters overwrite the values previously there. Well, ain't that a kick?

To change an address template collection, you must modify the existing collection and resubmit the modified value to Set-EmailAddressPolicy.

So what's the problem? The problem is the type of the required argument. The EnabledEmailAddressTemplates and DisabledEmailAddressTemplates parameters take a parameter of type Microsoft.Exchange.Data.ProxyAddressTemplateCollection. This is an enumerated collection (a fancy name for an array that you can use with a foreach(), or index it, or use the Item() method to access elements of the collection). Individual items of the collection are of type Microsoft.Exchange.Data.ProxyAddressTemplate or Microsoft.Exchange.Data.SmtpProxyAddressTemplate.

To add or remove items from the collection, you use the normal methods available to collections - that is, the Add() and Remove() methods. However, to use the Add() or Remove() methods, you need to have an object of type Microsoft.Exchange.Data.ProxyAddressTemplate or of type Microsoft.Exchange.Data.SmtpProxyAddressTemplate.

How the heck do you get one of those?

In general, with .Net objects, you create objects using a constructor. In PowerShell, that translates to the New-Object cmdlet. So my normal process is to begin by using Google/Bing to search for the object name. The first or second hit is usually the MSDN web page that describes the class. Then, I read the page, find the appropriate constructor and bam! I am done. This is the method I followed in an earlier article: More Multi-valued Parameters in PowerShell (SourceTransportServers in Set-SendConnector).

But it doesn't always work. I refer you back to the first paragraph. Many Exchange objects are poorly documented and the documentation that is present is often wrong. As an exercise, I invite you to Google/Bing the objects we are interested in today: Microsoft.Exchange.Data.ProxyAddressTemplate or Microsoft.Exchange.Data.SmtpProxyAddressTemplate. Read the MSDN web pages for the class, especially the page on constructors.

Once you've done that reading, you'll try this:

$template = New-Object Microsoft.Exchange.Data.ProxyAddressTemplate('smtp', '@example.com')

which will not work. Then you'll try:

$template = New-Object Microsoft.Exchange.Data.ProxyAddressTemplate('smtp:@example.com')

which also will not work. Then you'll try to get a raw object:

$template = New-Object Microsoft.Exchange.Data.ProxyAddressTemplate

which again will not work.

Then, if you are like me, you'll spend the next couple of hours banging your head against the wall getting nowhere. After sleeping on the problem, I did come up with a very unsatisfying work-around. Since, in this case, we are trying to take a "currently enabled" object and make it disabled, we should be able to use a reference to the existing object. That solution looks like the below:

	$template = '@example.com'

	$policy = Get-EmailAddressPolicy 'Default Policy'

	$EnabledAddresses  = $policy.EnabledEmailAddressTemplates
	$DisabledAddresses = $policy.DisabledEmailAddressTemplates

	$objTemplate = $null

	foreach ($addr in $EnabledAddresses)
	{
		if ( $addr.AddressTemplateString -ieq $template )
		{
			"found $template"
			$objTemplate = $addr
			break
		}
		"skipped " + $addr.AddressTemplateString
	}

	if ( $objTemplate )
	{
		$EnabledAddresses.Remove( $objTemplate )
		$DisabledAddresses.Add( $objTemplate )

		$policy |
			Set-EmailAddressPolicy -EnabledEmailAddressTemplates $EnabledAddresses `
					       -DisabledEmailAddressTemplates $DisabledAddresses
	}

That, however, is a kludgey hack. In the above solution, we are using a reference to an existing object to change the policies. That will not work in many use-cases; for example where you want to add a new address template, whether enabled or disabled. You will find that you can't modify the objects returned from Get-EmailAddressPolicy - they are marked read-only.

So, I asked some people who know a lot more about PowerShell than I. Shay Levy, a PowerShell MVP, pointed out that I was making it too hard and that this works:

$objTemplate = [Microsoft.Exchange.Data.ProxyAddressTemplate]'smtp:@example.com'

So, I slapped myself upside the head and said "Of course! ::Parse() is also a hidden constructor!" What that means, in English, is that the above statement is equivalent to:

$objTemplate = [Microsoft.Exchange.Data.ProxyAddressTemplate]::Parse( 'smtp:@example.com' )

and to:

$objTemplate = 'smtp:@example.com' -as [Microsoft.Exchange.Data.ProxyAddressTemplate]

PowerShell has a built-in capability, using a static method on an object, to take a string and attempt to coerce that string into the named object. In this case, a Microsoft.Exchange.Data.ProxyAddressTemplate. That static method must be named Parse() for PowerShell to automatically use the capability. You can see the static methods on this class using the below command:

[Microsoft.Exchange.Data.ProxyAddressTemplate] | Get-Member -static

Oisin Grehan, another PowerShell MVP, also pointed out that this is a perfect opportunity to use a filter. A filter is a PowerShell feature, somewhat uncommonly used, for processing objects in the pipeline. For example, to define a couple of useful filters:

	filter ConvertTo-ProxyAddressTemplate
	{
		$_ -as [Microsoft.Exchange.Data.ProxyAddressTemplate]
	}

	filter ConvertTo-SmtpProxyAddressTemplate
	{
		$_ -as [Microsoft.Exchange.Data.SmtpProxyAddressTemplate]
	}

These filters would take input from the pipeline and convert the pipeline input into the desired objects. For example:

	[PS] C:\S>'@example.com', '@sub1.example.com', '@sub2.example.com' | ConvertTo-SmtpProxyAddressTemplate

	AddressTemplateString      : @example.com
	ProxyAddressTemplateString : smtp:@example.com
	Prefix                     : SMTP
	IsPrimaryAddress           : False
	PrefixString               : smtp

	AddressTemplateString      : @sub1.example.com
	ProxyAddressTemplateString : smtp:@sub1.example.com
	Prefix                     : SMTP
	IsPrimaryAddress           : False
	PrefixString               : smtp

	AddressTemplateString      : @sub2.example.com
	ProxyAddressTemplateString : smtp:@sub2.example.com
	Prefix                     : SMTP
	IsPrimaryAddress           : False
	PrefixString               : smtp

So, given what we know now, what does the script look like? There are some similarities, but now it doesn't feel like a hack and it is much easier to read and understand:

	$objTemplate = '@example.com' | ConvertTo-SmtpProxyAddressTemplate

	$policy = Get-EmailAddressPolicy 'Default Policy'

	$EnabledAddresses  = $policy.EnabledEmailAddressTemplates
	$DisabledAddresses = $policy.DisabledEmailAddressTemplates

	$EnabledAddresses.Remove( $objTemplate )
	$DisabledAddresses.Add( $objTemplate )

	$policy |
		Set-EmailAddressPolicy -EnabledEmailAddressTemplates $EnabledAddresses `
				       -DisabledEmailAddressTemplates $DisabledAddresses

So, what is the lesson learned? Don't forget about ::Parse()! And, as almost always, in PowerShell there is often more than one way to skin a cat.

Until next time...

If there are things you would like to see written about, please let me know.

Published Monday, July 19, 2010 11:32 AM by michael

Comments

No Comments