In part 1 we sized the project: about 400 active mailboxes and 100 former employees' mailboxes, 4 TB in total, on a FortiMail unit running in server mode. This part moves the active mailboxes with the IMAP migration built into Exchange Online. I'll prepare FortiMail and the tenant, create the endpoint and the CSV files, run a pilot and the waves, then cut over and finish without losing the mail that arrives on the last day. The leavers use the same mechanism with a different target, which is part 3.
How IMAP migration works here#
Exchange Online connects to FortiMail's IMAP service with each mailbox's credentials and copies the mail folders into the matching Exchange Online mailbox. After the first full copy, the batch runs an incremental sync once every 24 hours until you remove it. Users keep working on FortiMail the whole time; the real switch is the MX record change at cutover.
Microsoft documents the limits, and they shape the plan:
- Only mail folders move: no contacts, calendar items or tasks.
- At most 500,000 items per mailbox are migrated, newest first.
- Messages larger than 35 MB aren't migrated.
- Folders with a forward slash (/) in their name aren't migrated.
Microsoft also recommends disabling messaging records management (MRM) and archival policies on the target mailboxes during a migration: anything those policies move or delete is reported as missing, which hides real data loss. In practice, don't enable archive mailboxes until the last sync is done, and pause any retention that deletes or moves mail in the targets.
Prerequisites#
- Your domain added and verified in Microsoft 365, and the users created and licensed: the target mailboxes must exist before a batch can use them.
- Exchange Online PowerShell (the ExchangeOnlineManagement module) and an admin role that includes the migration permissions.
- FortiMail admin access, and a public host name for its IMAP service (for example
imap.contoso.com) with a trusted certificate that matches it. - The MX record's TTL already lowered to 3,600 seconds.
- The mapping sheet from part 1: FortiMail account, Microsoft 365 address, size and wave.
Step 1: Prepare FortiMail#
Open IMAP to Exchange Online, and only to it#
The migration service connects from Microsoft's datacenters, so FortiMail's IMAP port (993 for IMAP over SSL/TLS) must accept connections from the internet. Microsoft's IMAP migration guidance points to the Exchange Online URLs and IP address ranges list for the addresses to allow. Restrict the firewall rule to those ranges, and close it again when the project ends.
Raise the connection limits#
Microsoft's advice for IMAP migrations is to raise three limits before you start, on the IMAP server and on any firewall in front of it: total connections, connections per user and connections per IP address. Exchange Online opens many IMAP sessions in parallel, and a firewall that hasn't been told to expect them can treat that as an attack.
Decide how the migration signs in#
The CSV file must contain credentials that open each FortiMail mailbox. Microsoft supports two approaches: the user's own credentials, or an administrator (super-user) account where the IMAP server supports one. Microsoft's documented administrator formats cover servers such as Exchange, Dovecot and Mirapoint, and FortiMail isn't among them, so unless Fortinet documents delegated IMAP access for your firmware, plan for per-user credentials:
- Local FortiMail passwords. Just before a wave starts, set each user in it to a known migration password, tell them, and stop them changing it until cutover. Microsoft warns that if users change their password after the first sync, the following syncs fail.
- Passwords checked against Active Directory. The migration password is then the user's directory password, and also their Microsoft 365 password if you synchronize password hashes. Schedule the reset with each user, or consider a third-party tool with a different access method.
- Former employees. Nobody uses these mailboxes, so a reset costs nothing (part 3).
Treat the CSV files as secrets: keep them encrypted, never email them, and delete them when the wave is finished.
Rescue what IMAP won't move#
- Address book. Export the global and domain address books from Domain & User › Address Book (CSV or vCard) and recreate the entries you still need as mail contacts in Exchange Online.
- Personal contacts and calendars. If users keep them in FortiMail webmail, ask them to export them before cutover with the export your webmail version offers, and import them into Outlook afterwards.
- Quarantine. Release anything users still need from their FortiMail quarantine before cutover. Don't plan on migrating quarantined mail.
- Backup. Take a FortiMail mailbox backup; Fortinet supports NFS, SMB, iSCSI, SSH and USB targets. It's a restore point for FortiMail, not an import format for Exchange, but it's your safety net until the project closes.
Step 2: Create the endpoint in Exchange Online#
Connect-ExchangeOnline
# Can Exchange Online reach FortiMail's IMAP service?
Test-MigrationServerAvailability -IMAP -RemoteServer imap.contoso.com -Port 993 -Security Ssl
# Create the endpoint with modest concurrency to start with
New-MigrationEndpoint -IMAP -Name "FortiMail-IMAP" -RemoteServer imap.contoso.com -Port 993 -Security Ssl `
-MaxConcurrentMigrations 20 -MaxConcurrentIncrementalSyncs 10
Get-MigrationEndpoint "FortiMail-IMAP" | Format-List EndpointType, RemoteServer, Port, Security, Max*Then keep retention away from the targets during the copy. Retention hold pauses the Exchange (MRM) retention policy on a mailbox. If you also use Microsoft Purview retention policies that delete mail after a number of years, exclude the migration targets or delay those policies until the migration is over, or years-old mail can be deleted soon after it arrives.
Import-Csv "C:\Migration\wave01.csv" | ForEach-Object {
Set-Mailbox -Identity $_.EmailAddress -RetentionHoldEnabled $true
}Step 3: Build the CSV files and the waves#
Each CSV needs three columns: EmailAddress (the Exchange Online mailbox), UserName (the name FortiMail expects at IMAP sign-in; check it with an IMAP client first, it's often the full address) and Password. A file can hold up to 50,000 rows, but smaller batches are easier to watch and to retry.
EmailAddress,UserName,Password
amal.hassan@contoso.com,amal.hassan@contoso.com,MIGRATION-PASSWORD-1
karim.osman@contoso.com,karim.osman@contoso.com,MIGRATION-PASSWORD-2A wave plan that works well for 400 mailboxes:
- Pilot: five to ten mailboxes, including IT staff, one heavy user and one of the largest mailboxes.
- Waves of 40 to 60 mailboxes: largest first, so the long copies start early; executives once the process is proven.
- Leavers: separate batches (part 3).
Look at the folder names FortiMail exposes over IMAP before you exclude anything. Junk and trash folders are the usual candidates for -ExcludeFolders; the names in the example below are placeholders for whatever your FortiMail shows.
Step 4: Run the pilot, then the waves#
New-MigrationBatch -Name "Wave00-Pilot" -SourceEndpoint "FortiMail-IMAP" `
-CSVData ([System.IO.File]::ReadAllBytes("C:\Migration\wave00.csv")) `
-ExcludeFolders "Junk","Trash" -NotificationEmails "migration-team@contoso.com" -AutoStart
# Batch and per-mailbox progress
Get-MigrationBatch | Format-Table Identity, Status, TotalCount
Get-MigrationUser -BatchId "Wave00-Pilot" | Format-Table Identity, Status, SyncedItemCount, SkippedItemCount
# Errors and skipped items for one mailbox
Get-MigrationUserStatistics -Identity amal.hassan@contoso.com -IncludeSkippedItems -IncludeReport |
Format-List Status, Error, SkippedItemsA mailbox shows Synced when its first full copy is done; from then on it gets an incremental sync every 24 hours. Use the pilot to measure throughput: check each pilot mailbox's size in Exchange Online at intervals with Get-EXOMailboxStatistics -Identity amal.hassan@contoso.com | Format-List TotalItemSize, add up the gigabytes copied across the batch per hour, and put that figure into the estimate from part 1.
Raise concurrency in steps while you watch FortiMail's CPU, disks and IMAP sessions, and the experience of the users still working on it. Microsoft's guidance is to start small and increase while you monitor the source; an endpoint allows up to 100:
Set-MigrationEndpoint -Identity "FortiMail-IMAP" -MaxConcurrentMigrations 40For errors at the endpoint, in the CSV or for individual users, see my quick reference for Exchange Online migration batch errors.
Step 5: Cut over#
Cut over when every batch is Synced and has completed a recent incremental sync. Then, in this order:
- Switch the MX record to Exchange Online, as described in part 4. Leave FortiMail running and accepting mail: senders with the old MX record cached keep delivering to it for a while, and the incremental syncs copy those messages across.
- Point Autodiscover at Exchange Online with a CNAME record from
autodiscover.contoso.comtoautodiscover.outlook.com. - Move the clients. Users sign in to Outlook with their Microsoft 365 account (a new profile in classic Outlook, or the new Outlook), install Outlook on their phones and remove the old IMAP or POP accounts, so nobody keeps filing mail into FortiMail.
- Keep syncing. Microsoft recommends waiting at least 72 hours after the MX change before you delete a batch, and checking that the batch synced at least once after mail started arriving in Exchange Online: its last synced time, under Migration in the Exchange admin center, must be later than the switch.
- Remove the batches and lift the retention hold.
Remove-MigrationBatch -Identity "Wave00-Pilot"
Get-MigrationBatch -Identity "Wave00-Pilot" # shows Removing, then an error once it's gone
Import-Csv "C:\Migration\wave01.csv" | ForEach-Object {
Set-Mailbox -Identity $_.EmailAddress -RetentionHoldEnabled $false
}Verify the migration#
Compare a sample of mailboxes with FortiMail, folder by folder:
Get-EXOMailboxFolderStatistics -Identity amal.hassan@contoso.com |
Format-Table FolderPath, ItemsInFolder, FolderSize -AutoSize- Folder counts match the counts an IMAP client shows on FortiMail, allowing for excluded folders and skipped items.
- Every skipped item in the batch reports is explained (usually oversize or damaged messages).
- New internet mail arrives in Exchange Online: message trace shows it (part 4 has the commands).
- Users can open old mail, find their sent items and search across the migrated folders.
Tips and gotchas#
- Don't change addresses mid-migration. Microsoft warns not to delete target mailboxes or change their SMTP addresses until the batch is complete and deleted.
- Rename folders that contain a slash on FortiMail before the batch, or their mail stays behind.
- Deal with oversize mail separately. Microsoft suggests asking users to save very large messages and attachments before the move; OneDrive is a better home than a USB stick.
- Very large or very full mailboxes. Over 100 GB or 500,000 items, IMAP alone leaves mail behind; use Large Mailbox Onboarding or a PST import for the rest (part 5).
- POP3 users have little on the server. Import their PST files instead (part 5).
- Leave FortiMail alone until the rollback window and the archive decision are closed. Keep it powered, backed up and in server mode: Fortinet warns that changing its operation mode resets most of the configuration.
References#
- Use PowerShell to perform an IMAP migration to Microsoft 365 (Microsoft Learn)
- Tips for optimizing IMAP migrations in Exchange Online (Microsoft Learn)
- Choosing the operation mode (FortiMail 7.6.5 Administration Guide, Fortinet)

