The untold story of “Use serial migration to migrate public folders to Exchange 2013 from previous versions”

 

Today I would like to share some secrets that you have to know when you try to migrate your public folders to the modern public folders following the famous article https://technet.microsoft.com/en-us/library/jj150486(v=exchg.150).aspx

I will focus mainly in four type of errors that I’ve seen repeated frequently during the migration for many customers.

If you follow the steps on the article everything should go through until you start the step 5 where the migration request start.

I will quickly explain three related issues that you may face with mail enabled public folder and normal public folders before we start developing the solution

1- The migration request will start to check every public folder and check the alias property before moving to exchange 2013.

If any special character in the alias found then the migration will throw an exception and won’t process any further folders.

FailureCode                      : -2146233088
FailureType                      : DataValidationException
FailureSide                      : Target
Message                          : Error: Property expression "AAL Ahmed ,22/6/98 " isn't valid. Valid values
are: Strings formed with characters from A to Z (uppercase or lowercase), digits
from 0 to 9, !, #, $, %, &, ', *, +, -, /, =, ?, ^, _, `, {, |, } or ~. One or more
periods may be embedded in an alias, but each period should be preceded and
followed by at least one of the other characters. Unicode characters from U+00A1 to
U+00FF are also valid in an alias, but they will be mapped to a best-fit US-ASCII
string in the e-mail address, which is generated from such an alias.

2- When it tried to change the alias I discovered that some public folders has leading or trailing whitespaces in the Name property.

WARNING: The property value is invalid. The value can't contain leading or trailing whitespace.

It’s possible to change the Name and Alias using set-MailPublicFolder but that is something that you can do when the data validation is OK and you are changing from a valid properties to another valid properties like you change the name from Test01 to Test001 but when you have a trailer whitespace the set-MailPublicFolder will throw the validation exceptions and you won’t be able to use it to change anything.
The command Set-PublicFolder in the other hand don’t perform data validation so we need to change the name first before we can check the alias and change it.

3- The last issue I faced is the wrong proxy addresses in some mail enabled folders.
WARNING: The primary SMTP address, "SMTP:A.H.N.@mylab.dev", is not valid.

Again that will block the set-MailPublicFolder command from changing the alias, so we need to run a proxy address script to fix all wrong proxy addresses and after that we can change the alias and the name properties.

Let’s start then with the proxy addresses script:

Starting from Exchange 2010, if you have some legacy old smtp addresses that are not valid, exchange will save the information into

$Email.ParseException and your job will be to check if you have any exception and remove it using set-mailpublicFolder

"Importing the public folders to the memory"
$listOfPublicFolders=import-csv publicFolders.csv
foreach($Folder in $listOfPublicFolders)

        "Start working on $($Folder.identity)"
       
        $PFFolder=Get-MailPublicFolder $($Folder.identity)
        "Found the folder $($PFFolder.identity)"       
        $Error.Clear()
        if($PFFolder)
        {
            write-progress -activity "Remove Wrong Proxy Addresses " -status "Currently processing the PF(alias) : $Folder.Alias" 
            $EmailAddresses=$PFFolder.EmailAddresses
            Foreach($Email in $EmailAddresses)
            {
                If($Email.ParseException)
                {
                    Write-Warning "$($PFFolder.alias)#Remove#$($Email.proxyaddressstring)" 
                     $Error.Clear()
                    set-mailpublicfolder -identity $PFFolder.alias -alias $($NewAlias) -emailaddresses @{remove=$($Email.proxyaddressstring)}
                    $Error[0].Exception
                }
            }
        }  

        "Finished working on $($Folder.identity)"
        "-------------------------------------------"
}

After we correct the wrong smtp addresses, it’s time to correct the alias and the name properties.

This time I decided to generate a report will all aliases that has a special characters or exceeding 64 char.

