Migración a EXO sin Remote Move
Cómo aprovisionar buzones locales de Exchange de forma controlada como nuevos buzones vacíos de Exchange Online: copia de seguridad PST, aprobación mediante CSV, RemoteMailbox, sincronización, validación y rollback.
Un Hybrid Remote Move es la vía habitual para mover un buzón local junto con su contenido a Exchange Online. No todas las organizaciones permiten esta ruta de migración. Si una directiva de seguridad excluye los Remote Moves, puede ser aceptable adoptar deliberadamente otro enfoque: el buzón local se respalda como PST, se desvincula del usuario de AD sincronizado y se aprovisiona para el mismo usuario un buzón nuevo y vacío en Exchange Online.
Esta vía no es una migración de buzón. No transfiere mensajes, calendarios, reglas ni permisos a la nube. El PST sirve exclusivamente como copia de seguridad y no se importa en este escenario. Por tanto, el procedimiento solo es adecuado si un buzón de destino vacío es aceptable desde el punto de vista operativo y se ha autorizado expresamente la pérdida de la configuración activa del buzón.
Estado objetivo y requisitos estrictos
Después del cutover, se conserva el mismo usuario de AD. Sin embargo, localmente ya no se administra como UserMailbox, SharedMailbox, RoomMailbox o EquipmentMailbox, sino como RemoteMailbox. Tras la sincronización, este objeto representa el nuevo buzón en Exchange Online.
El estado deseado es el siguiente:
- El buzón local cuenta con una copia de seguridad completa en PST.
- El buzón local está desconectado, pero aún no se ha eliminado definitivamente dentro de la retención configurada.
- El usuario de AD existente está habilitado como RemoteMailbox.
- Se conservan la dirección principal, los alias y el
LegacyExchangeDNanterior. - Entra Connect ha sincronizado los cambios.
- Para los buzones de usuario se ha asignado un plan de servicio de Exchange Online.
- Exchange Online muestra un buzón real en la nube y el flujo de correo termina allí.
Además, antes de comenzar se debe aclarar lo siguiente:
- El recurso compartido para PST es accesible mediante UNC. El grupo
Exchange Trusted Subsystemtiene allí permisos de lectura y escritura. - La cuenta ejecutora cuenta con el rol de administración
Mailbox Import Export. - El PST es únicamente la copia de seguridad acordada; no se prevé ninguna importación posterior.
- La retención, Litigation Hold, eDiscovery y los requisitos regulatorios se han revisado por separado.
- Se han inventariado las delegaciones, Send-As, Send-on-Behalf, los reenvíos, las reglas de bandeja de entrada, los dispositivos móviles y los accesos de aplicaciones.
- Durante la exportación y la conmutación, los mensajes entrantes se retienen de forma controlada en el gateway previo. Los usuarios y las aplicaciones ya no deben poder escribir en el buzón de origen.
- La retención de la base de datos de buzones local cubre la ventana de rollback.
Por qué una lista de aprobación CSV es indispensable
Una canalización directa como Get-Mailbox | Disable-Mailbox es demasiado arriesgada para este proceso. También podría incluir buzones del sistema, de Discovery u otros buzones no aprobados. Por ello, el siguiente procedimiento utiliza dos aprobaciones explícitas:
Action=CUTOVERdetermina qué fila puede conmutarse realmente.PstVerified=YESconfirma que el archivo de exportación se ha revisado técnica y organizativamente.
Primero solo se genera el inventario:
$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
A continuación, el archivo se depura desde el punto de vista operativo. Solo los buzones realmente aprobados reciben Action=CUTOVER. Los buzones del sistema y los objetos especiales no deben figurar en esta lista.
Fase 1: Respaldar el buzón principal y el archivo como PST
New-MailboxExportRequest solo escribe en una ruta UNC. Se genera un nombre de archivo único para cada buzón. Un archivo en línea activo del Exchange local se exporta por separado:
$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
}
}
La exportación solo queda aprobada cuando cada solicitud tiene el estado 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"
Además, se deben comprobar la existencia, el tamaño, la legibilidad, la incorporación a la copia de seguridad y la protección de acceso de los archivos. Solo entonces se establece PstVerified=YES para la fila CSV correspondiente.
Fase 2: Respaldar los datos del buzón y los atributos de Exchange
Antes del primer cambio, se crea una instantánea legible por máquina para cada buzón. Es más importante que una captura de pantalla, porque posteriormente se pueden reconstruir con precisión los alias, los GUID y el LegacyExchangeDN:
$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"
}
Las delegaciones y los reenvíos requieren exportaciones independientes. Como mínimo, se debe respaldar por separado la siguiente información:
$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
Estas configuraciones no se transfieren automáticamente al nuevo buzón en la nube.
Fase 3: Desconectar el buzón local y habilitar RemoteMailbox
El cutover propiamente dicho es breve, pero tiene consecuencias. Disable-Mailbox elimina los atributos de Exchange del usuario de AD y desconecta el buzón local. Los datos del buzón se conservan como disconnected mailbox hasta que expire la retención de la base de datos. Justo después, Enable-RemoteMailbox habilita el mismo usuario de AD para Exchange Online.
El siguiente script procesa exclusivamente filas aprobadas dos veces. Conserva la dirección SMTP principal, todas las direcciones proxy existentes y el LegacyExchangeDN anterior como dirección X500. La entrada X500 evita NDR al responder a mensajes antiguos o al utilizar entradas antiguas de autocompletado de Outlook.
$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
}
El script no pretende ser deliberadamente una herramienta de migración totalmente automatizada. Detiene la ejecución ante la primera discrepancia para que un administrador pueda evaluar la causa y el estado. Antes de un lote de producción, el código debe validarse con unos pocos buzones de prueba y las versiones de Exchange utilizadas.
Fase 4: Sincronizar, asignar licencias y verificar
Tras el cambio local, se inicia un ciclo delta en el servidor de Entra Connect:
Start-ADSyncSyncCycle -PolicyType Delta
Para los buzones de usuario, debe asignarse después un plan de servicio válido de Exchange Online, por ejemplo mediante licencias basadas en grupos. Los buzones compartidos, de salas y de equipos deben evaluarse conforme a las condiciones de licencia actuales de Microsoft y las funciones necesarias.
El aprovisionamiento es asíncrono. Microsoft suele indicar menos de 30 minutos para los cambios normales, pero en casos individuales puede tardar hasta 24 horas. Durante este tiempo, el flujo de correo previo debe retener los mensajes de forma controlada en lugar de entregarlos a un destino que aún no está listo.
La comprobación local debe mostrar ahora una RemoteMailbox:
Get-RemoteMailbox -Identity user01@contoso.com |
Format-List RecipientTypeDetails,PrimarySmtpAddress,RemoteRoutingAddress,EmailAddresses
Get-Mailbox -Identity user01@contoso.com -ErrorAction SilentlyContinue
En Exchange Online se comprueba si el anterior MailUser se ha convertido en un buzón real:
Get-EXORecipient -Identity user01@contoso.com |
Format-List RecipientTypeDetails,PrimarySmtpAddress,EmailAddresses
Get-EXOMailbox -Identity user01@contoso.com |
Format-List RecipientTypeDetails,PrimarySmtpAddress,ExchangeGuid
El lote solo se considera finalizado cuando, además, las siguientes pruebas se han realizado correctamente:
- Entrega desde el exterior y desde el interior
- Envío al exterior y al interior
- Respuesta a un mensaje antiguo para comprobar la dirección X500
- Inicio de sesión con Outlook y Outlook en la Web
- Delegaciones y Send-As
- Reenvíos y reglas de transporte
- Reservas de salas y equipos
- Aplicaciones, escáneres y relés SMTP
- Message Trace con entrega al nuevo buzón en la nube
Rollback y limpieza
El buzón local de origen no debe eliminarse durante la fase de validación con Remove-StoreMailbox. Mientras exista dentro de la retención del buzón como disconnected mailbox, sigue habiendo una posibilidad técnica de reversión. Sin embargo, un rollback requiere una reversión controlada de los atributos de RemoteMailbox y volver a conectar el buzón local; al mismo tiempo, se debe evitar que existan dos destinos de entrega activos.
Por tanto, antes de un rollback se deben asegurar el flujo de correo, el estado de sincronización y los mensajes ya recibidos en la nube. Una reversión no es una simple línea de comando y debe formar parte del cambio como runbook probado.
Tras la aceptación satisfactoria, se limpian las solicitudes de exportación, los archivos PST se archivan conforme al concepto de protección y retención, y se eliminan los permisos temporales en el recurso compartido de exportación:
Get-MailboxExportRequest -BatchName $BatchName |
Remove-MailboxExportRequest -Confirm:$false
Los disconnected mailboxes solo deben eliminarse definitivamente una vez transcurrida la ventana de rollback acordada y conforme al concepto de retención.
Conclusión
Si no se permiten Hybrid Remote Moves y no es necesario trasladar datos de buzón a Exchange Online, un usuario de AD sincronizado existente puede conmutarse de forma controlada de un buzón local a un buzón nuevo en la nube. La parte crítica no es Enable-RemoteMailbox, sino el control del proceso que lo rodea: inventario completo, copia de seguridad PST verificada, aprobaciones explícitas, conservación de las direcciones proxy y X500, un flujo de correo controlado y una ventana de rollback real.
Comentarios
Los comentarios se cargan desde GitHub / Giscus.