21 KiB
User
- User: Mike Swanson (mike)
- Machine: GURU-5070
- Role: admin
Session Summary
Worked two BirthBiologic migrations in parallel: the Datto Workplace → SharePoint migration and a new Google Workspace → M365 mail migration. The Datto/SharePoint thread started as a "verify current state" task but Mike clarified the migration host is a Jupiter VM, not BB-SERVER. Located it as the libvirt domain "Windows Server 2016" (actual Windows hostname ACG-DWP-X-BB, actually Server 2019 build 17763) — an ACG-owned migration box running Datto Workplace Server + SPMT, not enrolled in RMM and sitting on an APIPA address (no LAN). Diagnosed: host bridging was fine (vnet14 enslaved to br0, carrier up); the guest simply wasn't getting a DHCP lease from pfSense after ~2 months parked. Fixed with a static IP (172.16.3.45/22), installed the GuruRMM agent (enrolled under BirthBiologic / Main Office), and confirmed Datto Workplace Server reconnected and is re-syncing. Established (via the qemu guest agent and SPMT job storage) that the April 2026 migration only completed Supply Management (160 files, custom script) + ITSvcs (excluded); the four large folders (Admin 5.8 GB, Donor Services 109 GB, Quality 28 GB, Activity Reports) were SPMT's job and last ran 2026-04-29 — completion still unconfirmed. Per Mike, full reconciliation waits until Datto finishes re-syncing.
The larger thread was standing up the Google Workspace → M365 mail migration end-to-end. Confirmed
the Google super-admin (sysadmin@birthbiologic.com) lives in 1Password (Clients vault item "Google");
read it via the SOPS-vaulted 1Password service-account token and mirrored it into SOPS. Onboarded
BirthBio's tenant for Exchange Operator (already had Tenant Admin consented, so the suite was
provisioned programmatically — Exchange Operator SP created + Exchange Administrator role). Pulled the
authoritative Google roster via domain-wide delegation (20 accounts: 15 active, 5 suspended),
reconciled against M365, and surfaced two active accounts not on Mike's list (Dr. Chris Gillis
medicaldirector@, Michael Merritt mmerritt@) plus an address mismatch (Mindi is mindim@ in
Google, mmaher@ in M365).
Provisioned the M365 target side to Mike's licensing rules: active-12 → Business Premium (assigned BP to Mei Mei + Valerie, freed Savanna's BP by moving her to Exchange-only); created Gillis + Merritt with Exchange-only and vaulted their passwords; licensed the 4 disabled former employees with Exchange-only (kept sign-in disabled) as future shared-mailbox targets. License math closed exactly: 14 Business Premium + 7 Exchange Online Plan 1, all consumed.
Hit a real blocker creating the Gmail migration endpoint: Google returned unauthorized_client … not authorized for any of the scopes requested. Root cause = the DWD grant had only 3 of Microsoft's
required 5 scopes (missing m8/feeds and gmail.settings.sharing); Google rejects the migration
token all-or-nothing. Verified the exact 5-scope string against live MS Learn + a Grok live-search
cross-check (Gemini CLI was down on this box), updated our runbook, and Mike re-authorized all 5 in the
BB Google console. After that the endpoint (BB-Gmail) created cleanly and Batch 1 (14 live
mailboxes, mail + calendar + contacts) was created and auto-started — Status: Syncing.
Key Decisions
- Datto VM gets a static IP (172.16.3.45), not a pfSense DHCP fix. The fault was pfSense not leasing this MAC after a long park; a static on the ACG server range (172.16.3.x) is the reliable, convention-consistent fix. Follow-up: add a pfSense reservation or confirm it's outside the DHCP pool.
- Enrolled the Datto VM under BirthBiologic / Main Office (not AZ Computer Guru) since the box exists solely for BirthBio's migration and we had that site key; reversible (agents can be moved).
- Former employees migrate to shared mailboxes via a temp Exchange-only license (migrate into a licensed mailbox → convert to shared ≤50 GB = free → reclaim license). Source Google accounts must be un-suspended during migration (Gmail API can't read suspended accounts).
- Licensing tiers (Mike's rules): active-users-list → Business Premium; live Google accounts not yet
in M365 (Gillis, Merritt) → Exchange-only ("E1" = Exchange Online Plan 1); formers → Exchange-only
(reclaimable).
operations@stays BP through migration. Existing BP users left on BP (not downgraded). - Batch sequencing: live users first (Batch 1); formers as Batch 2 after un-suspending them in Google and freeing Workspace seats by suspending already-migrated live users.
- Mindi mapped via the CSV
Usernamecolumn (EmailAddress=mmaher@,Username=mindim@) — the proper MS mechanism — plus a belt-and-suspendersmindim@proxy on her mailbox. - Target delivery domain =
birthbiologic.onmicrosoft.comfor Batch 1 (no routing subdomain exists; acceptable for a near-term cutover; MS prefers a subdomain for long coexistence). - Drove Exchange via REST
InvokeCommand(Exchange Operator app token) — the EXO PowerShell module isn't installed and the app has no vaulted cert, so app-only Connect-ExchangeOnline wasn't available.
Problems Encountered
- Datto VM on APIPA (no LAN). Host bridging fine; pfSense wasn't leasing the MAC. Fixed with static 172.16.3.45/22, GW 172.16.0.1, DNS 172.16.0.1+1.1.1.1. Verified gateway/internet/DNS + RMM check-in.
vault.sh get-fieldreturnednull(len 4) for nested secrets until the field arg used dotted path:credentials.client_secret,credentials.credential. Plain leaf names don't resolve.- SPB skuId mismatch. The scope doc's BP GUID (
cbdc14ab-d96c-4132-b7f4-1f3a3a819bb4) was stale; the tenant's real SPB skuId iscbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46. License assign 400'd until corrected. - License seat propagation lag — Valerie's BP assign 400'd ("no available licenses") immediately after freeing Savanna's seat; succeeded on retry seconds later.
proxyAddressesread-only via Graph — adding Mindi's alias required ExchangeSet-Mailbox(EXO), not a Graph PATCH.- Gmail migration endpoint failed:
unauthorized_client … not authorized for any of the scopes requested. DWD had 3 of 5 required scopes. Got the verbatim 5-scope string from MS Learn + Grok; Mike re-authorized; endpoint then created. onboard365.shvault path — looked at/c/Users/guru/.claude/identity.json; fixed by exportingVAULT_ROOT_ENV=/d/vault(logged as friction).- GCP API enable initially run as the wrong identity — Mike first ran
gcloud services enableassysadmin@birthbiologic.com(no rights to ACG's project); succeeded once run as the ACG owner ofacg-msp-access. - Gemini CLI down (
throwIneligibleOrProjectIdError, needs interactive re-login) — used Grok for the live-doc cross-check instead. Logged to errorlog.
Configuration Changes
- ACG-DWP-X-BB (Jupiter "Windows Server 2016" VM): static IP 172.16.3.45/22, GW 172.16.0.1, DNS
172.16.0.1 + 1.1.1.1 (persistent). GuruRMM agent installed (universal installer), enrolled BirthBiologic
/ Main Office, agent
a4524e85-8a07-45d0-91b1-51ce7e2ca74a. - BirthBio M365 tenant (19a568e8-…): onboarded Exchange Operator (+ Defender Add-on) SPs via
onboard365.sh provision; roles assigned (Exchange Admin on Exchange Operator + Security Investigator, CA Admin on Tenant Admin, User Admin + Auth Admin on User Manager).- License changes: Mei Mei (
msenthavy) +BP; Valerie (vvaneaton) +BP; Savanna (sabron) BP→EXO; createdmedicaldirector@(Gillis) +EXO andmmerritt@(Merritt) +EXO; licensedaboutte,araso,khoffman,pnelsonwith EXO (kept sign-in disabled). Set-Mailbox mmaher@added secondarysmtp:mindim@birthbiologic.com.- Created Gmail migration endpoint
BB-Gmail; created + auto-started migration batchBB-Batch1(14 users, TargetDeliveryDomainbirthbiologic.onmicrosoft.com, NotificationEmails sysadmin@).
- License changes: Mei Mei (
- Vault (pushed):
clients/birth-biologic/google-workspace.sops.yaml,clients/birth-biologic/m365-medicaldirector.sops.yaml,clients/birth-biologic/m365-mmerritt.sops.yaml. - Repo: updated
projects/msp-tools/runbooks/google-workspace-to-m365-migration.md(exact 5-scope string + all-or-nothing gotcha + Contacts-API-retired/People-API + GCP-owner notes). - errorlog.md: gemini CLI failure entry (+ onboard365 vault-path friction).
Credentials & Secrets
- Google Workspace super-admin
sysadmin@birthbiologic.com(source tenant) — sourced from 1Password Clients vault item "Google"; mirrored to SOPSclients/birth-biologic/google-workspace.sops.yaml(credentials.password, 19 chars). Used for admin.google.com console (DWD/API) + as the migration impersonation admin. - M365 mailbox — Dr. Chris Gillis
medicaldirector@birthbiologic.com— created this session; password vaulted atclients/birth-biologic/m365-medicaldirector.sops.yaml(forceChangePasswordNextSignIn=true). - M365 mailbox — Michael Merritt
mmerritt@birthbiologic.com— created this session; password vaulted atclients/birth-biologic/m365-mmerritt.sops.yaml(forceChangePasswordNextSignIn=true). - App secrets used (already vaulted): Tenant Admin
msp-tools/computerguru-tenant-admin(credentials.client_secret); Exchange Operatormsp-tools/computerguru-exchange-operator(credentials.client_secret); Google SAmsp-tools/acg-msp-access-google-workspace(credentials.credential, full JSON); 1Password service tokeninfrastructure/1password-service-account.sops.yaml.
Infrastructure & Servers
- ACG-DWP-X-BB — Jupiter libvirt domain "Windows Server 2016" (actually WS2019, build 17763). Windows
hostname ACG-DWP-X-BB. NIC virtio 52:54:00:d4:8e:59 on br0 (vnet14). Static 172.16.3.45/22. Runs Datto
Workplace Server (svc
datto_workplace_server.default, proc WorkplaceServer) + SPMT (under Administrator profile). RMM agenta4524e85-8a07-45d0-91b1-51ce7e2ca74a. Datto source treeC:\Users\Public\Desktop\Datto Workplace Server Projects. - Jupiter 172.16.3.20 (Unraid, virsh host). LAN 172.16.0.0/22, GW pfSense 172.16.0.1. guest-exec
helper at
/root/gx.shon Jupiter. - BB-SERVER — RMM agent
6c02baa7-0f1c-4990-b466-c9ab9eaefd3b. Also has Datto Workplace Server + the original custom-script artifacts atC:\GuruMigration(bb-migration-state.json shows 160 Supply Mgmt + 49 ITSvcs uploaded in April). - BirthBio M365 tenant
birthbiologic.com/19a568e8-9e88-413b-9341-cbc224b39145.- SPs: Tenant Admin
7a199b11-97fb-4e65-917d-f8d29a53ba49; Exchange Operatorbab4699b-32a3-4434-9cad-7a4a08cc4d9e; Security Investigatorbf684a4b-…; User Manager3347ebcc-…; Defender Add-on161b8f61-…. New user objects: Gillis1bd491e1-3ba6-4214-8c6d-46426f8681da, Merritt117a3367-cd5f-4565-af11-af5ff089224f. - SKUs: Business Premium (SPB)
cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46(14/14); Exchange Online Plan 1 (EXCHANGESTANDARD)4b9405b0-7788-4568-add1-99614e613b69(7/7). Accepted domains: birthbiologic.com (default), birthbiologic.onmicrosoft.com.
- SPs: Tenant Admin
- Google project
acg-msp-access(number 806899474449). SAacg-msp-access@acg-msp-access.iam.gserviceaccount.com, OAuth2 client ID102231607889615995452. APIs enabled: Gmail, Calendar (calendar-json), People. - Google roster (DWD pull): 15 active, 5 suspended. Active staff emails per
clients/birth-biologic/ docs/migration/google-to-m365-scope.md; Mindi =mindim@(Google) ↔mmaher@(M365).
Commands & Outputs
- Required Google DWD scopes (exact, 5, comma-separated, no spaces):
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(m8/feedsis a still-valid alias for the contacts scope, served by People API; legacy Contacts API retired 2022, not enableable, not needed.) - EXO via REST:
POST https://outlook.office365.com/adminapi/beta/{tenant}/InvokeCommandwith Exchange Operator app token (scope=https://outlook.office365.com/.default), body{"CmdletInput":{"CmdletName":"…","Parameters":{…}}}. byte[] params (ServiceAccountKeyFileData, CSVData) passed as base64 strings. New-MigrationEndpoint -Gmail -Name BB-Gmail -ServiceAccountKeyFileData <b64> -EmailAddress sysadmin@birthbiologic.com→ created.New-MigrationBatch -Name BB-Batch1 -SourceEndpoint BB-Gmail -CSVData <b64> -TargetDeliveryDomain birthbiologic.onmicrosoft.com -AutoStart -NotificationEmails sysadmin@→ Status=Syncing, Total=14.- Get-MigrationUser BB-Batch1 → 14 Provisioning, 0 skipped (normal initial state).
- Datto source counts (ACG-DWP-X-BB): Admin 6,279/5.8GB · Donor Services 56,826/109GB · Quality 3,714/28GB · Supply Mgmt 160/33MB · Activity Reports 1 · ITSvcs 52 (excluded).
Pending / Incomplete Tasks
- Batch 1 monitor → MX cutover. Watch
BB-Batch1Provisioning→Syncing→Synced. When Synced: flip MX in SiteGround → M365, update SPF (include:spf.protection.outlook.com), enable/publish DKIM (2 CNAMEs), autodiscover CNAME → autodiscover.outlook.com, run final delta, then complete the batch. - Batch 2 — 5 former employees → shared. Un-suspend each in Google (free Workspace seats by suspending migrated live users), run a Gmail batch (targets already EXO-licensed: aboutte, araso, khoffman, pnelson, sabron), then convert to shared mailboxes and reclaim the 5 EXO licenses.
- Datto → SharePoint reconciliation. After ACG-DWP-X-BB finishes re-syncing with Datto cloud, compare source vs each SharePoint site to confirm what the April SPMT run left unfinished (Admin / Donor Services / Quality / Activity Reports).
- pfSense: add a DHCP reservation for 172.16.3.45 (MAC 52:54:00:d4:8e:59) or confirm it's outside the pool.
- Valerie VanEaton — active (receiving daily; last sent 2026-05-13). Julie to confirm whether the mid-May send drop-off = leave/departure; if departed, move her to the former→shared track.
- Decisions still open: confirm Merritt's long-term tier; whether
operations@becomes shared post-migration. - Wiki: BirthBio article is stale (says migration incomplete / 13 mailboxes) — recompile.
Reference Information
- Migration scope doc:
clients/birth-biologic/docs/migration/google-to-m365-scope.md. - Runbook (updated):
projects/msp-tools/runbooks/google-workspace-to-m365-migration.md. - MS Learn:
manually-configuring-gsuite-for-migration(scope string),automated-migration-neweac,google-workspace-migration-prerequisites,perform-g-suite-migration. - RMM install one-liner (BirthBio site):
irm https://rmm.azcomputerguru.com/install/BRIGHT-PEAK-5980/windows | iex. - Discord DMs to Mike: message_id 1520034139900739627 (initial DWD), 1520055625302675537 (corrected 5-scope).
- Vault enrollment key:
clients/birth-biologic/gururmm-site-main(site BRIGHT-PEAK-5980, id 3b20ef97-…).
Update: 04:42 PT (2026-06-27) — Datto->SharePoint delta completion, Quality recovery, April-vs-now reconcile, ticket #32187 billed
Continuation of the same session. Covers the SharePoint side: completing the additive delta, recovering an accidentally-deleted Quality site, reconciling SharePoint to match Datto (source of truth), freezing the Datto source, and updating/billing the Datto migration ticket.
Session Summary (update)
After ACG-DWP-X-BB finished re-syncing with Datto cloud, ran the Datto -> SharePoint delta. The April
SPMT run was additive and never re-synchronized, so the delta only needed to add files that had never
transferred. Built delta-recon-v2.ps1 (sanitize-aware reconcile: matches on both raw and sanitized
paths to find GENUINELY_MISSING files) and delta-upload-v3.ps1 (simple-PUT for <=244MB auto-creating
parent folders, EnsureFolder + chunked for larger, FileShare.ReadWrite shared reads for Datto-locked
files, long-path \\?\ for [IO.File] reads, SanRemote trim of leading/trailing spaces + trailing dots).
Reconciled to 0 missing across Supply Management, Admin, Birth Biologic Activity Reports, Donor
Services (107 GB / ~57K files), and Quality. Renamed 19 Datto source files to match SharePoint (stripped
leading/trailing spaces + trailing dots).
Quality site recovery. The Quality Department SharePoint site was deleted 6/26. Unified audit log
showed operations@ deleted the connected M365 Group, which cascaded (AAD -> SharePoint sync) to remove
the site. Restored from the SharePoint deleted-site recycle bin (cert-based app token; SP REST rejects
app-only tokens). Since Quality is being reorganized into the Quality Systems Department (QSD) site,
relocated the migrated Quality content there via server-side copy, then filled 44 missing + 3 file-lock
stragglers. Old /sites/QualityDepartment auto-purges ~7/26.
April-vs-now divergence + mirror. Because the April push was additive (not a sync), anything deleted,
moved, or changed in Datto after April was stale in SharePoint. Treating Datto as source of truth, built
a consolidated change-list (consolidated_changelist.csv): 1,583 deleted/moved + 161 modified (~1,744
differences). Cross-checked the SP unified audit log to find files users had created/edited directly in
SharePoint (operations@, ksteen, jbeck, etc. on live sites) and flagged 11 to protect. Ran
mirror-execute.ps1 (re-validates each row against a frozen Datto set, DELETEs stale by path-addressed
Graph DELETE -> recycle bin, refreshes modified via PUT, skips protected): deleted=1,564, refreshed=160,
protected-skip=11, fail=0. For the 1 modified protected file, pushed the Datto version beside the user's
edit as "...Datto Copy.docx"; the 10 deleted/moved protected are SP-only (no Datto copy) -> left as-is.
Froze the source. Stopped + disabled the Datto Workplace Server service on ACG-DWP-X-BB so the source no longer changes (also resolves the "reappearing files" complaint by removing the stale SP copies).
Ticket #32187 (Datto, Syncro 109277420). Posted a highly-detailed public+email completion/remediation note and billed 5.0h Labor - Remote Business ($150/hr = $750). Posted #bot-alerts notifications.
Key Decisions (update)
- Datto = source of truth for the reconcile; SharePoint mirrored to it. Deletes go to the SP recycle bin (recoverable 93 days), never hard-deleted.
- Protect user-touched SP files — never overwrite/delete the 11 flagged via audit log; for the one edited file, keep both (Datto pushed as "Datto Copy") rather than overwrite.
- Relocate Quality content to QSD rather than rebuild under the restored old site, matching the planned reorg; let the old site auto-purge.
- Root-cause correction (Mike): the "reappearing" files were NOT a Datto resurrection. They were stale SP copies sitting in SharePoint since the April additive push (files deleted from Datto after April were never removed from SP). Rewrote the ticket note's root-cause section accordingly.
Problems Encountered (update)
- SP REST "Unsupported app only token" -> SharePoint requires a cert-based token; granted Sites.FullControl.All and used a client_assertion JWT (x5t = cert thumbprint b64url). Fixed.
- Chunked-upload 400 into brand-new folders -> switched to simple-PUT (auto-creates parents). Fixed.
- Long-path SKIP-nofile ->
\\?\prefix for [IO.File] reads (not for Rename-Item/File.Move in PS5.1). - Filename 400s = leading/trailing spaces / trailing dots -> SanRemote trim; renamed 19 source files.
- Datto file-locks -> FileShare.ReadWrite shared read. Fixed.
- Background poller broke (curl --data-binary @file errored each iteration due to $0-relative temp paths under run_in_background) -> read the mirror log directly instead. Logged as friction.
- bot-alert missing link -> first #bot-alerts post for #32187 omitted the mandated
-> <link>tail; the helper posts text verbatim and does not auto-append. Reposted correctly + logged friction.
Configuration Changes (update)
- Created on ACG-DWP-X-BB / scratchpad:
delta-recon-v2.ps1,delta-upload-v3.ps1,mirror-execute.ps1,consolidated_changelist.csv, divergence CSVs,mirror-execute.log. - Stopped + disabled Datto Workplace Server service on ACG-DWP-X-BB.
- Renamed 19 Datto source files (whitespace/trailing-dot cleanup) under
C:\Users\Public\Desktop\Datto Workplace Server Projects.
Pending / Incomplete Tasks (update)
- Mail: MX cutover still pending (Batch 1 complete). Then authorize Workspace write scopes (apps.licensing + admin.directory.user + Licensing API), unlicense migrated Google users, run Batch 2.
- SP-only user files (Shift Coms / DEMO and similar) — decide whether to fold into Datto.
- Old
/sites/QualityDepartmentauto-purges ~7/26 (no action needed).
Reference Information (update)
- Datto migration ticket: #32187 (Syncro id 109277420). Comment id 420992239 (public+email); line item id 43043687 (5.0h Labor - Remote Business, product 1190473, $150).
- #bot-alerts: message_id 1520266361996316802 (corrected, with link).
- SP site IDs — Donor:
birthbiologic.sharepoint.com,bcbfa272-dc85-424c-af66-3f14c75ffeb4,8b0975dd-...; Admin:...,1baf65c1-c4b3-4602-9111-1f99ae800023,...; Supply:...,4700ecf3-25ba-41b6-918c-9fe620038172,...; QSD:...,3173c017-58bd-406a-8858-2c969667336f,.... - Tenant 19a568e8-9e88-413b-9341-cbc224b39145; Graph app client 709e6eed-0711-4875-9c44-2d3518c47063.