Files
claudetools/projects/msp-tools/runbooks/google-workspace-to-m365-migration.md
Mike Swanson 6d65bff791 sync: auto-sync from GURU-5070 at 2026-06-26 06:29:48
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-26 06:29:48
2026-06-26 06:30:46 -07:00

8.1 KiB
Raw Blame History

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 ~$1215/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.yamlacg-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_id in 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

  1. In GCP (project acg-msp-access or 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 — the m8/feeds contacts scope is now an alias served by the People API, so People API enablement covers it. Enabling the APIs in acg-msp-access requires being signed in as the ACG owner of that project — a client super-admin has no rights to ACG's GCP project.)

  2. 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 Learn manually-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/contacts
    

    Propagation can take 15 min24 h (usually minutes). Do NOT rely on a smaller "mail+calendar+contacts" set — m8/feeds and gmail.settings.sharing are both required by the MS endpoint.

  3. Confirm a Google super-admin mailbox exists for the migration to impersonate.

Step 2 — Target (M365) prep

  1. Add + verify the client domain in M365 (TXT). Do not change MX yet.
  2. Provision + license every target mailbox (match Google addresses). Create any missing ones.
  3. (Optional) pre-create shared mailboxes / distribution groups to mirror Google.

Step 3 — Create + run the migration batch

  1. EAC → Migration → Add migration batch → "Migration from Google Workspace."
  2. Provide the SA JSON key + the super-admin to impersonate; upload a CSV of mailboxes (EmailAddress of each source/target — they match on the verified domain).
  3. 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)

  1. Lower TTL on MX (and autodiscover) at the DNS host 2448h ahead.
  2. 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.
  3. Run a final incremental sync after cutover to catch mail delivered to Google during propagation.
  4. 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 (see clients/lonestar-electrical/google-workspace.sops.yaml for the shape) or reuse msp-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 a private_key once — 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.