7 August 2026 12 min read

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:

  1. The on-premises mailbox has been fully backed up as a PST.
  2. The on-premises mailbox is disconnected but has not yet been permanently deleted within the configured retention period.
  3. The existing AD user is enabled as a RemoteMailbox.
  4. The primary address, aliases, and the old LegacyExchangeDN are retained.
  5. Entra Connect has synchronized the changes.
  6. An Exchange Online service plan is assigned for user mailboxes.
  7. 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 Subsystem has read and write permissions there.
  • The executing account has the Mailbox Import Export management 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=CUTOVER determines which row may actually be switched.
  • PstVerified=YES confirms 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
Options explained
OptionEffect
Get-Mailbox -ResultSize UnlimitedRemoves the default limit of 1,000 results; without this parameter, mailboxes are missing from the inventory in large environments
-RecipientTypeDetails UserMailbox,SharedMailbox,RoomMailbox,EquipmentMailboxRestricts the query to the four mailbox types to be switched; system and discovery mailboxes are excluded
Sort-Object PrimarySmtpAddressSorts the output by the primary SMTP address so that the CSV file remains consistently ordered during business review
Export-Csv -PathDestination path of the CSV file
-NoTypeInformationSuppresses the type header #TYPE ..., which older PowerShell versions would otherwise write as the first line
-Encoding UTF8Writes the file in UTF-8 encoding so that umlauts in display names are retained correctly

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
    }
}
Options explained
OptionEffect
Import-Csv $CsvPathReads the approval list; each row becomes an object with the CSV columns as properties
Where-Object Action -eq "CUTOVER"Processes only explicitly approved rows
New-MailboxExportRequest -MailboxSource mailbox of the export (here, the identity from the CSV row)
-FilePathDestination path of the PST file; it must be a UNC path, as the cmdlet rejects local paths
-NameUnique request name; later enables targeted assignment of primary and archive exports
-BatchNameGroups all requests of a run under a batch name; the basis for status queries and cleanup
-IsArchiveExports the online archive instead of the primary mailbox; therefore, there is a second request per mailbox with an active archive

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"
Options explained
OptionEffect
Get-MailboxExportRequest -BatchNameLists all export requests in the specified batch
Get-MailboxExportRequestStatistics -IncludeReportAdds the detailed history report to the statistics, where the causes of errors for individual requests are listed
Format-Table ... -AutoSizeDisplays the specified properties in table form; -AutoSize adjusts column widths to the content
Where-Object Status -ne "Completed"Filters for all requests that have not yet completed or have failed; the output must be empty before proceeding

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"
    }
