8.1 KiB
Runbook: Google Workspace (Gmail) → Microsoft 365 mail migration
Reusable ACG process for moving a client's mail (and optionally calendar/contacts) from Google Workspace to Exchange Online. Covers method selection, prerequisites, the native Microsoft path end-to-end, the MX cutover, and gotchas. Author: 2026-06-25 (first written while scoping Birth Biologic). Status: working draft — refine after the first real run.
1. Pick the method
| Method | Moves | Cost | When to use |
|---|---|---|---|
| MS native "Migration from Google Workspace" (EAC) | mail + calendar + contacts, with incremental/delta sync | Free | Default. Need Google super-admin to set up a service account + domain-wide delegation on the source tenant. |
| IMAP migration (EAC) | mail only | Free | Fallback when you can't get domain-wide delegation. Gmail labels→folders get messy; no calendar/contacts. |
| BitTitan MigrationWiz (3rd-party) | mail + cal + contacts + Drive, coexistence, best throttling/reporting | ~$12–15/mailbox | Larger/complex jobs, strict cutover windows, or to make this a repeatable productized service. ACG has no BitTitan account yet. |
| Takeout MBOX / Gmail→PST → 365 PST Import | mail only, one-shot | Free | Last resort / archival only. Manual, per-mailbox, no delta, fidelity loss. Avoid for live cutovers. |
Default to the MS native path below.
2. Prerequisites (all methods)
- Google super-admin on the SOURCE Workspace tenant (for the SA + domain-wide delegation, or an app password for IMAP). This is the #1 gate — confirm it before quoting timelines.
- M365 target tenant with the client's domain added and verified (do NOT cut MX yet), and licensed mailboxes provisioned for every user being migrated.
- DNS control for the MX/autodiscover/SPF/DKIM cutover — know the registrar AND the DNS host (often different). Get edit access ahead of time.
- Inventory (from the Google Admin console): user list + mailbox sizes, shared mailboxes / delegated mailboxes, groups/distribution lists, aliases, calendars/resources, and who's actually active.
- Vault the source Google admin creds / SA per the ACG pattern (see §6).
3. ACG reusable asset — the Google domain-wide-delegation SA
ACG already has a Google service account for Workspace access:
msp-tools/acg-msp-access-google-workspace.sops.yaml →
acg-msp-access@acg-msp-access.iam.gserviceaccount.com (GCP project acg-msp-access).
- Currently scoped for the security-assessment read path (
gmail.readonly,drive.readonly,admin.directory.user,reports.audit, etc.) and onboarded only to lonestarelectrical.net. - For a migration you can reuse this SA (or create a per-job one) — but the migration needs the migration scopes added to the SA's domain-wide delegation in the SOURCE client's Google Admin console, not just ACG's project. Microsoft specifies the exact scopes (see §4 step 1).
- Reusing the same SA across clients is fine; each source tenant separately authorizes the SA's
client_idin their Admin console. Keep the key in the vault; rotate per the entry's notes.
4. MS native migration — end to end
Step 1 — Source (Google) prep
-
In GCP (project
acg-msp-accessor a new one): ensure the service account exists and a JSON key is in the vault. Enable APIs: Gmail API, Google Calendar API, People API. (The legacy Contacts API was retired by Google in 2022 and cannot be enabled — them8/feedscontacts scope is now an alias served by the People API, so People API enablement covers it. Enabling the APIs inacg-msp-accessrequires being signed in as the ACG owner of that project — a client super-admin has no rights to ACG's GCP project.) -
In the SOURCE Google Admin console → Security → API controls → Domain-wide delegation → add the SA's OAuth2 Client ID (the SA's numeric "Unique ID", NOT the app client_id) with the exact 5-scope string below, comma-separated, no spaces. Google rejects the migration token request all-or-nothing — if even one scope is missing the endpoint fails later with
unauthorized_client … not authorized for any of the scopes requested. Verified current 2026-06 (MS Learnmanually-configuring-gsuite-for-migration+ Grok live cross-check):https://mail.google.com/,https://www.googleapis.com/auth/calendar,https://www.google.com/m8/feeds/,https://www.googleapis.com/auth/gmail.settings.sharing,https://www.googleapis.com/auth/contactsPropagation can take 15 min–24 h (usually minutes). Do NOT rely on a smaller "mail+calendar+contacts" set —
m8/feedsandgmail.settings.sharingare both required by the MS endpoint. -
Confirm a Google super-admin mailbox exists for the migration to impersonate.
Step 2 — Target (M365) prep
- Add + verify the client domain in M365 (TXT). Do not change MX yet.
- Provision + license every target mailbox (match Google addresses). Create any missing ones.
- (Optional) pre-create shared mailboxes / distribution groups to mirror Google.
Step 3 — Create + run the migration batch
- EAC → Migration → Add migration batch → "Migration from Google Workspace."
- Provide the SA JSON key + the super-admin to impersonate; upload a CSV of mailboxes
(
EmailAddressof each source/target — they match on the verified domain). - Start the batch → initial sync, then it keeps doing incremental/delta passes. Leave it running (days for large mailboxes). Monitor the batch report for skipped items.
Step 4 — Validate
- Spot-check a few migrated mailboxes (mail counts, folders, calendar, contacts). Resolve large skipped-item counts before cutover.
Step 5 — MX cutover (the go-live)
- Lower TTL on MX (and autodiscover) at the DNS host 24–48h ahead.
- Flip MX → M365 (
<tenant>-com.mail.protection.outlook.com), update SPF (include:spf.protection.outlook.com), republish DKIM (enable in Defender, add the 2 CNAMEs), set autodiscover CNAME →autodiscover.outlook.com, review DMARC. - Run a final incremental sync after cutover to catch mail delivered to Google during propagation.
- Complete/finalize the batch.
Step 6 — Decommission
- After a validation window: reconfigure clients (Outlook/mobile to M365), remove Google licenses, remove the SA's domain-wide delegation from the client's Admin console, cancel Workspace.
5. Gotchas
- Labels → folders: Gmail labels become folders; a message with multiple labels can duplicate. The native tool handles the primary label; expect minor structure differences.
- Shared / delegated mailboxes, groups, aliases, calendars/resources don't all come across as "mailboxes" — inventory and recreate them in M365 deliberately.
- Throttling: Google + EXO both throttle; large tenants take days. Don't promise same-day.
- SPF/DKIM/DMARC: must be updated at cutover or outbound mail fails auth / inbound double-delivers.
- Don't cut MX early — mail in flight to Google after an early MX flip is missed until the final delta.
- Calendar/contacts only migrate via the native tool or BitTitan, NOT via IMAP.
- Retention/litigation hold: if the client needs Google data retained, export before decommission.
6. Credentials & vault
- Source Google admin / SA: vault under
clients/<slug>/google-workspace.sops.yaml(seeclients/lonestar-electrical/google-workspace.sops.yamlfor the shape) or reusemsp-tools/acg-msp-access-google-workspace.sops.yaml(note which clients it's delegated to). - Never echo the SA JSON / private key in chat — read named fields with
vault.sh get-field, not a regex over the whole blob (a line-redaction missed aprivate_keyonce — errorlog 2026-06-25). - Target M365: the client's tenant-admin path (CIPP / remediation-tool / GA).
7. Make it a service (future)
If Google→365 jobs recur, decide: standardize on the native path (free, this runbook) vs. buy
BitTitan (productized, per-mailbox, coexistence). Promote this runbook to a wiki pattern via
/wiki-compile once proven on the first real migration.