Public folders are usually the last thing left on-premises once the mailboxes have moved. Microsoft's batch migration copies them to Exchange Online with a handful of scripts and a single migration batch, but it's unforgiving about preparation. Here's the sequence for Exchange Server SE, 2019 and 2016, the commands at each step, and the errors that most often stop the batch.
Errors this guide fixes
Cannot find a recipient that has mailbox GUIDNew-MigrationBatch (Exchange Online)Error: Dumpster of the Dumpster folder.Public folder migration requestRequest was quarantined because of the following error: The given key wasn't present in the dictionary.Public folder migration requestNo active public folder mailboxes were foundComplete-MigrationBatchCouldn't find the public folder mailbox.Get-PublicFolder (after lock-down)5.7.13NDR (mail to migrated public folders)5.4.1NDR (mail to migrated public folders)
Prerequisites#
- Versions: Exchange 2016 CU4 or later, or any Exchange 2019 build; SE is covered by the same procedure. If you installed a CU without preparing Active Directory, run
Setup.exe /PrepareADfirst, or the migration parameters won't exist. - Permissions: Organization Management in Exchange Online; Organization Management or Server Management on-premises.
- MRSProxy enabled on at least one server that hosts public folder mailboxes, reachable from Exchange Online. If endpoint creation fails, see fixing hybrid migration endpoint errors.
- Users first: migrate the people who use the folders before the folders themselves.
- Limits: at most 100 target public folder mailboxes during the migration, each up to 100 GB (Microsoft suggests filling them to about half). Shrink or split any single folder over 25 GB first.
- One batch only, created in Exchange Online PowerShell; the EAC can't do it. Plan downtime: Microsoft recommends allowing at least 48 hours for the final sync.
Step-by-step#
1. Download the scripts and validate the source#
Put SourceSideValidations.ps1, Export-ModernPublicFolderStatistics.ps1, ModernPublicFolderToMailboxMapGenerator.ps1, Sync-ModernMailPublicFolders.ps1 and SetMailPublicFolderExternalAddress.ps1 in one folder, such as C:\PFScripts. Run .\SourceSideValidations.ps1 on a Mailbox server and fix everything it reports: orphaned or duplicate mail-enabled folder objects and bad permissions are what slow or break migrations later.
2. Prepare on-premises#
# Accepted domain that keeps mail to public folders flowing during the DNS transition
New-AcceptedDomain -Name PublicFolderDestination_78c0b207_5ad2_4fee_8cb9_f373175b3f99 `
-DomainName contoso.mail.onmicrosoft.com -DomainType InternalRelay
# Folders with \ or / in the name may not reach their target mailbox: rename them
Get-PublicFolder -Recurse -ResultSize Unlimited |
Where-Object { $_.Name -like "*\*" -or $_.Name -like "*/*" } | Format-List Name, Identity, EntryId
# Both values should be False unless a previous attempt is being discarded
Get-OrganizationConfig | Format-List PublicFolderMailboxesLockedForNewConnections, PublicFolderMailboxesMigrationComplete
# Snapshots to compare with afterwards
Get-PublicFolder -Recurse -ResultSize Unlimited | Export-CliXML OnPrem_PFStructure.xml
Get-PublicFolderStatistics -ResultSize Unlimited | Export-CliXML OnPrem_PFStatistics.xml
Get-MailPublicFolder -ResultSize Unlimited | Export-CliXML OnPrem_MEPF.xmlIf Microsoft Entra Connect syncs your directory, clear Exchange Mail Public Folders under Configure › Customize synchronization options › Optional features.
3. Prepare Exchange Online#
Get-MigrationBatch | Where-Object { $_.MigrationType.ToString() -eq "PublicFolder" } and Get-Mailbox -PublicFolder should both return nothing. Remove old public folder batches; if public folders already exist in the cloud, find out who created them before deleting anything.
4. Generate the mapping files#
.\Export-ModernPublicFolderStatistics.ps1 stats.csv
.\ModernPublicFolderToMailboxMapGenerator.ps1 -MailboxSize 50GB -MailboxRecoverableItemSize 1GB `
-ImportFile .\stats.csv -ExportFile map.csvmap.csv lists only folder trees and large folders with their target mailbox; children follow their parent. Rename the generic Mailbox1, Mailbox2 targets if they clash with existing names.
5. Create the target mailboxes in Exchange Online#
$mappings = Import-Csv C:\PFScripts\map.csv
$primary = ($mappings | Where-Object FolderPath -eq "\").TargetMailbox
New-Mailbox -HoldForMigration:$true -PublicFolder -IsExcludedFromServingHierarchy:$false $primary
($mappings | Where-Object TargetMailbox -ne $primary).TargetMailbox | Sort-Object -Unique |
ForEach-Object { New-Mailbox -PublicFolder -IsExcludedFromServingHierarchy:$false $_ }6. Sync mail-enabled folders, then create and start the batch#
On-premises, run .\Sync-ModernMailPublicFolders.ps1 -CsvSummaryFile:sync_summary.csv and note the hierarchy GUID from (Get-OrganizationConfig).RootPublicFolderMailbox.HierarchyMailboxGuid.GUID. It must come from on-premises, not Exchange Online. Then in Exchange Online PowerShell:
$cred = Get-Credential CONTOSO\pfadmin
$bytes = [System.IO.File]::ReadAllBytes('C:\PFScripts\map.csv')
$ep = New-MigrationEndpoint -PublicFolder -Name PublicFolderEndpoint -RemoteServer mail.contoso.com -Credentials $cred
New-MigrationBatch -Name PublicFolderMigration -CSVData $bytes -SourceEndpoint $ep.Identity `
-SourcePfPrimaryMailboxGuid <GUID from on-premises> -NotificationEmails admin@contoso.com
Start-MigrationBatch PublicFolderMigrationThe batch syncs about once every 24 hours until you complete it. Wait for Synced with no failed requests.
7. Lock down on-premises (downtime starts)#
Re-run the sync script to catch new mail-enabled folders, then lock the source:
Set-OrganizationConfig -PublicFolderMailboxesLockedForNewConnections $true
Get-PublicFolder \Once AD replication has caught up, Get-PublicFolder \ fails with Couldn't find the public folder mailbox. That's the expected sign that the lock is in place. Mail to mail-enabled folders queues until the end.
8. Complete, test, unlock#
Remove any on-premises entries returned by Get-MoveRequest or Get-PublicFolderMoveRequest, then run Complete-MigrationBatch PublicFolderMigration. The batch goes Synced, Completing, Completed; staying Synced for hours is normal. Point a few test users at the new hierarchy, check folders, permissions and posting in Outlook, then open it to everyone:
Set-Mailbox -Identity testuser@contoso.com -DefaultPublicFolderMailbox Mailbox1
Set-OrganizationConfig -RemotePublicFolderMailboxes $null -PublicFoldersEnabled Local9. Finalize on-premises#
Mail sent to mail-enabled folders during the downtime is still queued. Microsoft's article includes a script that exports those queued messages as a backup; run it first, then point the folders at Exchange Online and mark the migration complete:
.\SetMailPublicFolderExternalAddress.ps1 -ExecutionSummaryFile:mepf_summary.csv
Set-OrganizationConfig -PublicFolderMailboxesMigrationComplete:$true -PublicFoldersEnabled RemoteThe errors that stop the batch#
| Error | Cause | Fix |
|---|---|---|
Cannot find a recipient that has mailbox GUID on New-MigrationBatch | Directory replication in Exchange Online hasn't caught up with the new public folder mailboxes | Wait about an hour and retry |
| Batch fails with transient errors | Hierarchy GUID taken from Exchange Online | Recreate the batch with the on-premises GUID |
PublicFolderMailboxesLockedForNewConnections parameter not found | AD not prepared after a CU | Run /PrepareAD from the installed CU |
Error: Dumpster of the Dumpster folder. | Known migration issue | Stop the batch and start it again |
Request was quarantined because of the following error: The given key wasn't present in the dictionary. | A corrupt item the job can't copy | Stop the batch, find the folder in the migration report, move it to the primary public folder mailbox with New-PublicFolderMoveRequest, remove the move request, restart |
No active public folder mailboxes were found at Complete-MigrationBatch | Multi-domain forest | Follow Microsoft's article for that error before completing |
| Jobs stalled or failing intermittently | Too many concurrent jobs | Set-MigrationEndpoint PublicFolderEndpoint -MaxConcurrentMigrations 30 -MaxConcurrentIncrementalSyncs 20 -SkipVerification |
Also check (Get-PublicFolder -GetChildren "\NON_IPM_SUBTREE\DUMPSTER_ROOT").Count: more than 10,000 immediate subfolders there can make the migration fail.
Verify#
- Run the same
Export-CliXMLsnapshots in Exchange Online and compare folder counts, item counts and mail-enabled folders with the on-premises files. Get-PublicFolderMailboxMigrationRequest | Get-PublicFolderMailboxMigrationRequestStatisticsshows every request completed.- Users see the hierarchy in Outlook, and a test message to a mail-enabled folder arrives in Exchange Online.
Tips and gotchas#
- Permissions on the root folder and
\NON_IPM_SUBTREE\EFORMS REGISTRY, Send As and Send on Behalf don't migrate. Record them first and re-add them withAdd-PublicFolderClientPermission,Add-RecipientPermissionandSet-MailPublicFolder -GrantSendOnBehalfTo. - Every public folder mailbox in Exchange Online must serve the hierarchy (
IsExcludedFromServingHierarchyFalse). - External mail to migrated folders bouncing with 5.7.13 or 5.4.1? Grant Anonymous
CreateItemsand check Directory-Based Edge Blocking. - Keep the on-premises public folder mailboxes for a few weeks: deleting them is irreversible.

