Microsoft 365

Migrating public folders to Exchange Online with batch migration

Move modern public folders from Exchange Server 2016, 2019 or Subscription Edition to Exchange Online with the native batch migration: scripts, mapping CSV, lock-down, completion and verification.

12 min read
On this page

To migrate modern public folders from Exchange Server to Exchange Online, you generate a folder-to-mailbox mapping CSV with Microsoft's scripts, create matching public folder mailboxes in Exchange Online, and run a single public folder migration batch with New-MigrationBatch and Start-MigrationBatch. When the batch is Synced, you lock the on-premises folders, run Complete-MigrationBatch, test with a few users, and then switch the organization to Exchange Online public folders with Set-OrganizationConfig. Plan for at least 48 hours of public folder downtime during the final sync.

Who this is for and what you will have at the end

This guide is for Exchange administrators in a hybrid organization whose public folders live on Exchange Server 2016, Exchange Server 2019 or Exchange Server Subscription Edition, and who have already moved, or are about to finish moving, the user mailboxes. Microsoft's guidance is to complete the user migration before migrating public folders, and users who need public folder access should be migrated first.

By the end you will have:

  • A validated source hierarchy and snapshots to compare against.
  • Public folder mailboxes in Exchange Online sized from your own statistics.
  • A completed public folder migration batch, tested and unlocked.
  • Mail-enabled public folders routing correctly, and a rollback plan you didn't need.

Legacy public folders on Exchange Server 2010 follow a different procedure and aren't covered here.

Limits and requirements to check first

