Google Workspace to M365 Mailbox Migration: The Lessons We Learned the Hard Way
Migrating from Google Workspace to Microsoft 365 sounds straightforward on paper: set up domain-wide delegation in Google Cloud, configure a migration endpoint in Exchange Online, map your users, and hit start.
In practice, moving hundreds of mailboxes, calendars, and contacts across cloud ecosystems is full of real-world traps. When we recently migrated an enterprise organization off Google Workspace, we hit every classic hurdle—from silent API authentication failures to licensing missteps.
Here is our battle-tested operational playbook, including the Exchange Online PowerShell commands to run the migration and the exact gotchas to avoid.
The Architecture: How the Migration Bridge Works
Unlike standard IMAP migrations (which miss calendar events and contacts entirely), an enterprise Google Workspace migration relies on Google Cloud API Integration to pull full mailbox profiles into Microsoft 365.
The bridge requires four core components:
- Google Cloud Service Account: Acts as the authenticated bridge between tenants.
- API Scopes Enabled: Gmail API, Admin SDK API, Google Calendar API, and Contacts (People) API.
- Domain-Wide Delegation: Configured in Google Workspace Admin Console to allow the service account to impersonate user mailboxes without prompt.
- Exchange Online Migration Endpoint: Connects Exchange Online directly to Google’s infrastructure using the generated JSON key.
Step-by-Step PowerShell Execution
While the initial project setup happens in the Google Cloud Console, running and managing the actual migration is much faster using Exchange Online PowerShell.
1. Test Endpoint Connectivity
Before creating a migration endpoint, verify that Exchange Online can authenticate with Google Workspace using your Service Account JSON key and a Super Admin account email:
# Test connection to Google Workspace
Test-MigrationServerAvailability -Gmail -ServiceAccountKeyFileData $([System.IO.File]::ReadAllBytes("C:\Keys\gsuite-migration-key.json")) -EmailAddress "gsuite.admin@yourdomain.com"
2. Create the Migration Endpoint
Once the availability test returns Success, establish your migration endpoint:
# Create the Gmail Migration Endpoint in Exchange Online
New-MigrationEndpoint -Gmail -ServiceAccountKeyFileData $([System.IO.File]::ReadAllBytes("C:\Keys\gsuite-migration-key.json")) -EmailAddress "gsuite.admin@yourdomain.com" -Name "GoogleWorkspace_MigrationEndpoint"
3. Provision User Mapping CSV and Launch Batch
Prepare a simple CSV containing your target user mapping (Header: EmailAddress). Then create and start the migration batch:
# Create the batch using target routing domain
New-MigrationBatch -SourceEndpoint "GoogleWorkspace_MigrationEndpoint" -Name "Batch_Phase1_Pilot" -CSVData $([System.IO.File]::ReadAllBytes("C:\Migrate\PilotUsers.csv")) -TargetDeliveryDomain "yourdomain.mail.onmicrosoft.com"
# Kick off initial sync pass
Start-MigrationBatch -Identity "Batch_Phase1_Pilot"
Real-World Gotchas (Read Before Migrating)
Save yourself hours of troubleshooting by keeping these three production gotchas in mind:
- The Licensing Trap (Target MailUsers): Do NOT assign Exchange Online licenses to your target users before starting the batch. When the batch starts, target accounts in M365 must exist as
MailUsers. Exchange converts them to full mailboxes during sync. If a license is pre-assigned, the batch will throw a TargetMailboxAlreadyExists Exception. You have a 30-day window after batch startup to assign licenses. - The “Generic X” Endpoint Error: If Exchange fails to create the migration endpoint with an unhelpful error, 90% of the time it is due to trailing spaces in your Client ID when pasting into Google Workspace Domain-Wide Delegation, or forgetting to enable the Admin SDK API in Google Cloud Console.
- Do NOT Cut MX Records Early: DNS propagation takes up to 72 hours. Keep the migration batch running in a incremental sync state after MX cutover to catch delta emails sent to Google while old DNS records expire globally.
Migrating Google Drive & Shared Drives to M365: Automated Setup, Permissions & PowerShell Mapping
Migrating user files from Google My Drive to OneDrive for Business, and Google Shared Drives to SharePoint Online, requires careful planning. If you simply drag-and-drop or upload raw zip files, you lose document version history, break shared file links, and mess up internal permissions.
In this guide, we walk through the exact enterprise workflow using Microsoft 365 Migration Manager, including the PowerShell script to generate your user and site mapping files automatically.
Phase 1: Pre-Migration Scan & Google Authorization
Before moving a single gigabyte, you must authorize Microsoft 365 Migration Manager inside your Google Workspace tenant and run a comprehensive pre-migration scan.
- Navigate to Microsoft 365 Admin Center > Setup > Migration Center and select Google Workspace.
- Click Connect to Google Workspace and download the custom Service Account Public Key.
- Log into your Google Workspace Admin Console, go to Security > API Controls > Domain-Wide Delegation, and register the Service Account Client ID with the required scope endpoints.
- Return to Microsoft 365 Admin Center and initiate a Pre-Migration Scan. This scans all active Google Drives and flags illegal characters (
* : ? " < > |), oversized files (>250 GB), and path length errors (>400 characters).
Phase 2: Automated Mapping Script (Google Drive to OneDrive & SharePoint)
Managing target destination paths manually in the UI for hundreds of users is unfeasible. Save this script as New-M365DriveMapping.ps1 to generate your bulk CSV mapping file for Migration Manager automatically.
<#
.SYNOPSIS
Generates a Migration Manager compliant CSV mapping Google Drive to OneDrive/SharePoint.
.NOTES
Author: Innovation Guru (dsinnovationguru.com)
Input: CSV containing Source Google Email and Target UPN
#>
[CmdletBinding()]
param (
[string]$SourceCsvPath = "C:\Migrate\GoogleUsers.csv",
[string]$OutputCsvPath = "C:\Migrate\MigrationManagerMapping.csv",
[string]$TenantName = "yourcompany" # e.g., yourcompany.onmicrosoft.com
)
# Read Source Input
$GoogleUsers = Import-Csv -Path $SourceCsvPath
$MappingResults = @()
foreach ($User in $GoogleUsers) {
# Format Target OneDrive URL automatically
# Example UPN: john.doe@company.com -> john_doe_company_com
$SanitizedUser = $User.TargetUPN -replace '[^\w]', '_'
$OneDriveUrl = "https://$TenantName-my.sharepoint.com/personal/$SanitizedUser"
$MappingResults += [PSCustomObject]@{
SourceGoogleEmail = $User.SourceEmail
TargetDestination = "OneDrive"
TargetUrl = $OneDriveUrl
TargetSitePath = "/Documents"
}
}
# Export Formatted CSV for Migration Manager Import
$MappingResults | Export-Csv -Path $OutputCsvPath -NoTypeInformation
Write-Host "Migration mapping file generated successfully at: $OutputCsvPath" -ForegroundColor Green
Phase 3: Shared Drives to SharePoint Online Mapping Strategy
Google Shared Drives operate differently from personal My Drives. You must map each Google Shared Drive to an existing SharePoint Online Document Library or Teams Channel Site prior to cutover.
- Create Target SharePoint Sites: Set up target SharePoint Communication or Team sites for each department (e.g., Marketing, HR, Engineering).
- Upload Custom Shared Drive CSV: In Migration Manager, switch to the Shared Drives tab and upload a 2-column CSV mapping the
Source Shared Drive Nameto the targetSharePoint Site Document Library URL. - Run Initial Migration Copy: Click Migrate. Data is copied incrementally in the background without affecting active Google Drive users.
Production Gotchas & Best Practices
- Google Docs/Sheets/Slides Auto-Conversion: Native Google format files (.gdoc, .gsheet, .gslides) cannot exist in Microsoft 365. Migration Manager automatically converts them into native OpenXML formats (.docx, .xlsx, .pptx) during transfer. Ensure users know their file extensions will change.
- Delta Sync Before Cutover: Run incremental migration passes on the final weekend before cutover. Only modified files will copy over, reducing downtime drastically.
- Permissions Mapping: External permissions from Google Workspace will not transfer unless the external users already exist as Guests in Microsoft Entra ID. Run an external sharing audit before copying.
Google Chat, Spaces & Groups to M365: The Hard Truth and Transition Playbook
When migrating an enterprise from Google Workspace to Microsoft 365, mailboxes and drives get the most attention. But collaboration continuity—specifically historical chat threads, Google Spaces, and mailing lists—is where most migrations hit a wall.
If you are looking for a magical one-click tool that converts active Google Chat spaces straight into Microsoft Teams channels with all historical logs intact, it doesn’t exist. Native APIs simply don’t support direct thread-to-thread structural migration.
Here is our production playbook on how to handle Google Chat, Spaces, and Google Groups without leaving your users in the dark.
1. Dealing with Google Chat and Spaces (The Archival Strategy)
Because direct message history and Google Chat threads cannot be imported natively into Microsoft Teams as interactive chat history, you must choose one of two paths for cutover:
- The Clean Cutover (Recommended): Announce a hard freeze date for Google Chat. Use Google Takeout or compliance exports to archive historical JSON/HTML transcripts for legal/audit compliance, store them in a secure SharePoint compliance vault, and start fresh inside Microsoft Teams.
- Third-Party Migration Tools: For heavily regulated enterprises that require historical chats to be visible, leverage specialized enterprise migration vendors (like CloudFuze or AvePoint) that extract Google Chat payloads into readable archive files or read-only channels.
2. Recreating Google Groups as Microsoft 365 Groups (PowerShell)
Unlike chat threads, Google Groups (mailing lists and collaborative inboxes) can be systematically mapped and recreated in Microsoft Entra ID and Exchange Online using PowerShell.
Save this script as New-M365GroupsFromGoogle.ps1 to bulk-provision your distribution lists and security groups from a CSV export.
<#
.SYNOPSIS
Bulk creates Microsoft 365 Distribution Groups from a Google Groups export CSV.
.NOTES
Author: Innovation Guru (dsinnovationguru.com)
Prerequisites: ExchangeOnlineManagement module installed.
#>
[CmdletBinding()]
param (
[string]$GroupCsvPath = "C:\Migrate\GoogleGroupsExport.csv"
)
# Connect to Exchange Online
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
Connect-ExchangeOnline -ShowProgress $true
# Import Group CSV (Expected headers: GroupName, GroupEmail, Description)
$GoogleGroups = Import-Csv -Path $GroupCsvPath
foreach ($Group in $GoogleGroups) {
Write-Host "Creating Distribution Group: $($Group.GroupName) ($($Group.GroupEmail))..." -ForegroundColor Yellow
try {
New-DistributionGroup -Name $Group.GroupName `
-DisplayName $Group.GroupName `
-PrimarySmtpAddress $Group.GroupEmail `
-Notes $Group.Description `
-Type "Security"
Write-Host "Successfully created: $($Group.GroupEmail)" -ForegroundColor Green
}
catch {
Write-Error "Failed to create group $($Group.GroupEmail): $_"
}
}
3. Mapping Group Memberships
Once your groups are provisioned in Microsoft 365, you must populate their memberships. You can export membership lists from the Google Workspace Admin console as CSV files and use Exchange PowerShell to add members in bulk:
# Add members to the newly created M365 distribution group in bulk
$Members = Import-Csv -Path "C:\Migrate\GroupMembers_Marketing.csv"
foreach ($Member in $Members) {
Add-DistributionGroupMember -Identity "marketing-dl@yourdomain.com" -Member $Member.UserEmail
}
Write-Host "Group membership import complete." -ForegroundColor Green
Summary Checklist for Collaboration Cutover
- Export Google Chat history via Google Workspace compliance tools for retention storage before disabling old accounts.
- Pre-create your Microsoft Teams structures and Channels manually or via Teams PowerShell templates matching your old Google Spaces.
- Provision all Google Groups as M365 Distribution or Security Lists *before* switching your final MX records.
