EXO Migration Without a Remote Move
How to provision on-premises Exchange mailboxes in a controlled manner as new, empty Exchange Online mailboxes: PST backup, CSV approval, RemoteMailbox, synchronization, validation, and rollback.
A hybrid remote move is the standard way to move an on-premises Exchange mailbox, including its contents, to Exchange Online. Not every organization permits this migration path. If a security policy excludes remote moves, a deliberately different approach may be justifiable: The on-premises mailbox is backed up as a PST, separated from the synchronized AD user, and a new, empty mailbox is provisioned in Exchange Online for the same user.
This approach is not a mailbox migration. It transfers neither messages nor calendars, rules, or permissions to the cloud. The PST serves solely as a backup and is not imported in this scenario. This process is therefore suitable only if an empty target mailbox is acceptable from a business perspective and the loss of the active mailbox configuration has been explicitly approved.
Target State and Strict Prerequisites
After cutover, the same AD user remains in place. On-premises, however, it is no longer managed as UserMailbox, SharedMailbox, RoomMailbox or EquipmentMailbox, but as RemoteMailbox. After synchronization, this object represents the new mailbox in Exchange Online.
The desired state is as follows:
- The on-premises mailbox has been fully backed up as a PST.
- The on-premises mailbox is disconnected but has not yet been permanently deleted within the configured retention period.
- The existing AD user is enabled as a RemoteMailbox.
- The primary address, aliases, and the old
LegacyExchangeDNare retained. - Entra Connect has synchronized the changes.
- An Exchange Online service plan is assigned for user mailboxes.
- Exchange Online shows an actual cloud mailbox, and mail flow ends there.
Before starting, the following must also be clarified:
- The PST share is accessible via UNC. The group
Exchange Trusted Subsystemhas read and write permissions there. - The executing account has the
Mailbox Import Exportmanagement role. - The PST is only the agreed backup; no later import is planned.
- Retention, Litigation Hold, eDiscovery, and regulatory requirements have been reviewed separately.
- Delegations, Send As, Send on Behalf, forwarding, inbox rules, mobile devices, and application access have been inventoried.
- During export and cutover, incoming messages are held in a controlled manner at the upstream gateway. Users and applications must no longer write to the source mailbox.
- The retention period of the on-premises mailbox database covers the rollback window.
Why a CSV Approval List Is Essential
A direct pipeline such as Get-Mailbox | Disable-Mailbox is too risky for this process. It could also include system, discovery, or otherwise unapproved mailboxes. The following process therefore uses two explicit approvals:
Action=CUTOVERdetermines which row may actually be switched.PstVerified=YESconfirms that the export file has been reviewed technically and organizationally.
First, only the inventory is generated:
$CsvPath = "C:\Migration\mailboxes.csv"
$RemoteRoutingDomain = "contoso.mail.onmicrosoft.com"
Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails `
UserMailbox,SharedMailbox,RoomMailbox,EquipmentMailbox |
Sort-Object PrimarySmtpAddress |
ForEach-Object {
[pscustomobject]@{
Identity = $_.Identity
DisplayName = $_.DisplayName
PrimarySmtpAddress = $_.PrimarySmtpAddress.ToString()
Alias = $_.Alias
SourceType = $_.RecipientTypeDetails.ToString()
ArchiveStatus = $_.ArchiveStatus.ToString()
ServerName = $_.ServerName
Database = $_.Database.ToString()
RemoteRoutingAddress = "$($_.Alias)@$RemoteRoutingDomain"
Action = "REVIEW"
PstVerified = "NO"
}
} |
Export-Csv -Path $CsvPath -NoTypeInformation -Encoding UTF8
The file is then cleaned up from a business perspective. Only mailboxes that have actually been approved receive Action=CUTOVER. System mailboxes and special objects do not belong on this list.
Phase 1: Back Up the Primary Mailbox and Archive as PST Files
New-MailboxExportRequest writes only to a UNC path. A unique file name is generated for each mailbox. An active Exchange on-premises online archive is exported separately:
$CsvPath = "C:\Migration\mailboxes.csv"
$PstShare = "\\fileserver\exchange-pst$"
$BatchName = "EXO-NewMailbox-20260807"
$targets = Import-Csv $CsvPath | Where-Object Action -eq "CUTOVER"
foreach ($row in $targets) {
$safeName = ($row.PrimarySmtpAddress -replace '[^a-zA-Z0-9@._-]', '_')
$primaryPath = "$PstShare\$safeName.pst"
New-MailboxExportRequest `
-Mailbox $row.Identity `
-FilePath $primaryPath `
-Name "Primary-$safeName" `
-BatchName $BatchName
if ($row.ArchiveStatus -eq "Active") {
New-MailboxExportRequest `
-Mailbox $row.Identity `
-IsArchive `
-FilePath "$PstShare\$safeName-archive.pst" `
-Name "Archive-$safeName" `
-BatchName $BatchName
}
}
The export is not approved until every request has the status Completed:
Get-MailboxExportRequest -BatchName $BatchName |
Get-MailboxExportRequestStatistics -IncludeReport |
Format-Table DisplayName,Status,PercentComplete,FailureCode,Message -AutoSize
Get-MailboxExportRequest -BatchName $BatchName |
Where-Object Status -ne "Completed"
In addition, the files’ existence, size, readability, backup transfer, and access protection must be verified. Only then is PstVerified=YES set for the corresponding CSV row.
Phase 2: Back Up Mailbox Data and Exchange Attributes
Before the first change, a machine-readable snapshot is created for each mailbox. It is more important than a screenshot because aliases, GUIDs, and the LegacyExchangeDN can later be reconstructed exactly:
$SnapshotPath = "C:\Migration\Snapshots"
New-Item -ItemType Directory -Path $SnapshotPath -Force | Out-Null
Import-Csv "C:\Migration\mailboxes.csv" |
Where-Object { $_.Action -eq "CUTOVER" -and $_.PstVerified -eq "YES" } |
ForEach-Object {
$mailbox = Get-Mailbox -Identity $_.Identity
$safeName = ($_.PrimarySmtpAddress -replace '[^a-zA-Z0-9@._-]', '_')
$mailbox |
Select-Object Identity,DistinguishedName,ExchangeGuid,ArchiveGuid,
RecipientTypeDetails,PrimarySmtpAddress,EmailAddresses,
LegacyExchangeDN,Alias,Database,ServerName |
Export-Clixml "$SnapshotPath\$safeName.xml"
}
Delegations and forwarding require separate exports. At a minimum, this information should be backed up separately:
$mailbox = Get-Mailbox -Identity user01@contoso.com
$mailbox | Format-List ForwardingAddress,ForwardingSmtpAddress,DeliverToMailboxAndForward
Get-MailboxPermission -Identity $mailbox.Identity
Get-ADPermission -Identity $mailbox.DistinguishedName
Get-InboxRule -Mailbox $mailbox.Identity
Get-CalendarProcessing -Identity $mailbox.Identity -ErrorAction SilentlyContinue
These configurations are not automatically transferred to the new cloud mailbox.
Phase 3: Disconnect the On-Premises Mailbox and Enable RemoteMailbox
The actual cutover is brief but consequential. Disable-Mailbox removes the Exchange attributes from the AD user and disconnects the on-premises mailbox. The mailbox data remains as a disconnected mailbox until database retention expires. Immediately afterward, Enable-RemoteMailbox enables the same AD user for Exchange Online.
The following script processes only doubly approved rows. It preserves the primary SMTP address, all existing proxy addresses, and the old LegacyExchangeDN as an X500 address. The X500 entry prevents NDRs when replying to older messages or using old Outlook autocomplete entries.
$CsvPath = "C:\Migration\mailboxes.csv"
$PstShare = "\\fileserver\exchange-pst$"
$targets = Import-Csv $CsvPath |
Where-Object { $_.Action -eq "CUTOVER" -and $_.PstVerified -eq "YES" }
foreach ($row in $targets) {
$safeName = ($row.PrimarySmtpAddress -replace '[^a-zA-Z0-9@._-]', '_')
$pstPath = "$PstShare\$safeName.pst"
if (-not (Test-Path $pstPath)) {
throw "PST fehlt: $pstPath"
}
if ((Get-Item $pstPath).Length -eq 0) {
throw "PST ist leer: $pstPath"
}
$mailbox = Get-Mailbox -Identity $row.Identity -ErrorAction Stop
$primary = $mailbox.PrimarySmtpAddress.ToString()
$legacyDn = $mailbox.LegacyExchangeDN
if ($primary -ine $row.PrimarySmtpAddress) {
throw "Primaere SMTP-Adresse stimmt nicht mit der Freigabeliste ueberein: $($row.Identity)"
}
$preservedAddresses = @($mailbox.EmailAddresses | ForEach-Object ToString)
Disable-Mailbox -Identity $row.Identity -Confirm:$false
$enableParams = @{
Identity = $row.Identity
Alias = $row.Alias
PrimarySmtpAddress = $primary
RemoteRoutingAddress = $row.RemoteRoutingAddress
ACLableSyncedObjectEnabled = $true
}
switch ($row.SourceType) {
"SharedMailbox" { $enableParams.Shared = $true }
"RoomMailbox" { $enableParams.Room = $true }
"EquipmentMailbox" { $enableParams.Equipment = $true }
"UserMailbox" { }
default { throw "Nicht unterstuetzter Mailbox-Typ: $($row.SourceType)" }
}
Enable-RemoteMailbox @enableParams
$orderedAddresses = @(
"SMTP:$primary"
$preservedAddresses |
Where-Object { $_ -notmatch '^SMTP:' -or $_.Substring(5) -ine $primary }
"smtp:$($row.RemoteRoutingAddress)"
"X500:$legacyDn"
)
$seen = [System.Collections.Generic.HashSet[string]]::new(
[System.StringComparer]::OrdinalIgnoreCase
)
$finalAddresses = foreach ($address in $orderedAddresses) {
if ($seen.Add($address)) { $address }
}
Set-RemoteMailbox -Identity $row.Identity -EmailAddressPolicyEnabled $false
Set-RemoteMailbox -Identity $row.Identity -EmailAddresses $finalAddresses
Set-RemoteMailbox -Identity $row.Identity -PrimarySmtpAddress $primary
Get-RemoteMailbox -Identity $row.Identity |
Format-List DisplayName,RecipientTypeDetails,PrimarySmtpAddress,
RemoteRoutingAddress,EmailAddresses
}
The script is intentionally not a fully automated migration tool. It stops at the first discrepancy so that an administrator can assess the cause and state. Before a production batch, the code should be validated with a small number of test mailboxes and the Exchange versions in use.
Phase 4: Synchronize, License, and Verify
After the on-premises change, a delta cycle is started on the Entra Connect server:
Start-ADSyncSyncCycle -PolicyType Delta
For user mailboxes, a valid Exchange Online service plan must then be assigned, for example through group-based licensing. Shared, room, and equipment mailboxes must be evaluated according to the current Microsoft licensing terms and the required functionality.
Provisioning is asynchronous. Microsoft states that normal changes usually take less than 30 minutes, but in individual cases may take up to 24 hours. During this time, the upstream mail flow should hold messages in a controlled manner instead of delivering them to a target that is not yet ready.
The on-premises verification must now show a RemoteMailbox:
Get-RemoteMailbox -Identity user01@contoso.com |
Format-List RecipientTypeDetails,PrimarySmtpAddress,RemoteRoutingAddress,EmailAddresses
Get-Mailbox -Identity user01@contoso.com -ErrorAction SilentlyContinue
In Exchange Online, verify whether the previous MailUser has become an actual mailbox:
Get-EXORecipient -Identity user01@contoso.com |
Format-List RecipientTypeDetails,PrimarySmtpAddress,EmailAddresses
Get-EXOMailbox -Identity user01@contoso.com |
Format-List RecipientTypeDetails,PrimarySmtpAddress,ExchangeGuid
The batch is considered complete only once the following tests are also successful:
- Delivery from external and internal senders
- Sending to external and internal recipients
- Replying to an old message to verify the X500 address
- Signing in with Outlook and Outlook on the web
- Delegations and Send As
- Forwarding and transport rules
- Room and equipment bookings
- Applications, scanners, and SMTP relays
- Message trace confirming delivery to the new cloud mailbox
Rollback and Cleanup
The on-premises source mailbox must not be deleted with Remove-StoreMailbox during the validation phase. As long as it exists as a disconnected mailbox within mailbox retention, a technical fallback option remains. However, rollback requires a controlled reversal of the RemoteMailbox attributes and reconnection of the on-premises mailbox; at the same time, two active delivery targets must be prevented.
Before a rollback, mail flow, synchronization state, and messages already received in the cloud must therefore be backed up. Switching back is not a simple one-liner and should be part of the change as a tested runbook.
After successful acceptance, export requests are cleaned up, PST files are archived according to the protection and retention concept, and temporary permissions on the export share are removed:
Get-MailboxExportRequest -BatchName $BatchName |
Remove-MailboxExportRequest -Confirm:$false
The disconnected mailboxes should only be permanently cleaned up after the agreed rollback window has expired and according to the retention concept.
Conclusion
If hybrid remote moves are not permitted and no mailbox data needs to be transferred to Exchange Online, an existing synchronized AD user can be switched in a controlled manner from an on-premises mailbox to a new cloud mailbox. The critical part is not Enable-RemoteMailbox, but the process control around it: complete inventorying, verified PST backup, explicit approvals, retention of proxy and X500 addresses, controlled mail flow, and an actual rollback window.
Comments
Comments are loaded from GitHub / Giscus.