This is a reference post on how to perform (as much as possible) of a mail migration from G Suite (formerly Google Apps) into Office 365 via PowerShell.
The Office 365 documentation for G Suite is great, where you might end up with sub-pages requiring a bunch of tabs or some circular page logic along the way… This is to hopefully condense it into a single page as a quick reference. You’ll also want to understand Office 365’s best practices.
G Suite to Office 365 will use IMAP which only migrates mail content. You will have a few limitations where you will need to determine a solution before leaving one service for another. The below bulleted list is from the above IMAP link, with emphasis added which may concern you (or other IT departments):
- You can only migrate items in a user’s inbox or other mail folders. This type of migration doesn’t migrate contacts, calendar items, or tasks.
- You can migrate a maximum of 500,000 items from a user’s mailbox (emails are migrated from newest to oldest).
- The biggest email you can migrate is 35 MB.
- If you limited the connections to your source email system, it’s a good idea to increase them to improve migration performance.
Common connection limits include client/server total connections, per-user connections, and IP address connections on either the server or the firewall.
OK, figure out something to do for those, if you don’t want to upset your end users. Let’s continue (where this information is provided as is, at your own risk and no warranties implied!).
Step 1: Verify your own domain
This adds the domain name you want to absorb into your Office 365 tenant.
$o365admin = Get-Credential $domain = ‘domain-name-to-move.com’ Import-Module -Name MSOnline Connect-MSOLService -Credential $o365admin New-MsolDomain -Name $domain -VerificationMethod DnsRecord Get-MsolDomainVertificationDns -DomainName $domain -Mode DnsTxtRecord <# And we hit the first step that requires something outside of PowerShell (maybe)! Add the Text value to your DNS infrastructure. Wait ~15 minutes for initial replication to occur or check it has replicated on an external service. #> Confirm-MsolDomain -DomainName $domain
Optional Step: Reduce email delays
Microsoft’s guide provides an optional suggestion of updating your MX record’s Time-To-Live (TTL) to 3600 (1 hour). This is noted to be performed before the migration request, but is listed after you start a migration batch in their current directions where you might have closed out your DNS management tool. I’ve moved it higher in the steps list if you want to make the modification at the same time as adding the TXT record in from the step above.
Step 2: Add Users to Office 365
Create a CSV with the following header information as outlined here:
User Name,First Name,Last Name,Display Name,Job Title,Department,Office Number,Office Phone,Mobile Phone,Fax,Address,City,State or Province,ZIP or Postal Code,Country or Region
<# Create the Office 365 accounts and assign a license for a mailbox to be created for copying from Gmail into Office 365. #> Import-Csv -Path "C:\Office365Migration\NewAccounts.csv" | ` ForEach-Object -Process { New-MsolUser -DisplayName $PSItem.DisplayName -FirstName $PSitem.FirstName -LastName $PSitem.LastName -UserPrincipalName $PSitem.UserPrincipalName -UsageLocation $PSitem.UsageLocation -LicenseAssignment $PSitem.AccountSkuId } | Export-Csv -Path ‘C:\Office365Migration\NewAccountResults.csv’
Step 3: Create a list of Gmail mailboxes
An abridged summary. Create a CSV with the following header columns:
EmailAddress,UserName,Password
EmailAddress is the Office 365 User Name (and mailbox) content should be copied into,
UserName is the sign-in name for the Gmail account,
Password is the mailbox’s Gmail password or an app password (which is recommended as it’s more secure) for Gmail. Save the file a CSV. Example C:\Office365Migration\GmailMailboxes.csv Keep this path handy for a step below.
Step 4: Connect Office 365 to Gmail (with extra info on what’s going on here and here)
$o365admin = Get-Credential $Session = New-PSSession -ConfigurationName 'Microsoft.Exchange' -ConnectionUri 'https://outlook.office365.com/powershell-liveid/' -Credential $o365admin -Authentication 'Basic' -AllowRedirection Import-PSSession -Session $Session $gmail = ‘imap.gmail.com’ $port = 993 # Or 143 $security = ‘Ssl’ # Or ‘Tls’, or ‘None’ $endpointname = ‘GSuiteMigrations’ <#Test connectivity to the environment #> Test-MigrationServerAvailability -IMAP -RemoteServer $gmail -Port $port -Security $security <# Create the endpoint: #> New-MigrationEndpoint -IMAP -Name $endpointname -RemoteServer $gmail -Port $port -Security $security -MaxConcurrentMigrations 50 -MaxConcurrentIncrementalSyncs 25 -Authentication 'Basic' <# Verify the connector was created: #> Get-MigrationEndpoint -Name $endpointname
Step 5: Create a migration batch and start migrating Gmail mailboxes (with extra info here)
You should probably test and migrate in a few “department champions” or test mailboxes first.
(Maybe move the IT team first.)
If you have a ton of mailboxes, you could migration in multiple “waves”. Keep the same CSV headers, and split up the GmailMailboxes.csv into different groups if you’d like.
$batchname = ‘FirstWave’ <# Path to the CSV created in Step 3: #> $path = ‘C:\Office365Migration\GmailMailboxes.csv’ New-MigrationBatch -Name $batchname -SourceEndpoint $endpointname -CSVData ([System.IO.File]::ReadAllBytes("$path")) -AutoStart <# You can alternatively not copy over specific folders or apply some extra switch options #> New-MigrationBatch -Name $batchname -SourceEndpoint $endpointname -CSVData ([System.IO.File]::ReadAllBytes("$path")) -AutoStart -ExcludeFolders ‘Junk’ -AllowIncrementalSyncs:$true <# Check on status along the way: #> Get-MigrationBatch -Name $batchname | Format-List <# Complete a batch like so: #> Complete-MigrationBatch -Name $batchname <# Alternatively, instead of just sending to the single admin issued the completion (which is what happens when a single person runs the cmdlet, you could send it to a list or mail-enabled group: #> $emailadmins = ‘email-admins-group@an-existing-office365-domain.com’ Complete-MigrationBatch -Name $batchname -NotificationEmails $emailadmins
Step 6: Update your DNS records to route Gmail directly to Office 365 (with more info here)
Okay, your mail should be copied over and you are ready to have the end users begin using their Office 365 mailbox and want to cut mail flow into your G Suite where it should only be delivered into Office 365.
[Wait, what? That’s not what I want to do!]
If you need to keep mailflow running in a different manner and didn’t read through the link at the top, you’ll need to look into additional options. TechNet has a ton more info for you to read depending on what you want to do… Otherwise, continue:
Update your MX record.
Your <domain-key> is your Office 365 domain name.
e.g. if you signed up with domain ‘contoso.com’, it would be: contoso-com.mail.protection.outlook.com
Name | Type | TTL | Data |
@ | MX | 3600 (1HR) | <domain-key>.mail.protection.outlook.com. |
Add (4) Required CNAME records.
Name | Type | TTL | Data |
autodiscover | CNAME | 3600 (1HR) | autodiscover.outlook.com. |
sip | CNAME | 3600 (1HR) |
sipdir.online.lync.com. |
lyncdiscover | CNAME | 3600 (1HR) | webdir.online.lync.com. |
msoid | CNAME | 3600 (1HR) | clientconfig.microsoftonline-p.net. |
Update TXT record for SPF record to prevent spam.
Name | Type | TTL | Data |
@ | TXT | 3600 (1HR) | v=spf1 include:spf.protection.outlook.com -all |
Add SRV record for other Office 365 Services.
Name | Type | TTL | Data |
_sip._tls | SRV | 3600 (1HR) |
100 1 443 sipdir.online.lync.com. |
_sipfederationtls._tcp | SRV | 3600 (1HR) | 100 1 5061 sipfed.online.lync.com. |
Mobile Device Management (MDM) requires (2) additional CNAME records.
Name | Type | TTL | Data |
enterpriseregistration | CNAME | 3600 (1HR) | enterpriseregistration.windows.net. |
enterpriseenrollment | CNAME | 3600 (1HR) | enterpriseenrollment-s.manage.microsoft.com. |
Step 7: Stop synchronization with Gmail
Once happy that mailflow is working as expected, you can remove the migration batch(es):
Remove-MigrationBatch -Identity $batchname -Confirm:$false
And alternatively the migration endpoint:
Remove-MigrationEndpoint -Identity $endpointname -Confirm:$false