Function RegexSearchAndReplace
{
param($Test)

$SpechilaChars = ',','!', '"', '$', '%', '&', '^', '*', '(', ')', '@', '=', '+', '¬', '`', '\', '<', '>', '.', '?', '/', ':', ';', '#', '~', "'",' ' | % {[regex]::escape($_)}

$OrFiltering = [string]::join('|',$SpechilaChars)

    if(select-string -inputobject $test  -Pattern $OrFiltering -AllMatches)
    {
        $ValidAlias=$test -replace $OrFiltering, ""
       
    }
    else
    {
        $ValidAlias="No Change"
    }
    return $ValidAlias
}

"Importing the public folders to the memory"
$listOfPublicFolders=import-csv pflist.csv
"Identity#Oldalias#ValidAlias" | Out-File aliasChange.txt  
foreach($Folder in $listOfPublicFolders)

    "Start working on $($Folder.alias)"
   
        $alias=$Folder.alias
        if($alias.length -gt 64)
        {
            write-progress -activity "Checking the publicFolders " -status "Exceeding 64Char: PF(alias) : $alias" 
            "$($alias)#Exceedin 64" | Out-File -append aliasexceeding64-03.txt
        }

        $ValidAlias=RegexSearchAndReplace $alias
        $ValidAlias
        if($ValidAlias -eq "No Change")
        {
            write-progress -activity "Checking the PublicFolders " -status "No Change needed:PF(alias) : $alias" 
        }
        else
        {
            write-progress -activity "Checking the PublicFolders " -status "Spechial char found:PF(alias) : $alias" 
            "$($Folder.identity)#$($alias)#$($ValidAlias)" | out-file -append aliasChange.txt
        }
    "Finished working on $($Folder.identity)"
    "-------------------------------------------"
}

The next step is to change the alias based on the file from the previous step

"Importing the public folders to the memory"
$listOfPublicFolders=import-csv aliasChange.txt -Delimiter "#"
foreach($Folder in $listOfPublicFolders)

        "Start working on $($Folder.oldalias)"
        $OldAlias=$Folder.OldAlias
        $Newalias=$Folder.ValidAlias
        write-progress -activity "Change the Alias " -status "Currently processing the PF(alias) : $Folder.OldAlias" 
        $PFFolder=Get-MailPublicFolder $OldAlias
        "Found the folder $($PFFolder.alias)" 
        $Error.Clear()
        $PFFolder | set-MailPublicFolder -alias $($NewAlias) -ea silentlycontinue -EmailAddressPolicyEnabled:$false
        $Error[0].Exception
        Switch -wildcard ($Error[0].Exception)
        {
        "*The primary SMTP address*" {$Error.Clear();Write-Warning "Wrong SMTP $($Error[0].Exception)"}
        "*The value can't contain leading or trailing whitespace*" {$Error.Clear();Write-Warning "Found Spaces $($PFFolder.name)"; $PFFolder | Set-PublicFolder -Name $PFFolder.Name.tostring().trim();set-MailPublicFolder -identity $($Folder.Identity) -alias $($NewAlias) -ea silentlycontinue -EmailAddressPolicyEnabled:$false}   
        Default {"$($OldAlias)#$($Error[0].Exception)"}
        }
       

        "Finished working on $($Folder.identity)"
        "-------------------------------------------"
}

We didn’t finish yet Smile

Public folder migration will surprise you with a new error Fatal error TooManyLargeItemsPermanentException has occurred.

The bottom line for this issue is to change the maxSendSize to a higher number, but the question is how to know this number.

The migration report will report all large items so you can check them and found the large item size.

The other option is to generate a report will all items in the public folders and check the largest message size

Get-PublicFolder –recurse –resultsize unlimited | get-publicfolderitemstatistics | select subject,publicfoldername,messagesize

#  DISCLAIMER:
# THIS CODE IS SAMPLE CODE. THESE SAMPLES ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
# MICROSOFT FURTHER DISCLAIMS ALL IMPLIED WARRANTIES INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR OF FITNESS FOR
# A PARTICULAR PURPOSE. THE ENTIRE RISK ARISING OUT OF THE USE OR PERFORMANCE OF THE SAMPLES REMAINS WITH YOU. IN NO EVENT SHALL
# MICROSOFT OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS,
# BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE
# SAMPLES, EVEN IF MICROSOFT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. BECAUSE SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION
# OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE LIMITATION MAY NOT APPLY TO YOU.