ItemRequirement or limit
Source versionExchange Server 2016 CU4 or later, any Exchange Server 2019 version, or Exchange Server Subscription Edition. (The procedure also lists Exchange Server 2013 CU15 or later.) Microsoft recommends installing the latest CU and SU first.
Active DirectoryAfter the CU upgrade, prepare Active Directory, or the migration fails and parameters such as PublicFolderMailboxesLockedForNewConnections are missing.
Target mailboxesUp to 100 public folder mailboxes for migration (up to 1,000 can be created afterwards), each up to 100 GB.
Total size5 TB is the maximum recommended, based on filling each target mailbox to 50 percent.
Single folderTrim or split any single public folder larger than 25 GB (child folders don't count toward it).
BatchesExactly one public folder migration batch.
DumpsterMore than 10,000 immediate subfolders under \NON_IPM_SUBTREE\DUMPSTER_ROOT can cause failure.
ToolsExchange Management Shell on-premises and Exchange Online PowerShell. The EAC can't run the migration.
PermissionsOrganization Management in Exchange Online; Organization Management or Server Management on-premises.
MRS ProxyEnabled on at least one Exchange server that also hosts public folder mailboxes.

Two settings can delete content before you even start: check Get-OrganizationConfig | Format-List DefaultPublicFolderAgeLimit and the AgeLimit on individual folders, so automatic deletions don't remove content you expect to migrate.

Microsoft recommends against using Outlook's PST export to migrate public folders, and specifically when the on-premises hierarchy is larger than 30 GB. Public folder mailboxes in Exchange Online grow through auto-split, which can't keep up with a sudden bulk import, and you can wait up to two weeks for it to move data out of a full primary mailbox. Permissions are also lost with PST export.

Step 1: Download the scripts and validate the source

Download the scripts from Exchange Server Public Folders Migration Scripts (https://aka.ms/PublicFolderScripts) and the pre-migration scripts (https://aka.ms/ssv2) into one folder, for example C:\PFScripts:

ScriptPurpose
SourceSideValidations.ps1Scans the source public folders and reports issues known to slow or break migration, with fixes.
Sync-ModernMailPublicFolders.ps1Synchronizes mail-enabled public folder objects to Exchange Online.
Export-ModernPublicFolderStatistics.ps1Creates the folder name to size and deleted item size file.
ModernPublicFolderToMailboxMapGenerator.ps1Creates the folder-to-mailbox mapping file.
SetMailPublicFolderExternalAddress.ps1Points on-premises mail-enabled public folders at their Exchange Online counterparts after migration.

Run SourceSideValidations.ps1 on an on-premises Mailbox server first and fix what it reports. It covers the general prerequisites: no orphaned public folder mail objects in Active Directory, matching SMTP addresses between Active Directory and Exchange, and no duplicate public folder objects.

Step 2: Prepare on-premises

In the Exchange Management Shell:

  1. Create the well-known accepted domain that keeps mail flowing to mail-enabled public folders while DNS caches catch up after the migration. Use your Exchange Online routing domain, which the Hybrid Configuration Wizard has already configured a send connector for:
New-AcceptedDomain -Name PublicFolderDestination_78c0b207_5ad2_4fee_8cb9_f373175b3f99 -DomainName "contoso.mail.onmicrosoft.com" -DomainType InternalRelay

If the domain already exists on-premises, rename it to that name instead of creating a second one.

  1. Find and rename folders whose names contain a backslash or forward slash, because they may not land in their designated mailbox:
Get-PublicFolder -Recurse -ResultSize Unlimited |
    Where {$_.Name -like "*\*" -or $_.Name -like "*/*"} |
    Format-List Name, Identity, EntryId
 
Set-PublicFolder -Identity "<public folder EntryId>" -Name "<new public folder name>"
  1. If this is a repeat attempt, check for flags from a previous migration and reset them only after you confirm that attempt can be discarded:
Get-OrganizationConfig | Format-List PublicFolderMailboxesLockedForNewConnections, PublicFolderMailboxesMigrationComplete

If either value is True and the earlier attempt can be discarded, reset both:

Set-OrganizationConfig -PublicFolderMailboxesLockedForNewConnections:$false -PublicFolderMailboxesMigrationComplete:$false
  1. Take snapshots for comparison after the migration:
Get-PublicFolder -Recurse -ResultSize Unlimited | Export-CliXML OnPrem_PFStructure.xml
Get-PublicFolderStatistics -ResultSize Unlimited | Export-CliXML OnPrem_PFStatistics.xml
Get-PublicFolder -Recurse -ResultSize Unlimited | Get-PublicFolderClientPermission |
    Select-Object Identity,User,AccessRights -ExpandProperty AccessRights | Export-CliXML OnPrem_PFPerms.xml
Get-MailPublicFolder -ResultSize Unlimited | Export-CliXML OnPrem_MEPF.xml
  1. If you use Microsoft Entra Connect, open it, select Configure > Customize synchronization options, and on Optional Features make sure Exchange Mail Public Folders is cleared. Clearing it removes the synchronized mail-enabled public folder objects from Entra ID; if more than 500 are removed you may need to allow the deletion past the accidental-delete protection.

  2. Record who has Send As and Send on Behalf on mail-enabled public folders, because those permissions aren't migrated:

Get-MailPublicFolder | Get-ADPermission | ?{$_.ExtendedRights -like "*Send-As*"}
Get-MailPublicFolder | ?{$_.GrantSendOnBehalfTo -ne "$null"} | Format-Table Name, GrantSendOnBehalfTo

Step 3: Prepare Exchange Online

In Exchange Online PowerShell, confirm there is no earlier public folder batch and no existing public folder hierarchy:

Get-MigrationBatch | ?{$_.MigrationType.ToString() -eq "PublicFolder"}
Get-Mailbox -PublicFolder
Get-PublicFolder -Recurse

If the first command returns a failed or abandoned public folder batch, remove it, or your new batch will fail:

Remove-MigrationBatch <name of migration batch> -Confirm:$false

If public folders already exist in Exchange Online, find out who created them and why before removing anything; the removal commands in Microsoft's procedure permanently delete the content. Also confirm the public folder quotas in Exchange Online aren't below 25 GB. You can raise them with Set-OrganizationConfig and the DefaultPublicFolderIssueWarningQuota and DefaultPublicFolderProhibitPostQuota parameters.

Step 4: Generate the mapping files

On-premises, export the folder statistics. The output has three columns, FolderName, FolderSize and DeletedItemSize, in bytes:

.\Export-ModernPublicFolderStatistics.ps1 stats.csv

Then generate the folder-to-mailbox map:

.\ModernPublicFolderToMailboxMapGenerator.ps1 -MailboxSize 50GB -MailboxRecoverableItemSize 1GB -ImportFile .\stats.csv -ExportFile map.csv

MailboxSize is the most you want to place in any one target mailbox. The maximum is 100 GB, and Microsoft recommends about half of that to leave room for growth. The recoverable items size is the dumpster quota for the target mailboxes; Microsoft recommends 15 GB or less.

The map isn't a list of every folder. It names the roots of large subtrees and individually large folders; children follow their parent into the same target mailbox unless a separate line sends them elsewhere. The script uses generic target names such as Mailbox1 and Mailbox2. Rename them in Notepad to fit your naming policy, and make sure none collide with existing mailbox names. Keep the total at 100 unique target mailboxes or fewer.

Step 5: Create the target public folder mailboxes

Copy map.csv to the computer where you run Exchange Online PowerShell (the examples use C:\PFScripts). Then create the primary hierarchy mailbox on hold for migration, followed by the rest, all serving the hierarchy:

$mappings = Import-Csv C:\PFScripts\map.csv
$primaryMailboxName = ($mappings | Where-Object FolderPath -eq "\").TargetMailbox
New-Mailbox -HoldForMigration:$true -PublicFolder -IsExcludedFromServingHierarchy:$false $primaryMailboxName
($mappings | Where-Object TargetMailbox -ne $primaryMailboxName).TargetMailbox | Sort-Object -Unique |
    ForEach-Object { New-Mailbox -PublicFolder -IsExcludedFromServingHierarchy:$false $_ }

Leaving any mailbox with IsExcludedFromServingHierarchy set to $true is a known cause of failed public folder migrations.

Step 6: Create and start the migration batch

  1. On-premises, from a server hosting public folder mailboxes, sync the mail-enabled public folders to Exchange Online:
.\Sync-ModernMailPublicFolders.ps1 -CsvSummaryFile:sync_summary.csv
  1. On-premises, get the GUID of the primary hierarchy mailbox. It must come from on-premises; a GUID taken from Exchange Online makes the batch fail with a transient error:
(Get-OrganizationConfig).RootPublicFolderMailbox.HierarchyMailboxGuid.GUID
  1. In Exchange Online PowerShell, create the endpoint and the batch, then start it:
$Source_Credential = Get-Credential contoso\pfadmin
$Source_RemoteServer = "mail.contoso.com"
$bytes = [System.IO.File]::ReadAllBytes('C:\PFScripts\map.csv')
$PfEndpoint = New-MigrationEndpoint -PublicFolder -Name PublicFolderEndpoint -RemoteServer $Source_RemoteServer -Credentials $Source_Credential
New-MigrationBatch -Name PublicFolderMigration -CSVData $bytes -SourceEndpoint $PfEndpoint.Identity -SourcePfPrimaryMailboxGuid <GUID from step 2> -NotificationEmails admin@contoso.com
Start-MigrationBatch PublicFolderMigration

$Source_RemoteServer is the internet-routable FQDN of your MRS Proxy endpoint. If New-MigrationBatch fails with "Cannot find a recipient that has mailbox GUID" for a public folder mailbox in Exchange Online, Active Directory replication hasn't caught up; wait an hour and retry.

Monitor progress with:

Get-MigrationBatch PublicFolderMigration
Get-PublicFolderMailboxMigrationRequest | Get-PublicFolderMailboxMigrationRequestStatistics

When the batch reaches Synced with no errors, run the four on-premises snapshot commands from Step 2 again so your comparison files reflect the current state.

Public folder migrations use the Data Consistency Score. A grade of Investigate requires you to approve skipped items before completion; the TooManyBadItemsPermanentException guide explains how to review and approve them. After the initial copy, the batch syncs from the source once every 24 hours.

Step 7: Lock the on-premises public folders (downtime starts)

Before you lock anything, confirm recent syncs. LastSyncedDate on the batch and LastSuccessfulSyncTimestamp on each request should be within the last seven days:

Get-MigrationBatch | ?{$_.MigrationType -like "*PublicFolder*"} | Format-Table *last*sync*
Get-PublicFolderMailboxMigrationRequest | Get-PublicFolderMailboxMigrationRequestStatistics |
    Format-Table TargetMailbox, *last*sync*

Rerun Sync-ModernMailPublicFolders.ps1 to pick up new mail-enabled folders, then lock on-premises:

Set-OrganizationConfig -PublicFolderMailboxesLockedForNewConnections $true

Users are logged off public folders, and mail to mail-enabled public folders queues until the migration finishes. With public folder mailboxes on several servers, wait for Active Directory replication. When Get-PublicFolder \ on-premises returns Couldn't find the public folder mailbox, the folders are locked.

Step 8: Complete the batch

On-premises, make sure no other public folder or public folder mailbox moves exist (Get-MoveRequest, Get-PublicFolderMoveRequest) and remove any that are in progress or Completed. Then, in Exchange Online PowerShell:

Complete-MigrationBatch PublicFolderMigration

The status goes from Synced to Completing to Completed. It commonly stays on Synced for a few hours, and with many target mailboxes more than 24 hours is normal as long as no request has failed or been quarantined. After completion, nothing more can be synchronized from on-premises.

Step 9: Test, unlock and finalize

  1. Point a few test users at a migrated public folder mailbox and test in Outlook: view the hierarchy, check permissions, create and delete folders, post and delete items. Changes can take 15 to 30 minutes to appear.
Set-Mailbox -Identity testuser@contoso.com -DefaultPublicFolderMailbox Mailbox1
  1. Unlock public folders in Exchange Online:
Set-OrganizationConfig -RemotePublicFolderMailboxes $Null -PublicFoldersEnabled Local
  1. On-premises, back up queued messages addressed to mail-enabled public folders using the export command in Microsoft's procedure, then stamp the external addresses and mark the migration complete:
.\SetMailPublicFolderExternalAddress.ps1 -ExecutionSummaryFile:mepf_summary.csv
Set-OrganizationConfig -PublicFolderMailboxesMigrationComplete:$true -PublicFoldersEnabled Remote
  1. Re-apply what isn't migrated. Permissions on the root folder and on \NON_IPM_SUBTREE\EFORMS REGISTRY need Add-PublicFolderClientPermission. Send As needs Add-RecipientPermission -AccessRights SendAs, and Send on Behalf needs Set-MailPublicFolder -GrantSendOnBehalfTo.

Verification

Take the same four snapshots in Exchange Online and compare them with the on-premises files:

Get-PublicFolder -Recurse -ResultSize Unlimited | Export-CliXML Cloud_PFStructure.xml
Get-PublicFolder -Recurse -ResultSize Unlimited | Get-PublicFolderStatistics | Export-CliXML Cloud_PFStatistics.xml
Get-PublicFolder -Recurse -ResultSize Unlimited | Get-PublicFolderClientPermission |
    Select-Object Identity,User,AccessRights | Export-CliXML Cloud_PFPerms.xml
Get-MailPublicFolder -ResultSize Unlimited | Export-CliXML Cloud_MEPF.xml

Keep the on-premises public folder mailboxes for a few weeks while you monitor. Removing them is irreversible.

Troubleshooting

SymptomCauseFix
Error creating a second public folder batchOnly one public folder batch is allowedRemove the old batch with Remove-MigrationBatch first.
Jobs stalled or failing intermittentlyToo many jobs in parallelSet-MigrationEndpoint PublicFolderEndpoint -MaxConcurrentMigrations 30 -MaxConcurrentIncrementalSyncs 20 -SkipVerification
"Dumpster of the Dumpster folder"Known issueStop the batch and restart it.
Request quarantined: "The given key wasn't present in the dictionary"Corrupt item in a folderStop the batch, move the affected folder to the primary public folder mailbox on-premises with New-PublicFolderMoveRequest, remove the move request, restart the batch.
PublicFolderMailboxesLockedForNewConnections not recognizedActive Directory not prepared after the CU upgradePrepare Active Directory and domains.
External mail to a mail-enabled public folder fails with 5.7.13 or 5.4.1Anonymous can't create items, or Directory-Based Edge Blocking rejects itGrant anonymous CreateItems and disable DBEB for that domain.
Final sync very slowLarge changes, or many corrupt ACLs not cleaned upPlan for at least 48 hours; fix the issues SourceSideValidations.ps1 reports before you start.

Rolling back

If testing fails before you go live, unlock on-premises with Set-OrganizationConfig -PublicFolderMailboxesLockedForNewConnections:$false -PublicFolderMailboxesMigrationComplete:$false -PublicFoldersEnabled Local (the unlock can take several hours), revert the ExternalEmailAddress values using OnPrem_MEPF.xml or the script summary, remove the Exchange Online public folders and mailboxes, and run Set-OrganizationConfig -PublicFoldersEnabled Remote in Exchange Online. Anything added in Exchange Online after the migration is lost unless you export it first.

Checklist

  • Users migrated first; source on a supported CU with Active Directory prepared.
  • Validation script clean; no folder over 25 GB; no slashes in names; age limits checked.
  • Snapshots and Send As / Send on Behalf lists saved.
  • Map generated at about 50 percent mailbox fill, 100 targets or fewer.
  • One batch created with the on-premises hierarchy GUID; skipped items reviewed.
  • Recent syncs confirmed, folders locked, batch completed.
  • Tested with a few users, unlocked, external addresses stamped, migration marked complete.
  • Snapshots compared; missing permissions re-applied.

References

Questions people ask

How much public folder data can the native migration move to Exchange Online?

The native method supports up to 100 target public folder mailboxes, each with a maximum capacity of 100 GB. Microsoft recommends filling each target mailbox to about 50 percent, which makes 5 TB the maximum recommended size to migrate. Any single public folder larger than 25 GB should be trimmed or split first.

How long are public folders unavailable during the migration?

Users keep access to on-premises public folders while the batch copies and syncs. Downtime starts when you lock the on-premises folders for the final sync, and Microsoft recommends planning for at least 48 hours, because the final sync can take a long time on large hierarchies or hierarchies with many corrupt ACLs.

Can I run more than one public folder migration batch?

No. All public folder data must go through a single migration batch; creating a second public folder batch at the same time returns an error. After the batch reaches Completed, no more data can be copied from the source.

Can I move public folders back from Exchange Online to Exchange Server?

There are no native tools to migrate public folders from Exchange Online to Exchange Server. Before the migration is finalized you can roll back to the on-premises folders, but content added in Exchange Online after the migration is lost unless you export it first.

Exchange OnlinePublic foldersExchange ServerMigration batches
  1. Exchange Online migration batch stuck on Syncing: causes and fixes

    Find out why an Exchange Online migration batch sits on Syncing, read the per-user stall reasons in PowerShell, and apply the right fix for concurrency, throttling, source health or skipped items.

    Microsoft 36513 min read
  2. IMAP to Exchange Online migration from cPanel and other hosts

    Move mailboxes from cPanel or any IMAP host to Exchange Online with migration batches: CSV prep, endpoint, PowerShell, MX cutover and the cPanel email routing setting that catches people out.

    Microsoft 36513 min read
  3. Calendar permissions in Exchange Online: Add-MailboxFolderPermission guide

    Share calendars, change the organization-wide Default permission and add calendar delegates in Exchange Online with Add-, Set- and Remove-MailboxFolderPermission, including localized folder names.

    Microsoft 3659 min read