Tag Archives: Migration

Migrating Exchange 2016 Public Folders to Office 365

Many customers are running in an Exchange hybrid environment where they have mailboxes in Exchange Online and in Exchange on-premises and a lot of my customers have Exchange 2016 running on-premises. Not a lot of customers still have Public Folders in Exchange on-premises, but they are still there. This blog explains steps to migrate Modern Public Folders from Exchange 2016 to Exchange Online, but this blog is also valid for modern Public Folders in Exchange 2013 and Exchange 2019. If you are still running Exchange 2010 and you want to move your (legacy) Public Folders to Exchange Online, follow the steps in this Microsoft article: Use batch migration to migrate legacy public folders to Microsoft 365 or Office 365.

Note. This is a long read. It has also been a long project, preparations took a couple of weeks, the synchronization a couple of days, roll-back after the first attempt to finalize the migration and start over again with fixing the unexpected issues. When migration Public Folder from Exchange 2016 to Exchange Online, take your time and do it right!

When to migrate your Public Folders to Exchange Online

Public Folder access cross-premises is one-way only. Mailboxes in Exchange Online can access Public Folders in Exchange on-premises, but not the other way around. So, mailboxes in Exchange on-premises cannot access Public Folders in Exchange Online. You should only migrate Public Folder to Exchange Online, after you have migrated all user mailboxes to Exchange Online.

Note. I deliberately say “user mailboxes” in this context, you can still have mailboxes in Exchange on-premises for applications, service accounts, devices etc. that do not need to access Public Folders.


The following requirements on-premises need to be met before the migration of Public Folders to Exchange Online can be started:

  • Exchange 2016 CU4 or higher (which is a no brainer in my opinion)
  • The Exchange administrator needs to be a member of the Organization Management role group (in Exchange Online and in Exchange 2016)
  • Public Folders need to be less than 25GB in size
  • User mailbox migration need to be finished before Public Folder migration starts
  • Migration needs to be executed using Exchange PowerShell. The Public Folder Migration option is not available in the Exchange Admin Center
  • All Public Folder data must be migrated in one single migration batch
  • Verify if the DefaultPublicFolderAgeLimit is configured on the organization level or if you have any AgeLimit configured for the individual Public Folders, so that automatic deletions of the content is prevented

e domains, the Exchange system objects (like mail-enabled Public Folder objects) can reside in multiple locations since these locations have changed over the years (with different versions of Exchange). Sometimes these object can be found in the root domain, but also in a child domain. If you are using an Active Directory environment with multiple domains, make sure you extend the scope of Powershell using the following command:

[PS] C:\> Set-ADServerSetting -ViewEntireForest $True

Migration Steps

Migration of Public Folders from Exchange 2016 to Exchange Online consists of the following steps:

  1. Download the migration scripts
  2. Prepare for the migration
  3. Generate the CSV files
  4. Create Public Folder mailboxes in Exchange Online
  5. Start Migration Request
  6. Lockdown Public Folders in Exchange 2016
  7. Finalize Public Folder migration
  8. Test and unlock Public Folders in Exchange Online
  9. Finalize the migration in Exchange 2016

These steps will be discussed in the following sections.

1. Download the migration scripts

Download the premigration or source side validation script from the Microsoft website https://aka.ms/ssv2 (SourceSideValidations.ps1) and the Public Folder migration scripts (https://www.microsoft.com/en-us/download/details.aspx?id=54855). Store the scripts on the Exchange server in the C:\PFScripts directory.

The SourceSideValidations.ps1 script does an inventory of all Public Folders in the Exchange organization and reports any issues that are found and can cause problems during the Public Folders migration.

The results of this script are written in the SourceSideValidations.csv in the PFScripts directory and contains something like this:

TotalItemSizeError2Items should be deleted from these folders until the folder size is less than 25 GB.
EmptyFolderInformation138Folders contain no items and have only empty subfolders. These will not cause a migration issue, but they may be pruned if desired.
SpecialCharactersError12Folders have characters @, /, or \ in the folder name. These folders should be renamed prior to migrating. The following command can be used:

