X500 address woes on Exchange 2007 Staged Migration

If you are migrating from Exchange 2007 to Exchange Online, you have the option of performing a Staged Migration.  The full details are on TechNet, with all the necessary steps to have a successful migration.  Step 6 in the list is to convert the Mailbox users to Mail-Enabled users.  This disconnects the local mailbox and points them at the cloud mailbox.  This allows autodiscover to configure your Outlook correctly and for administrators to manage both their on-prem and cloud users locally.

The second script in the process (Exchange2007MBtoMEU.ps1) is supposed to do the actual conversion of the account, which it does.  However I was getting an error about creating the X500 addresses for the user.  After looking through the script I found some coding errors that were causing the problem and better yet I rewrote the script to be a bit shorter and more robust.  Continue past the break to learn more...

The full error looks like this:
Set-MailUser : Cannot convert 'x500:/o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=account-name-redacted' to the type 'Microsoft.Exchange.Data.ProxyAddressCollection' required by parameter
'EmailAddresses'. The value 'X500:/o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=account-name-redacted' is already present in the collection.
At C:\scripts\Exchange2007MBtoMEU.ps1:70 char:60
+         Set-MailUser -Identity $UserInfo.Identity -EmailAddresses <<<<  $UserInfo.ProxyAddresses -WindowsEmailAddress $Use
rInfo.Mail -DomainController $DomainController
    + CategoryInfo          : InvalidArgument: (:) [Set-MailUser], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.Exchange.Management.RecipientTasks.SetMailUser
 As you can see, it is tripping over the fact that the parameter cannot be converted to the type Microsoft.Exchange.Data.ProxyAddressCollection.  Let's take a look at the script and see what's happening.  The command that generates the error is this one:
Set-MailUser -Identity $UserInfo.Identity -EmailAddresses $UserInfo.ProxyAddresses
It doesn't seem to like the $UserInfo.ProxyAddresses parameter.  Let's trace back and see where that's created:
$ProxyAddresses = @()
foreach($Address in $Mailbox.EmailAddresses)
{
$ProxyAddresses += $Address
}
$UserInfo | Add-Member -Type NoteProperty -Name ProxyAddresses -Value $ProxyAddresses
Ah, so the $ProxyAddresses object is a generic array which has been populated with the email address objects from the $Mailbox.EmailAddresses.  The object $Mailbox.EmailAddresses is of the type Microsoft.Exchange.Data.ProxyAddressCollection, but $ProxyAddresses is not.  That could be an issue.  Rather than doing that, I changed the script to this:
$ProxyAddresses = New-Object Microsoft.Exchange.Data.ProxyAddressCollection
$ProxyAddresses = $Mailbox.EmailAddresses
Look at that!  Since I created a ProxyAddressCollection Object, I was able to copy the $Mailbox.EmailAddresses object rather than iterate through the items in the collection.  No foreach loop for me!  I also now have all the Method and Attributes associated with the ProxyAddressCollection.  That includes the contains/notcontains operator and the Add method.  That will be helpful in a moment.  Let's look at how those X500 addresses are added:
    $CloudLegacyDNPresent = $false
$LegacyDNPresent = $false
foreach($Proxy in $UserInfo.ProxyAddresses)
{
if(("x500:$UserInfo.CloudLegacyDN") -ieq $Proxy)
{
$CloudLegacyDNPresent = $true
}
if(("x500:$UserInfo.LegacyDN") -ieq $Proxy)
{
$LegacyDNPresent = $true
}
}
if(-not $CloudLegacyDNPresent)
{
$X500Proxy = "x500:" + $UserInfo.CloudLegacyDN
Write-Host "Adding $X500Proxy to EmailAddresses" -ForegroundColor Green
$UserInfo.ProxyAddresses += $X500Proxy
}
if(-not $LegacyDNPresent)
{
$X500Proxy = "x500:" + $UserInfo.LegacyDN
Write-Host "Adding $X500Proxy to EmailAddresses" -ForegroundColor Green
$UserInfo.ProxyAddresses += $X500Proxy
}
Woof.  That's not pretty at all.  So we're looking through the $ProxyAddresses array to try and find an X500 address for the CloudLegacyDN and LegacyDN values.  If we don't find it, we need to add it.  There's got to be a better way, like this:
if($UserInfo.ProxyAddresses -notcontains ("X500:"+$UserInfo.CloudLegacyDN))
{
$X500Proxy = "x500:" + $UserInfo.CloudLegacyDN
Write-Host "Adding $X500Proxy to EmailAddresses" -ForegroundColor Green
$UserInfo.ProxyAddresses.Add($X500Proxy)
}
if($UserInfo.ProxyAddresses -notcontains ("X500:"+$UserInfo.LegacyDN))
{
$X500Proxy = "x500:" + $UserInfo.LegacyDN
Write-Host "Adding $X500Proxy to EmailAddresses" -ForegroundColor Green
$UserInfo.ProxyAddresses.Add($X500Proxy)
}
Boom.  Use the notcontains operator for the logic test and use the Add method to add the X500 address.  Using the Add method ensures that the address is added as the correct object type.  You don't really need to create an $X500Proxy variable either, but's it's nice to have for the Write-Host output.  Now when we pass the $ProxyAddresses object to the Set-MailUser command, it is of the correct type and the objects inside it are as well.  After making these changes, the script ran like a charm and those X500 addresses were added.

If you are looking for a full copy of the updated script, let me know.  Otherwise, you can just cut and paste the original with my modification.  I also left a comment on the Microsoft site to let them know about this potential issue.  Hopefully they'll amend their script as well.

UPDATE: People have been asking for my modified script.  I put it up on Gist:

Labels: , , , , , , ,