Options explained
OptionEffect
New-Item -ItemType Directory -Path ... -ForceCreates the snapshot directory; -Force suppresses the error if it already exists
Get-Mailbox -IdentityRetrieves the current mailbox object for the respective CSV row
Select-Object Identity,...,ServerNameReduces the object to the attributes required for later reconstruction (GUIDs, addresses, LegacyExchangeDN, database)
Export-ClixmlSerializes the object as type-preserving CLIXML; unlike CSV, multivalued properties such as EmailAddresses are fully retained and can be read again using Import-Clixml

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
Options explained
OptionEffect
Format-List ForwardingAddress,ForwardingSmtpAddress,DeliverToMailboxAndForwardDisplays the mailbox’s three forwarding attributes in list format
Get-MailboxPermission -IdentityLists mailbox permissions such as Full Access
Get-ADPermission -IdentityLists AD permissions on the user object, including Send As; expects the Distinguished Name here
Get-InboxRule -MailboxLists the mailbox’s server-side inbox rules
Get-CalendarProcessing -IdentityDisplays booking configuration; relevant for room and equipment mailboxes
-ErrorAction SilentlyContinueSuppresses the error for mailbox types without booking configuration so that the backup does not stop

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
}
Options explained
OptionEffect
Get-Mailbox -Identity ... -ErrorAction StopRetrieves the source mailbox; -ErrorAction Stop makes a lookup error a terminating error instead of silently continuing
Disable-Mailbox -IdentityRemoves the Exchange attributes from the AD user and disconnects the on-premises mailbox; the data remains as a disconnected mailbox in the database
-Confirm:$falseSuppresses the interactive prompt; approval is provided here through the CSV list, not at the prompt
Enable-RemoteMailbox -IdentityEnables the same AD user as a RemoteMailbox for Exchange Online
-AliasSets the Exchange alias back to the value from the approval list
-PrimarySmtpAddressRetains the previous primary SMTP address
-RemoteRoutingAddressTarget address in the mail.onmicrosoft.com routing domain through which on-premises Exchange reaches the cloud mailbox
-ACLableSyncedObjectEnabledMarks the object as ACL-capable so that permissions such as Full Access can be evaluated in Exchange Online after synchronization
-Shared / -Room / -EquipmentCreates the applicable special type instead of a user mailbox; the script sets exactly one switch appropriate to the source type
Set-RemoteMailbox -EmailAddressPolicyEnabled $falseExcludes the object from the email address policy so that it does not overwrite manually configured addresses
-EmailAddressesSets the complete, deduplicated address list, including old proxy addresses, the routing address, and the X500 entry
Get-RemoteMailbox -IdentityQueries the result for verification immediately after the cutover

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
Options explained
OptionEffect
-PolicyType DeltaSynchronizes only objects changed since the last cycle; the alternative Initial would perform a complete, significantly longer run

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
Options explained
OptionEffect
Get-RemoteMailbox -IdentityMust return the switched object as a RemoteMailbox
Format-List RecipientTypeDetails,...Displays the type, addresses, and routing address for verification in list format
Get-Mailbox -Identity ... -ErrorAction SilentlyContinueCross-check: The command must no longer return anything because no connected mailbox exists on-premises; -ErrorAction SilentlyContinue suppresses the expected error message

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
Options explained
OptionEffect
Get-EXORecipient -IdentityDisplays the recipient type in Exchange Online; UserMailbox or the special type is expected, no longer MailUser
Get-EXOMailbox -IdentityReturns only actual cloud mailboxes; a result proves that provisioning is complete
Format-List ...,ExchangeGuidLists the verification attributes; ExchangeGuid uniquely identifies the new cloud mailbox

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
Options explained
OptionEffect
Get-MailboxExportRequest -BatchNameSelects exactly the export requests of the completed batch
Remove-MailboxExportRequest -Confirm:$falseRemoves the requests without prompting; the PST files themselves remain unaffected

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.

Sources

  1. Microsoft Learn – Enable-RemoteMailbox

    Enables an existing on-premises AD user for a mailbox in the cloud-based service and documents the switches for user, shared, room, and equipment mailboxes.

    https://learn.microsoft.com/en-us/powershell/module/exchange/enable-remotemailbox?view=exchange-ps
  2. Microsoft Learn – New-MailboxExportRequest

    Reference for exporting primary and archived on-premises mailboxes to PST files.

    https://learn.microsoft.com/en-us/powershell/module/exchangepowershell/new-mailboxexportrequest?view=exchange-ps
  3. Microsoft Learn – Mailbox import and export requests

    Prerequisites for the export share, permissions, and Exchange Trusted Subsystem.

    https://learn.microsoft.com/en-us/exchange/mailbox-import-and-export-requests-exchange-2013-help
  4. Microsoft Learn – Disable or delete a mailbox in Exchange Server

    Behavior of Disable-Mailbox, removal of Exchange attributes, and retention of the disconnected mailbox.

    https://learn.microsoft.com/en-us/exchange/recipients/disconnected-mailboxes/disable-or-delete-mailboxes
  5. Microsoft Learn – Disconnected mailboxes

    Reconnecting, restoring, and permanently deleting disconnected mailboxes.

    https://learn.microsoft.com/en-us/exchange/recipients/disconnected-mailboxes/disconnected-mailboxes
  6. Microsoft Learn – Delays in provisioning of a user or mailbox
  7. Microsoft Learn – Move mailboxes between on-premises and Exchange Online organizations

    The standard hybrid remote move as a reference and distinction from the new setup described here.

    https://learn.microsoft.com/en-us/exchange/hybrid-deployment/move-mailboxes

Comments

Comments are loaded from GitHub / Giscus.