Import-Csv .\ValidationResults.csv |
? ResultType -eq SpecialCharacters |
% {
$newName = ($_.ResultData -replace “@|/|\”, ” “).Trim()
Set-PublicFolder $_.FolderEntryId -Name $newName
OrphanedMPFError37Mail public folders are orphaned. They exist in Active Directory but are not linked to any public folder. Therefore, they should be deleted. After confirming the accuracy of the results, you can delete them manually, or use a command like this to delete them all:
Import-Csv .\ValidationResults.csv |
? ResultType -eq OrphanedMPF |
% {
$folder = <see below>
$parent = ([ADSI]”$($folder.Parent)”)
OrphanedMPFDuplicateError3Mail public folders point to public folders that point to a different directory object. These should be deleted. Their email addresses may be merged onto the linked object. After confirming the accuracy of the results, you can delete them manually, or use a command like this:
Import-Csv .\ValidationResults.csv |
? ResultType -eq OrphanedMPFDuplicate |
% {
$folder = <see below>
$parent = ([ADSI]”$($folder.Parent)”)
OrphanedMPFDisconnectedError1Mail public folders point to public folders that are mail-disabled.
These require manual intervention. Either the directory object should be deleted, or the folder should be mail-enabled, or both.
Open the ValidationResults.csv and filter for ResultType of OrphanedMPFDisconnected to identify these folders. The
FolderIdentity provides the DN of the mail object. The FolderEntryId provides the EntryId of the folder.
BadpermissionError89Invalid permissions were found. These can be removed using the RemoveInvalidPermissions switch as follows:
.\SourceSideValidations.ps1 -RemoveInvalidPermissions

Note. In this table in row 4 and row 5 there’s the $folder variable. I was able to add the PowerShell command in there, so here’s the command. Please substitute as needed:

$Folder = ([ADSI]("LDAP://$($_.FolderIdentity)"))

Besides reporting the issues, the last column also reports the solutions about how to solve the issues. These are (also) discussed in the next section.

2. Prepare for the migration

The SourceSideValidations.ps1 script is a good starting point as it returns a number of potential issues for the migration and these need to be fixed before you can start the migration:

  • The TotalItemSize is easy to fix. Remember that the size is per folder, so if you have a large folder containing for example 40GB of data, you can create 3 subfolders and move 10GB of data in each subfolder. Problem solved 🙂
  • The EmptyFolder is informational. You can migrate these to Exchange Online, or decide to remove them before the migration.
  • Public Folder names that contain a backslash or a forward slash are not supported in Exchange Online. These are also reported by the SourceSideValidations.ps1 script. To check for Public Folders containing these characters, you can use the following commands in Exchange PowerShell on-premises:
[PS] C:\> Get-PublicFolder -Recurse -ResultSize Unlimited | Where {$_.Name -like "*\*" -or $_.Name -like "*/*"} | Format-List Name, Identity, EntryId

To remove these illegal characters, you can use the following commands in Exchange PowerShell on-premises:

Import-Csv .\ValidationResults.csv | ? ResultType -eq SpecialCharacters |
% {
  $newName = ($_.ResultData -replace "@|/|\\", " ").Trim()
  Set-PublicFolder $_.FolderEntryId -Name $newName
  • Check for orphaned folders, duplicate orphaned folders and disconnected orphaned folders. You can try to mail-disabled the corresponding Public Folder and re-enable the Public Folder again. I have also seen situations where the Exchange object in Active Directory (in the Microsoft Exchange System Objects container) was deleted using ADSI Edit.
  • Confirm SMTP Email addresses and Accepted Domains in Exchange on-premises and Exchange Online match. Most likely this is the case since all (user) mailboxes are already migrated, but it can happen that an email address is set on a Public Folder in Exchange 2016 with a domain that’s unknown in Exchange Online (contoso.local for example).
  • Also, make sure your public folders are stamped with a Microsoft online email address, like folder@contoso.mail.onmicrosoft.com. This makes life much easier after the migration, when you still have local application trying to send email to public folders.
  • Create an Accepted Domain with a well-known name to prevent messages getting lost in the DNS transition period. To do this, execute the following command in Exchange PowerShell on-premises:
[PS] C:\> New-AcceptedDomain -Name PublicFolderDestination_78c0b207_5ad2_4fee_8cb9_f373175b3f99 -DomainName "contoso.mail.onmicrosoft.com" -DomainType InternalRelay
  • Create a snapshot of the existing Public Folder environment in Exchange 2016. This can be useful when checking if the Public Folder migration was successful. The following four commands will export the Public Folders, the Public Folder Statistics, the Public Folder permissions and the mail-enabled Public Folders. Be aware that this can take a considerable amount of time, depending of the number of Public Folders in your organization.
[PS] C:\> Get-PublicFolder -Recurse -ResultSize Unlimited | Export-CliXML OnPrem_PFStructure.xml
[PS] C:\> Get-PublicFolderStatistics -ResultSize Unlimited | Export-CliXML OnPrem_PFStatistics.xml
[PS] C:\> Get-PublicFolder -Recurse -ResultSize Unlimited | Get-PublicFolderClientPermission | Select-Object Identity,User,AccessRights -ExpandProperty AccessRights | Export-CliXML OnPrem_PFPerms.xml
[PS] C:\> Set-ADServerSettings -ViewEntireForest $True
[PS] C:\> Get-MailPublicFolder -ResultSize Unlimited | Export-CliXML OnPrem_MEPF.xml

  • In Azure AD Connect there’s the option to synchronize Exchange Mail Public Folders to Azure AD, as shown in the following screenshot:

This is used for Directory Based Edge Blocking (DBEB) only so that external mail for Public Folders is not blocked by DBEB. DBEB is automatically available when recipients are in Exchange Online, so there’s no need to synchronize this using Azure AD Connect. Uncheck the Exchange Mail Public Folders in the Optional Features in Azure AD Connect.

Note. When the issues are solved and all prerequisites are met, run the SourceSideValidations.ps1 script again to see if no more issues are returned.

3. Generate .CSV Files

If you have done all the prerequisite step, it’s time to generate the CSV files in preparation of the actual Public Folder migration to Exchange Online.

The first CSV file is the Name-to-Folder size mapping file. This file contains three columns: FolderSize, DeletedItemSize and Foldername. To create this file, execute the Export-ModernPublicFolderStatistics.ps1 script with a filename option, like this:

[PS] C:\PFScripts\> .\Export-ModernPublicFolderStatistics.ps1 On_Prem_Stats.csv

The second CSV is created using the ModernPublicFolderToMailboxMapGenerator.ps1 script. This script creates a mapping between the Public Folders from the previous step to mailboxes in Exchange Online. For input, this script takes the CSV file from the previous step, together with the maximum Public Folder mailbox size and the maximum mailbox recoverable items quota.

The maximum mailbox size by default is 100GB, but it recommended to use 50GB here to anticipate for future growth of the Public Folders in this mailbox. The recommended size for recoverable items quota is 15GB.

The command to execute this script is something like this:

[PS] C:\> PFScripts\ .\ModernPublicFolderToMailboxMapGenerator.ps1 -MailboxSize 50GB -MailboxRecoverableItemSize 15GB -ImportFile On_Prem_Stats.csv -ExportFile PFMapping.csv

When you look at the output file you will see only two columns: TargetMailbox and FolderPath. The TargetMailbox shows the Public Folder mailboxes that will be created. These Public Folder mailboxes have generic names, like Mailbox1, Mailbox2, Mailbox3 etc. You can change the Public Folder mailbox names in the CSV file into something that’s more suitable for your environment, for example PFMailbox1, PFMailbox2, PFmailbox3 etc.

4. Create Public Folder mailboxes in Exchange Online

When all information has been gathered the Public Folder mailboxes can be created. One Public Folder mailbox will be used for the Public Folder hierarchy (this will be primary mailbox, and the first mailbox in the CSV file that was created earlier) and the others are used for storing the Public Folders contents.

To create the Public Folder mailboxes, use the following commands in Exchange Online (!) PowerShell:

$PFMappings = Import-Csv C:\PFScripts\PFMapping.csv
$PrimaryMailboxName = ($PFMappings | Where-Object FolderPath -eq "\").TargetMailbox
New-Mailbox -HoldForMigration:$True -PublicFolder -IsExcludedFromServingHierarchy:$False $PrimaryMailboxName
($PFMappings | Where-Object TargetMailbox -ne $PrimaryMailboxName).TargetMailbox | Sort-Object -Unique | ForEach-Object { New-Mailbox -PublicFolder -IsExcludedFromServingHierarchy:$False $_}

It is possible that some warning messages appear because of AD replication within Exchange Online as can be seen in the following screenshot. Just wait some time and these Public Folder mailboxes will become automatically.

5. Start Migration Request

Before starting the migration request, start the synchronization of mail-enabled public folders to Exchange Online. In my current project I don’t have to do this since this script was already running as part of the hybrid configuration (mailboxes in Exchange Online, Public Folders in Exchange 2016).

But to start this, run the following command:

[PS] C:\PFScripts> .\Sync-ModernMailPublicFolders.ps1 -CsvSummaryFile:sync_summary.csv

Note. Be aware that you use the latest version of this script. Older version (prior to June 2022) do not support Modern Authentication and will fail with an Access Denied error. I have blogged about this a couple of months ago: Sync-ModernMailPublicFOlders.ps1 fails with access denied.

It is possible that error messages are shown on the console, and if you have a lot of Public Folders these can also be quite a lot (been there, done that unfortunately), but you can import the sync_summary.csv file into Microsoft Excel for detailed analysis.

To create the migration request, we need the source credential of the PF Administrator, the endpoint where the MRS is running, the GUID of the hierarchy mailbox in Exchange 2016 and the Public Folder mapping file (PFMapping.csv) that was created in the previous step.

Execute the following command in Exchange 2016 PowerShell. Copy the value of $HierarchyGUID to (for example) Notepad since it will be used in the last command where the actual migration batch is created.

[PS] C:\PFScripts> $HierarchyGUID = (Get-OrganizationConfig).RootPublicFolderMailbox.HierarchyMailboxGuid.GUID

Execute the following command in Exchange Online PowerShell:

[PS] C:\PFScripts> $Source_Credential = Get-Credential Contoso\Administrator
[PS] C:\PFScripts> $Source_RemoteServer = "webmail.contoso.com"
[PS] C:\PFScripts> $Mapping = [System.IO.File]::ReadAllBytes('C:\PFScripts\PFMapping.csv')
[PS] C:\PFScripts> $PfEndpoint = New-MigrationEndpoint -PublicFolder -Name PublicFolderEndpoint -RemoteServer $Source_RemoteServer -Credentials $Source_Credential
[PS] C:\PFScripts> New-MigrationBatch -Name PublicFolderMigration -CSVData $Mapping -SourceEndpoint $PfEndpoint.Identity -SourcePfPrimaryMailboxGuid <HierarchyGUID> -NotificationEmails Administrator@contoso.com

As shown in the following screenshot:

Note. Please take some time between creating the Public Folder mailboxes (especially the primary hierarchy mailbox), the New-MigrationEndpoint command will fail if there’s not enough time for internal Exchange Online replication, causing error messages like “mailbox <GUID> cannot be found”.

When created you can start the migration request using the following command in Exchange Online Powershell:

[PS] C:\PFScripts> Start-MigrationBatch PublicFolderMigration

When the migration batch is started, it will create a number of Public Folder Mailbox Migration Requests, depending on the number of Public Folder Mailboxes. In this example, there are 18 Public Folder mailboxes and 18 individual migration requests will be created as part of the migration batch. It took up to 4 hours before all migration requests were created. Be aware of this because at first you will be thinking that something is wrong 🙂

Use the following commands in Exchange Online PowerShell to monitor the migration batch and the individual migration requests:

[PS] C:\PFScripts> Get-PublicFolderMailboxMigrationRequest
[PS] C:\PFScripts> Get-PublicFolderMailboxMigrationRequestStatistics
[PS] C:\PFScripts> Get-MigrationBatch -Identity PublicFolderMigration

What I personally do for some more details is adding some more options and using the format-table feature, like this:

[PS] C:\PFScripts\> Get-PublicFolderMailboxMigrationRequest | Get-PublicFolderMailboxMigrationRequestStatistics | Select TargetMailbox,Status,StatusDetail,ItemsTransferred,BytesTransferred,BytesTransferredPerMinute,PercentComplete | ft -a

If for some reason you have failed migration requests in your migration batch, you can always run the Start-MigrationBatch -Identity PublicFolderMigration command to resume the batch.

Another interesting issue I had was that the PFMailbox1 that holds the hierarchy, failed synchronization with StatusDetail ‘FailedOther’. When requesting the PublicFolderMailboxMigrationStatistics for this PFMailbox, the following error is returned:

olderMappingFlags: InheritedInclude" could not be mail-enabled. The error is as follows: "No mail public folder was found in Active Directory with OnPremisesObjectId='dd887445-0b0a-447f-a6fc-889cc49ab16c' or LegacyExchangeDN='/CN=Mail Public Folder/CN=Version_1_0/CN=e71f13d1-0178-42a7-8c47-24206de84a77/CN=000000006F0ABC0AC0DF544387022DEA38DAE5840100F33760E70CFA4C489E930054C1EC880900038611485B0000'". This may indicate that mail public folderobjects in Exchange Online are out of sync with your Exchange deployment. You may need to rerun the script Sync-MailPublicFolders.ps1 on your source Exchange server to update mail-enabled public folder objects in Exchange Online Active Directory.

Synchronization takes a couple of days (I started on Tuesday and plan to finalize the next weekend) and during that time new mail-enabled Public Folders are created and synchronized.

To bring this back in sync, run the Sync-ModernMailPublicFolders.ps1 script again, wait some time (for replication in Exchange Online) and start the migration batch again.

Remember that up to this point, users can continue to work just like they can when their mailbox is migrated to Exchange Online. Only when the migration request is finalized the Public Folders are not available.

6. Lockdown Public Folders in Exchange 2016

Finalizing the Public Folder migration is not different than when migrating Mailboxes from Exchange 2016 to Exchange Online. When the finalization takes places, users are logged off of the Public Folders, the last content is migrated to Exchange Online and the Public Folder mailboxes in Exchange Online become active. However, they are not automatically available to users, some additional steps (including testing) are needed.

Important to note is that a Public Folder migration finalization can take a lot of time, depending on the number of folders, the number of items (equals data) and if there are corrupt ACLs in the source Public Folders. Microsoft recommends to plan at least 48 hours of downtime during the Public Folder migration finalization.

To check if the Public Folder migration batch is successfully synced (and thus ready to finalize) use the following commands in Exchange Online PowerShell:

[PS] C:\PFScripts> Get-MigrationBatch -Identity PublicFolderMigration | ft *last*sync*
[PS] C:\PFScripts> Get-PublicFolderMailboxMigrationRequest | Get-PublicFolderMailboxMigrationRequestStatistics |ft targetmailbox,*last*sync*

Preferably, the LastSyncedDate on the migration batch and the LastSuccessfulSyncTimestamp on the individual jobs) should be within the last 7 days as can be seen in the following screenshot. If it’s not, check the Public Folder migration requests to see why it is not in sync.

If all is ok, you can lock down the Public Folders in Exchange 2016 by executing the following command in Exchange PowerShell:

[PS] C:\PFScripts> Set-OrganizationConfig -PublicFolderMailboxesLockedForNewConnections $true

After (Active Directory) replication you can check if Public Folders do not accept new connections anymore by typing the following command in Exchange PowerShell:

[PS] C:\PFScripts> Get-PublicFolder \

It should generate an error message saying “Could not find the public folder mailbox” as shown in the following screenshot:

7. Finalize Public Folder migration

Before finalizing the Public Folder migration you should run the SyncModernPublicFolders.sp1 script again to make sure that newly created Mail-Enabled Public Folder (or better, their email addresses) are synchronized with Exchange Online.

[PS] C:\PFScripts> .\Sync-ModernPublicFolders.ps1 -CsvSummaryFile:Sync_Summary.csv

And to complete the migration batch:

[PS] C:\PFScripts> Complete-MigrationBatch PublicFolderMigration

Migrationbatch status will change from Synced to Completing. This can take a tremendous amount of time; Microsoft recommendation is to take 48 hours into account for this. In my scenario, there are 18 Public Folder mailboxes that are sync. The on-premises Public Folders were closed around 10PM on Friday night, but after twelve hours all Public Folder Mailbox Requests still had a status of ‘synced’. But when requesting the details of the migration batch (Get-MigrationBatch | fl) the TriggeredAction property of the migration batch was set to SyncAndComplete. Microsoft also says that it can take up to 24 hours before the status of the migration batch and the corresponding migration requests change from ‘synced’ to ‘Completing’. In the meantime, you can only wait, and check back every few hours. Eventually the migrationbatch status will change to Completed.

When the migration batch is completed you can test the Public Folders in Exchange. To do this, configure a user account with the default Public Folder mailbox in Exchange Online, using the following command in Exchange Online PowerShell:

[PS] C:\PFScripts> Set-Mailbox -Identity <user> -DefaultPublicFolderMailbox PFMailbox1

The default Public Folder mailbox is the first PF mailbox (holding the hierarchy) that was created in a previous step using the ModernPublicFolderToMailboxMapGenerator.ps1 script.

Make sure you can see the PF hierarchy, check the permissions, create some Public Folders (and delete them) and post some content into Public Folders (both direct as via email). In our first attempt, permissions failed and we had to roll-back the migration. It took over 6 weeks before we could do a second attempt (ok, I have to admit, it was holiday time, but still….).

When tested successfully, change the Public Folders for all users. This is an organizational setting and can be changed using the following command in Exchange Online PowerShell:

[PS] C:\PFScripts> Set-OrganizationConfig -RemotePublicFolderMailboxes $Null -PublicFoldersEnabled Local

It is possible that SMTP message are stuck in SMTP Queues on the Exchange 2016 servers during the migration. To redirect these stuck messages run the following command on your Exchange 2016 server:

[PS] C:\PFScripts> $Server=Get-TransportService;ForEach ($t in $server) {Get-Message -Server $t -ResultSize Unlimited| ?{$_.Recipients -like "*PF.InTransit*"} | ForEach-Object {Suspend-Message $_.Identity -Confirm:$False; $Temp="C:\ExportFolder\"+$_.InternetMessageID+".eml"; $Temp=$Temp.Replace("<","_"); $Temp=$Temp.Replace(">","_"); Export-Message $_.Identity | AssembleMessage -Path $Temp;Resume-message $_.Identity -Confirm:$false}}

To stamp Mail-Enabled Public Folder objects in Active Directory with an external email address in Exchange Online (i.e. @contoso.mail.onmicrosoft.com) execute the following PowerShell script in Exchange 2016:

[PS] C:\PFScripts> .\SetMailPublicFolderExternalAddress.ps1 -ExecutionSummaryFile:mepf_summary.csv

And the ultimate last step, set the Public Folders in Exchange 2016 to ‘remote’. This is an organizational settings and can be configured by executing the following command in Exchange 2016:

[PS] C:\PFScripts> Set-OrganizationConfig -PublicFolderMailboxesMigrationComplete:$true -PublicFoldersEnabled Remote

8. Remove Public Folder Mailboxes

After some time, if you are 100% sure you are not going to roll-back your Public Folder migration, the Public Folder Mailboxes in Exchange 2016 can be removed. Remember, this step is irreversible!

Roll-back Public Folder migration

If for some reason you must roll-back the migration you must execute the PowerShell commands in a reverse order. Be aware that if you roll back the migration after you finalized the migration, you will lose all mail delivered to the Public Folders in Exchange Online (unless you manually copy all new items to a location in a mailbox, which is practically impossible of course).

The first step is to unlock the Public Folder migration in Exchange 2016 by using the following command:

[PS] C:\> Set-OrganizationConfig -PublicFoldersLockedForMigration:$False

Delete all Public Folder mailboxes in Exchange Online using the following Powershell commands:

PS] C:\> $hierarchyMailboxGuid = $(Get-OrganizationConfig).RootPublicFolderMailbox.HierarchyMailboxGuid
[PS] C:\> Get-Mailbox -PublicFolder:$true | Where-Object {$_.ExchangeGuid -ne $hierarchyMailboxGuid} | Remove-Mailbox -PublicFolder -Confirm:$false -Force
[PS] C:\> Get-Mailbox -PublicFolder:$true | Where-Object {$_.ExchangeGuid -eq $hierarchyMailboxGuid} | Remove-Mailbox -PublicFolder -Confirm:$false -Force

If you run into issues with this, check one of my previous blogs Multiple Mailbox users match identity “Mailbox1” (which I ran into after a roll-back and a 2nd migration attempt).

The last step is to undo the migration completion in the organization config of Exchange 2016 using the following command:

[PS] C:\> Set-OrganizationConfig -PublicFolderMigrationComplete:$False

You should now be able to continue to work with the Public Folders in Exchange 2016.

Send-As and Send-on-Behalf permissions

A common pitfall is that Send-As and Send-on-Behalf permissions are not migrated to Exchange Online. If you are using these permissions you have to identify the Public Folders that have these permissions applied using the following commands:

[PS] C:\> Get-MailPublicFolder | Get-ADPermission | ?{$_.ExtendedRights -like "*Send-As*"}
[PS] C:\> Get-MailPublicFolder | ?{$_.GrantSendOnBehalfTo -ne "$null"} | Format-Table name,GrantSendOnBehalfTo

To grant these permissions in Exchange Online, use the following example commands in Exchange Online PowerShell:

[PS] C:\> Add-RecipientPermission -Identity <Public Folder> -Trustee <User> -AccessRights SendAs
[PS] C:\> Set-MailPublicFolder -Identity <Public Folder> -GrantSendOnBehalfTo <User>

Most likely you will have tons of Public Folders with these permissions, to you must first export to a CSV file, following with an import of the CSV file and assigning permissions in Exchange Online.

Allow anonymous users to send email to a mail-enabled Public Folder

I did not run into this after the migration to Exchange Online, but it might be possible that you lose the anonymous user permissions to send mail to a public folder after the migration. To make sure anonymous users can send email to a Public Folder, use the following command in Exchange Online PowerShell:

[PS] C:\> Add-PublicFolderClientPermission -Identity "\publicfoldername" -User "Anonymous" -AccessRights CreateItems


In this blog I tried to write down my experiences with a recent Public Folder migration from Exchange 2016 to Exchange Online. Depending on your Public Folder infrastructure this can take a considerable amount of time. And even if you have everything right, there still is the possibility that something goes wrong and you have to start all over again.

Although after the migration the Public Folder infrastructure is Microsoft’s problem and you only have the service available, I still recommend not migrate your Public Folders to Exchange Online and look for a different solution like Microsoft 365 Groups, Shared Mailboxes or Microsoft Teams. That’s the better solution, and at least it’s more future proof than Public Folders.

Multiple mailbox users match identity “Mailbox1”

So I’m working on a nice Public Folder migration from Exchange 2016 to Exchange Online. Last week all preparations were performed and this morning I was planning to start the migrationbatch. Fourteen Public Folder mailboxes in Exchange 2016 to eighteen Public Folder mailboxes in Exchange Online. What could possibly go wrong….

Creating the new endpoint for the migrationbatch failed with an error saying “Multiple mailbox users match identity “PFMailbox1″. Specify a unique value” as shown in the following screenshot:

Or in plain text:

PS C:\PFScripts> $PfEndpoint = New-MigrationEndpoint -PublicFolder -Name PublicFolderEndpoint -RemoteServer $Source_RemoteServer -Credentials $Source_Credential
Multiple mailbox users match identity "Mailbox1". Specify a unique value.
    + CategoryInfo          : NotSpecified: (:) [New-MigrationEndpoint], MigrationPermanentException
    + FullyQualifiedErrorId : [Server=AM0PR09MB2402,RequestId=bccebc4f-d231-41d3-b8fe-a676311b3575,TimeStamp=22-8-2022 09:33:52] [FailureCategory=Cmdlet-MigrationPermanentException] 80069390,Microsoft.Exchange.Management.Migration. MigrationService.Endpoint.NewMigrationEndpoint
    + PSComputerName        : outlook.office365.com
PS C:\PFScripts>

This is strange since there’s no mailbox user named “Mailbox1”, there’s only one Public Folder mailbox named “Mailbox1” and that is holding the Public Folder hierarchy. But, last week I have been preparing the Public Folder migration, and one step was to create a Public Folder mailbox, just to see if it would work. I deleted the Public Folder mailbox, but it is a soft deleted state, and this conflicts with the creation of the Public Folder migration endpoint.

To find the Public Folder mailboxes including the soft deleted mailboxes, execute the following command against Exchange Online:

PS C:\PFScripts> Get-Recipient  -IncludeSoftDeletedRecipients -RecipientTypeDetails publicfoldermailbox | fl Name, OrganizationalUnit, DistinguishedName, ExchangeGuid

It will return a list of Public Folder mailboxes, including the ones in a soft deleted state. Clearly visible in the following screenshot is the soft deleted Public Folder mailbox:

Use the following command against Exchange Online to remove the soft deleted Public Folder mailbox:

[PS] C:\> Remove-Mailbox -PublicFolder "<ExchangeGuid>" -PermanentlyDelete

Wait some time for replication to happen in EXODS and try again to create the Public Folder migration endpoint. This time it succeeded.

Hafnium and Exchange mitigation and remediation

Last week Microsoft discovered a zero-day vulnerability for Exchange (which was initially detected by security companies last January) and an urgent patch was released. Unfortunately this patch is only available for recent versions of Exchange 2019 and Exchange 2016 and the last version of Exchange 2013. If you have an older version of Exchange running you have to bring it to the latest Cumulative Update first and then deploy the Security Update.

There are some mitigation rules available though:

  • Exchange server that are not available on the Internet are much less vulnerable (ok, this is an open door, I know). I have two customers that have their Exchange servers available only via a VPN connection. This works well from a security perspective.
  • Similar to the previous bullet, Exchange hybrid servers should not be publicly available, only Exchange Online must be able to access the Exchange on-premises servers. URL’s and IP ranges can be found on https://docs.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide.
  • Microsoft also posted a number of mitigation rules on the Microsoft Security Response Center Blog. These mitigation rules are temporary though and should only be used until the Exchange servers are fully patched. Mitigation rules are an IIS Re-Write Rule, disabling UM services and disable OAB en ECP Application Pool.

Microsoft has published a script (Test-ProxyLogon.ps1) on GitHub that can be used to check your Exchange servers if they are compromised. This script can be found on CSS-Exchange/Security at main · microsoft/CSS-Exchange · GitHub.

When you run the script it will show in seconds if something is found:

Too bad that this is a production Exchange 2016 server that was compromised.

At this moment I would recommend to turn off and remove the Exchange server and rebuild it using the /Mode:RecoverServer option of the Exchange setup application. This is documented in my next blog Rebuild your server (after HAFNIUM infection. When other (better or easier) recommendations are published I’ll update this blog.

Last updated: March 10, 2021

Exchange 2010 Public Folder migration

So far, all my customers had decommissioned Public Folders in the Exchange 2010 timeframe, all migration I did have never included Public Folders, until now. The Exchange 2010 (hybrid) to Exchange 2016 (hybrid) migration included approx. 1000 mailboxes and maybe 100 Public Folders (in only one Public Folder database) but they were real Public Folders with real users using them 😊

Migrating Public Folders looks easy, it’s a matter of gathering information regarding the Public Folders and its permissions, copying that over to a Public Folder database and keeping the contents in sync and when you’re ready finalize the migration. Or in steps:

  1. Downloading scripts from Microsoft from https://www.microsoft.com/en-us/download/details.aspx?id=38407.
  2. Preparing the organization for public folder migration.
  3. Generating CSV files using the Microsoft scripts.
  4. Creating public folder mailboxes in Exchange 2016 databases.
  5. Starting the migration, and waiting for initial synchronization to complete.
  6. Locking the public folders for final migration (this requires an outage, usually of at least an hour, but longer for very large environments).
  7. Finalize the public folder migration.
  8. Testing modern public folders, and then unlocking them for access by users (the outage is now over).

I know there are several other blogs available on this topic, but this way I also have my own reference available.

Note. Migrating Public Folders can only be done when all mailboxes are migrated to Exchange 2016. Mailboxes still on Exchange 2010 cannot access Public Folders on Exchange 2016!

Take snapshot of existing Public Folders on Exchange 2010

The first step is to take a snapshot of the existing environment, both the Public Folders, their statistics and permissions. Basic PowerShell and the output is exported to an XML file:

[PS] C:\> Get-PublicFolder -Recurse | Export-CliXML C:\PFMigration\Legacy_PFStructure.xml
[PS] C:\> Get-PublicFolderStatistics | Export-CliXML C:\PFMigration\Legacy_PFStatistics.xml
[PS] C:\> Get-PublicFolder -Recurse | Get-PublicFolderClientPermission | Select-Object Identity,User -ExpandProperty AccessRights | Export-CliXML C:\PFMigration\Legacy_PFPerms.xml

Most customers will run into issues with naming of their Public Folders. Customers tend to use strange characters in the Public Folders names (back slash, forward slash) and these are not supported. Hence, they should be replaced. To retrieve a list of all Public Folders with a back slash or forward slash in their name, you can use the following command:

[PS] C:\> Get-PublicFolderStatistics -ResultSize Unlimited | Where {($_.Name -like “*\*”) -or ($_.Name -like “*/*”) } | Format-List Name, Identity

You can use any tool you like to change the name, depending of the number of Public Folders of course. If you have 300,000 Public Folders you won’t use the Exchange Management Console to changes names of Public Folders (well, I cannot imagine you would).
Check for any previous migration attempts in your Exchange environment:

[PS] C:\> Get-OrganizationConfig | Format-List PublicFoldersLockedforMigration, PublicFolderMigrationComplete

Both values should have a value of False:

PF Locked for Migration

You can (should) use the Get-MigrationBatch cmdlet on Exchange 2016 (!) to see if there are any previous migration batches for Public Folders. If there are, delete them using the Remove-MigrationBatch cmdlet before continuing.

Generate the CSV files

The next step is to export the Public Folder statistics using the scripts downloaded from the Microsoft site. This will create a CSV file which is later used to map it to one or more Public Folders mailboxes. Run the following command on the Exchange 2010 server:

[PS] C:\PFScripts> .\Export-PublicFolderStatistics.ps1 C:\PFMigration\PFStatistics.csv


The PublicFolderToMailboxMapGenerator scripts is used to create a mapping file. This maps the output of the previous command to one or more Public Folder mailboxes.
For this, you need to set the maximum size of a Public Folder mailbox. For example, to use a maximum size of 30GB, the value (in bytes) would be 32212254720. So, for a 30GB Public Folder mailbox you can use the following command:

[PS] C:\PFScripts > .\PublicFolderToMailboxMapGenerator.ps1 32212254720 C:\PFMigration\PFStatistics.csv C:\PFMigration\folder-to-mailbox.csv


Create the Public Folder mailboxes in Exchange 2016

The Public Folder database(s) in Exchange 2016 are created using the Create-PublicFolderMailboxesForMigration.ps1 script. This script takes the mapping file that was created in the previous step, and it takes the estimated number of concurrent users. In my environment the number of concurrent users is approx. 900, so the command will be:

[PS] C:\PFScripts> .\Create-PublicFolderMailboxesForMigration.ps1 -FolderMappingCsv C:\PFMigration\folder-to-mailbox.csv -EstimatedNumberOfConcurrentUsers:900


This will create a mailbox called Mailbox1 in a random Exchange 2016 database. If you want to use a specific database, you can edit the Create-PublicFolderMailboxesForMigration.ps1 script and add the -Database parameter to the New-Mailbox command.


Public Folder Migration Batch

At this point everything is in place for the Public Folder migration. The Public Folder database has been created, the Public Folders are created in this Mailbox and the permissions are set. A migration batch can be created using the following command on the Exchange 2016 server:

[PS] C:\PFScripts> New-MigrationBatch -Name PFMigration -SourcePublicFolderDatabase (Get-PublicFolderDatabase -Server ) -CSVData (Get-Content -Encoding Byte) -NotificationEmails

This would translate to something like:

[PS] C:\PFScripts> New-MigrationBatch -Name PFMigration -SourcePublicFolderDatabase PF01 -CSVData (Get-Content “C:\PFMigration\folder-to-mailbox.csv” -Encoding Byte) -NotificationEmails j.wesselius@exchangelabs.nl

Start the migration batch using the Start-MigrationBatch cmdlet:


It is possible to use the Get-PublicFolderMailboxMigrationRequest to get more information regarding the migration from Exchange 2010 to Exchange 2016:


You can see this migration batch in the Exchange Admin Center as well (under Recipients | Migration).

Exchange Admin Center

When you click the View Details option, all details regarding the migration, including the number of Public Folders, sizes, performance etc. will be downloaded as a text file.

Migration Status Report

To get more information regarding the batch you can also use the Get-PublicFolderMailboxMigrationRequest in combination with the Get-PublicFolderMailboxMigrationRequest Statistics command. This also gives a lot of information, and using the Select parameter you can only show the information you’re interested in. For example:

[PS] C:\>PFScripts> Get-PublicFolderMailboxMigrationRequest | Get-PublicFolderMailboxMigrationRequestStatistics | Select StartTimeStamp, CompleteAfter,BytesTransferred, ItemsTransferred, PercentComplete,Message


The ItemsTransferred value can be compared against the ItemsCount value when running Get-PublicFolder -Recurse | Get-PublicFolderStatistics command on the old Exchange 2010 server. This should closely match.

Finalize the Public Folder migration

All users still connect to the old Public Folders in Exchange 2010 and all changes are replicated to the Public Folders in Exchange 2016. The last stop in the migration is finalizing. In this step the Public Folders in Exchange 2010 are closed (users are disconnected) and the remaining contents are synchronized with Exchange 2016.

Be aware that this automatically means downtime for users. I typically recommend finalizing migrations off business hours. Since finalizing Public Folder migration can take quite some time (and this is unpredictable) I recommend starting this on Friday night.

The migration batch is now completed, and the new Public Folders in Exchange 2016 can be tested. Once ok the migration is finalized, and all users can connect to Public Folders in Exchange 2016. This is fully transparent for users, so no need to do anything on the client side.

To lock the Public Folders for migration, change the organization configuration and complete the migration batch, use the following commands:

[PS] C:\PFScripts> Set-OrganizationConfig -PublicFoldersLockedForMigration:$true Set-OrganizationConfig -PublicFoldersEnabled Remote

It can take some time before the previous commands are activated. When not fully active and continue with the Complete-MigrationBatch cmdlet, you will receive an error message stating:

Before finalizing the migration, it is necessary to lock down public folders on the legacy Exchange server (downtime required). Make sure public folder access is locked on the legacy Exchange server and then try to complete the batch again.


You can speed up this process by restarting the Information Store on the Exchange 2010 server (although difficult when running multiple Exchange 2010 servers).

Continue with the following cmdlet to complete the migration batch:

[PS] C:\>PFScripts> Complete-MigrationBatch PFMigration

To test the new Public Folders, you can enable it for a test mailbox using the following command:

[PS] C:\PFScripts> Set-Mailbox -Identity <testuser> -DefaultPublicFolderMailbox <PF Mailbox Identity>

In our environment, the mailbox identity is Mailbox1. You should check for the following:

  • View hierarchy
  • Check permissions.
  • Access content
  • Create and delete public folders.
  • Post content to and delete content from a public folder.

If all is well, you can finalize the migration using the following commands:

[PS] C:\> Get-Mailbox -PublicFolder | Set-Mailbox -PublicFolder -IsExcludedFromServingHierarchy $false
[PS] C:\> Set-OrganizationConfig -PublicFolderMigrationComplete:$true
[PS] C:\> Set-OrganizationConfig -PublicFoldersEnabled Local


Outlook clients will now (seamlessly) connect to the new Public Folder infrastructure on Exchange 2016. Outlook will use Autodiscover to retrieve Public Folder mailbox information, so it can take some time (again) before this is picked up correctly. You can see this when running the Test E-mail Autoconfiguration option in Outlook (2010 in this example):



When moving from Exchange 2010 to Exchange 2016 you have to move Public Folders too. Starting in Exchange 2013, Microsoft introduced the concept of Modern Public Folders. Migrating to Modern Public Folders is not that difficult, but it needs proper preparation.

In our scenario we had issues with Public Folder names (strange unsupported characters) and Public Folders that were no longer in use. These can be fixed or removed before the actual migration takes place.

Another issue we ran into was that it took a long time before all clients discovered that Public Folders had moved to a new environment. Sometimes up to 48 hours (don’t know why and how). When this happens users cannot open Public Folders and start logging tickets at the Servicedesk. The only thing I could say at that point is “be patient”.

Other than that the migration went smooth, all clients (Outlook 2010 and 2016, mailboxes in Exchange 2016 and Exchange Online) were able to access the Public Folders after the migration.

More information

Last updated: October 22, 2020

Autodiscover in an Exchange interorg migration with Quest QMM

Outlook clients get their configuration information using the Autodiscover protocol from the Exchange server where their mailbox resides and the underlying Active Directory. This works fine, until you are in an interorg migration scenario using the Quest Migration Manager (QMM) for Exchange.

When using Microsoft tools (ADMT, Prepare-MoveRequest.ps1 and New-MoveRequest) the source Mailbox in Exchange is converted to a Mail-Enabled user at the moment of migration finalization. At the same moment the Mail-Enabled User property targetAddress is stamped with the SMTP address of the Mailbox in the new forest. SMTP works fine now, and also Autodiscover will follow the SMTP domain that’s in the targetAddress property. This is true for an interorg migration on-premises, but it is also true when moving Mailboxes from Exchange on-premises to Exchange Online in a hybrid scenario.

When using Quest tooling things are a bit different. The source Mailbox is not converted to a Mail-Enabled User, but it continues to exist at a regular Mailbox. The Outlook profile on the desktop is converted using the CPUU tool using local Autodiscover.xml files so that the Outlook client no longer connects to the old Mailbox but to the new Mailbox.

This works fine for the existing client, but when a user gets a new laptop, or has to configure the Outlook profile again, Outlook will use the Autodiscover process and thus connect to the old Mailbox. Since this isn’t converted to a Mail-Enabled User, Outlook will find the (old) Mailbox, it will stop searching (and thus will not follow the targetAddress property) and return the configuration information for the old Mailbox.

To fix this, we have to export the Autodiscover information from the new Exchange organization to the old organization. I found an old blogpost written by Andread Kapteina (Senior Consultant at Microsoft) in the Google cache (since his blogs no longer exist at the Microsoft Technet Site) about this scenario in an interorg Exchange 2007 to Exchange 2010 migration, but I found that it is also valid in an interorg Exchange 2013 to Exchange 2016 migration. And it should be valid in every interorg Exchange migration from Exchange 2007 and higher.


To export the Autodiscover configuration from the new Exchange 2016 to the old Exchange 2013 organization, execute the following commands on the Exchange 2016 server:

$OldCred = Get-Credential OldForest\administrator
Export-AutodiscoverConfig -DomainController <NewForestFQDN> -TargetForestDomainController <OldForestFQDN> -TargetForestCredential $OldCred -MultipleExchangeDeployments $true

The -MultiplExchangeDeployments options should be set to $true since both forests contain an Exchange organization.

The Exchange Management Shell does not report anything back, so no need to show it here 😊

When we look in the AD Configuration container we can now see two SCP records:

  • One record can be found under CN=Services, CN=Microsoft Exchange, CN=<Organization>, CN=Administrative Groups, CN=Exchange Adminstrative Groups (FYDIBOHF23SPDLT), CN=Servers, CN=<ServerName>, CN=Protocols, CN=Autodiscover, CN=<ServerName>. This will contain the regular SCP information that Outlook needs to connect to the existing Exchange organization to retrieve its information.
  • The second record can be found under CN=Services, CN=Microsoft Exchange Autodiscover, CN=<FQDN of new Forest>. This will contain information regarding the target (i.e. new Exchange 2016) forest that the Outlook needs for migrated Mailboxes.
    This second record can be seen in the following screenshot:

CN=Microsoft Exchange Autodiscover

The keywords property of the SCP record contains the Accepted Domains of the new Exchange 2016 organization, like Exchangefun.nl, Corporate.Exchangefun.nl and target.qmm (the Quest target domain). This means when a new Accepted Domain is added to Exchange 2016, the Export-AutodiscoverConfig command needs to be run again.

The serviceBindingInformation property contain an LDAP link to the Exchange 2016 forest where Outlook clients can find information from the migrated Mailboxes.

Granting permissions

To avoid issues with Outlook clients of not migrated Mailboxes (that need to retrieve information from the old Exchange 2013 organization) we have to hide the exported SCP for these users. At the same time, we have to hide the original SCP record in Exchange 2013 for Mailboxes that have been migrated to Exchange 2016 (and where Outlook should NEVER receive old Exchange 2013 information).

To achieve this, create a Universal Security Group with a name like “Migrated_Users”, remove the Authenticated Users group from the exported SCP and grant the Migrated_Users Security Group Read permissions on this object as shown in the following screen shot:

Remove Authenticated Users

At the same time we have to grant an explicit deny Read permission to the Migrated_Users Security Group on the original SCP record as shown in the following screenshot:

Explicit Deny


Now when a Mailbox is migrated using QMM and the CPUU tool, add the user to the Migrated_Users Security Group. At this moment its Outlook client will no longer find the original (Exchange 2013) SCP record but the exported SCP record. Outlook will then connect to the target Active Directory forest with Exchange 2016 and retrieve the correct information.

Note. It took us quite some time when testing this scenario with different versions of Outlook (2010, 2013 and 2016) but the scenario explained here turned out to be working fine with these versions. But please test in your own test environment with various clients as well.

More information