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

117 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.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_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.