Did you just run Import-MailPublicFoldersForMigration.ps1 and receive a ton of red errors saying that
“The proxy address “smtp:firstname.lastname@example.org” is already being used by “foldername”.”
And also a summary at the end letting you know that “The following mail enabled public folders could not be imported:”? Not to worry! You are not alone, and I can show you why you got the error and more importantly how to fix it. If you haven’t run into this yet, this post will help you avoid it.
In order to migrate public folders to Exchange Online or enabled legacy public folder access from Exchange Online to an on-premises server, Microsoft has a set of scripts to export the mail-enabled public folder recipient objects from on-premises and import them to the Exchange Online directory. Unfortunately, the import script makes a rather dubious decision that might be causing some admins a serious headache.
The export script runs Get-MailPublicFolder against your public folder structure and extracts the following properties: Name, Alias, EmailAddresses, EntryId, HiddenFromAddressListsEnabled, LegacyExchangeDN, WindowsEmailAddresses.
The Name property is unique within a given organization. The Alias field is not. EmailAddresses is an array of email addresses which are associated with the mail-enabled public folder. The import script runs a specialized cmdlet called New-SyncMailPublicFolder which was specifically written for this script. It essentially takes the data collected from the export script for each public folder and creates a PublicFolder recipient type object in the Office 365 directory. This is only important if an Exchange Online user is sending mail to a public folder and they actually want it to be delivered.
You would expect that the import script would simply import the xml data and run the New-SyncMailPublicFolder cmdlet for each item, and for the most part that’s what it does. There is one block of code that makes very little sense and here it is:
$emailAddressesArray = @($folder.EmailAddresses);
$acceptedDomainCount = $script:AcceptedDomains.Count;
if ($folder.WindowsEmailAddress.ToString() -ne “”)
for ($index = 0; $index -lt $acceptedDomainCount; $index++)
$emailAddressesArray += $folder.WindowsEmailAddress.Local + “@” + $script:AcceptedDomains[$index].DomainName.ToString();
For those not accustomed to reading code, here’s what the script is doing.
At an earlier point the script runs a Get-AcceptedDomains command to populate the variable $script:AcceptedDomains array with all domains which are accepted by this Exchange Online tenant. The first line loads all the EmailAddresses stored in the xml file. That is where the process should probably stop. However, the next line gets a count of how many accepted domains there are and then uses that in a for loop a few lines down.
This for loop adds an email address for each accepted domain it found in your Exchange Online tenant using the name stored in the WindowsEmailAddress.Local variable. So if you have the accepted domains of contoso.com, fabrikam.com, and tradewinds.com and a WindowsEmailAddress of email@example.com, the script will add the email addresses:
# if ($folder.WindowsEmailAddress.ToString() -ne “”)
# for ($index = 0; $index -lt $acceptedDomainCount; $index++)
# $emailAddressesArray += $folder.WindowsEmailAddress.Local + “@” + $script:AcceptedDomains[$index].DomainName.ToString();
Now you can save and run the script. The objects will be created again, this time with only the email addresses found in the xml file. If you were lucky enough to find this article before you ran the script, you can just alter the script as detailed above and skip all the extra parts. You will need to change your execution policy to Unrestricted instead of RemotelySigned since altering the script invalidates the embedded signature.
You might notice that the new objects have an incremented alias name. This is okay. The old entries seem to be living on somewhere in the directory, but the important thing is having the correct LegacyDN, EntryID, and EmailAddresses.
I’ve notified Microsoft of the code issue and suggested a change. Hopefully they will post a new version of the scripts.
Director, Cloud Solutions and Microsoft MVP: Cloud (Azure/Azure Stack) & DC Mgmt