Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gws-mdm-enablement

Headless Google Workspace MDM-security tenant enablement. A series of management API calls — packaged as Terraform + small TypeScript scripts — that turn on every Workspace function relevant to mobile device management (telemetry and response actions) and produce a verified handoff packet so a downstream technical person can connect their apparatus without touching admin.google.com.

What this is. The rail laying — APIs enabled, DWD authorizations registered, Pub/Sub topics provisioned, Workspace Events subscriptions stood up, IAM bindings granted to a separately-owned apparatus, scope verification probed live, handoff packet emitted.

What this is not. The apparatus itself. No collector, no forwarder, no downstream-SIEM wiring. The apparatus team consumes the handoff packet and connects from their side.

Architecture

                 ┌──────────────────────────────────────────────┐
                 │              Workspace tenant                │
                 │  (Admin SDK / Cloud Identity / Alert Center  │
                 │   / Workspace Events / Chrome Management)    │
                 └──────────────────────┬───────────────────────┘
                                        │
                       ┌────────────────┴────────────────┐
                       │      THIS repo (Terraform +     │
                       │        TS scripts)              │
                       │                                 │
                       │ • Enables Cloud APIs            │
                       │ • Registers DWD scope sets via  │
                       │   DWD-API for apparatus client  │
                       │ • Provisions Pub/Sub topics     │
                       │ • Creates Workspace Events subs │
                       │ • Sets up audit log → Pub/Sub   │
                       │ • Probes scopes live            │
                       │ • Emits handoff.json + .md      │
                       └────────────────┬────────────────┘
                                        │ handoff packet
                                        ▼
                       ┌─────────────────────────────────┐
                       │   Apparatus (apparatus-side     │
                       │   GCP — NOT in this repo)       │
                       │                                 │
                       │ • Reads Pub/Sub subscriptions   │
                       │ • Polls Reports / Alert / etc   │
                       │ • Calls action endpoints with   │
                       │   writer DWD                    │
                       │ • Forwards to its SIEM          │
                       └─────────────────────────────────┘

Prerequisites

  • gcloud authenticated as someone with Workspace super-admin + GCP org admin (one user, the MDR engineer)
  • A free GCP project ID to bootstrap, an org ID, and a billing account
  • The apparatus team's OAuth 2.0 client ID for their reader SA (21-digit unique_id field). Optionally a second client ID for their writer SA, plus a subscriber SA email
  • Terraform ≥ 1.6
  • Bun ≥ 1.1

Runbook — fully headless

The one OAuth consent screen you encounter (gcloud auth login) is Google's standard sign-in flow, NOT admin.google.com. See Residual UI.

# 1. Bootstrap GCP project (one-shot — cannot be Terraform)
PROJECT_ID=pai-gws-mdm-enablement \
ORG_ID=123456789012 \
BILLING_ID=000000-AAAAAA-BBBBBB \
./scripts/bootstrap-gcp.sh

# 2. Apply Terraform — enables APIs, provisions Pub/Sub topics + IAM,
#    creates Workspace Events subscriptions (skipping unsupported types).
cd terraform
cp terraform.tfvars.example terraform.tfvars
$EDITOR terraform.tfvars     # fill in customer ID, super-admin, apparatus client IDs
terraform init
terraform apply
cd ..

# 3. Register DWD scope sets against the apparatus's OAuth client ID(s).
#    Idempotent — re-running adds any missing scopes without duplicating.
cp .env.example .env
$EDITOR .env                 # fill in the same values from terraform.tfvars
bun run register-dwd

# 4. Probe scopes live. Writes handoff/probe-report.json.
#    REQUIRES test apparatus SA identity (see .env TEST_APPARATUS_*).
#    In production: apparatus team runs this from their side.
bun run verify-scopes

# 5. Emit the handoff packet for the apparatus team.
bun run emit-handoff

# Or fold steps 3-5 into one:
bun run enable-end-to-end

After step 5, ship handoff/handoff.json and handoff/handoff.md to the apparatus team. That is the entire handoff.

What the apparatus subscribes to

Family Topic Carries Source path
mobile workspace-events-mobile Mobile device events (Android, iOS) Workspace Events when covered; otherwise apparatus polls Reports API applicationName=mobile
chrome workspace-events-chrome ChromeOS telemetry + events Workspace Events when covered; otherwise apparatus polls Chrome Management telemetry/events
alerts workspace-events-alerts Alert Center pre-correlated alerts Apparatus polls Alert Center; topic carries any forwarded copies
audit workspace-events-audit Workspace audit log export (login, token, admin, user_accounts, drive, …) Cloud Logging sink (configured in this repo) → Pub/Sub
deadletter workspace-events-deadletter Failed deliveries (any topic, 30 day retention) Pub/Sub native

The apparatus's subscriber SA, if provided in terraform.tfvars, already has roles/pubsub.subscriber on every family topic.

Reader scope set (registered via DWD)

https://www.googleapis.com/auth/admin.directory.device.mobile.readonly
https://www.googleapis.com/auth/admin.directory.user.readonly
https://www.googleapis.com/auth/admin.directory.group.readonly
https://www.googleapis.com/auth/admin.directory.orgunit.readonly
https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly
https://www.googleapis.com/auth/admin.reports.audit.readonly
https://www.googleapis.com/auth/admin.reports.usage.readonly
https://www.googleapis.com/auth/apps.alerts
https://www.googleapis.com/auth/cloud-identity.devices.readonly
https://www.googleapis.com/auth/chrome.management.telemetry.readonly
https://www.googleapis.com/auth/chrome.management.policy.readonly

Writer scope set (registered via DWD)

https://www.googleapis.com/auth/admin.directory.device.mobile.action
https://www.googleapis.com/auth/cloud-identity.devices
https://www.googleapis.com/auth/admin.directory.user.security
https://www.googleapis.com/auth/chrome.management.policy

Endpoint catalog (apparatus reference)

Endpoint Method Scope Purpose
admin.googleapis.com/admin/directory/v1/customer/{id}/devices/mobile GET admin.directory.device.mobile.readonly Mobile inventory
.../devices/mobile/{resourceId}/action POST admin.directory.device.mobile.action block / wipe / approve / cancel
cloudidentity.googleapis.com/v1/devices LIST cloud-identity.devices.readonly Modern device inventory (work-profile, BYOD, ChromeOS)
cloudidentity.googleapis.com/v1/devices/{name}:wipe POST cloud-identity.devices Full wipe
cloudidentity.googleapis.com/v1/devices/{name}/deviceUsers/{userId}:block POST cloud-identity.devices Per-user-on-device block
admin.googleapis.com/admin/reports/v1/activity/users/all/applications/{app} GET admin.reports.audit.readonly Audit stream per app (mobile, login, token, admin, user_accounts, drive, chrome, context_aware_access, saml, groups_enterprise)
alertcenter.googleapis.com/v1beta1/alerts LIST apps.alerts Curated security alerts
chromemanagement.googleapis.com/v1/customers/{id}/telemetry/devices LIST chrome.management.telemetry.readonly ChromeOS device telemetry
chromemanagement.googleapis.com/v1/customers/{id}/telemetry/events LIST chrome.management.telemetry.readonly ChromeOS event stream
chromepolicy.googleapis.com/v1/customers/{id}/policies/orgunits:batchModify POST chrome.management.policy Chrome policy mutation
admin.googleapis.com/admin/directory/v1/users/{userKey}/signOut POST admin.directory.user.security Sign user out everywhere
workspaceevents.googleapis.com/v1/subscriptions LIST (apparatus reads via subscriber SA) View / verify push subscriptions

(Sample curl per endpoint generated into handoff/handoff.md by emit-handoff.)

Handoff packet

Two files produced under handoff/:

  • handoff.json — machine-readable: customer ID, sub, scope sets, topic ARNs, endpoint catalog, probe-report status
  • handoff.md — human-readable: same content, plus a sample curl per endpoint pre-filled with the customer's tenant context

Ship both. The apparatus team needs nothing else from us.

Residual UI verdict

Scenario admin.google.com clicks Other UI
DWD-API available (happy path) 0 one gcloud auth login OAuth consent (Google sign-in, not admin.google.com)
DWD-API returns 404 on your tenant (register-dwd fails fast and tells you) one ~2 min visit at setup time — Security → API controls → Domain-wide delegation → paste each client ID + scope CSV same OAuth consent
Cloud Identity Premium not licensed impossible to fix in API or admin console — license question (call Google)

The script register-dwd checks DWD-API availability with its first request and prints the fallback URL inline if it 404s — no silent failure.

Operational rules honored

  • bun/bunx only (no npm/npx anywhere in scripts or package.json)
  • TypeScript only (no Python anywhere)
  • Markdown only (no XML, no HTML for content)
  • No web server runtime in this repo (no Hono, no Express)
  • Nothing in this repo writes to admin.google.com; the only place that string appears is in this README's Residual UI section, where it must

Repository layout

gws-mdm-enablement/
├── package.json
├── bunfig.toml
├── tsconfig.json
├── .env.example
├── README.md                    # this file
├── handoff/                     # gitignored — emit-handoff output lands here
├── scripts/
│   └── bootstrap-gcp.sh         # one-shot project + billing setup
├── src/
│   ├── auth/
│   │   └── dwd.ts               # DWD JWT mint + probeScopes
│   ├── lib/
│   │   ├── env.ts
│   │   └── logger.ts
│   └── scripts/
│       ├── register-dwd.ts      # POST DWD-API to register scope sets
│       ├── verify-scopes.ts     # mint + probe each scope live
│       └── emit-handoff.ts      # write handoff/handoff.{json,md}
└── terraform/
    ├── versions.tf
    ├── main.tf                  # provider + locals
    ├── variables.tf
    ├── apis.tf                  # google_project_service for every API
    ├── pubsub.tf                # topics + IAM + audit log sink
    ├── workspace_events.tf      # subscriptions with apply-time probe
    ├── iam.tf
    ├── outputs.tf
    ├── probe-event-types.sh     # apply-time eventTypes probe
    └── terraform.tfvars.example

Reusing per customer

The Terraform module is parameterized. Adding a new customer is one new terraform.tfvars (different customer ID, super-admin, apparatus client IDs), one terraform init + apply in a fresh state, one bun run enable-end-to-end. No code changes.

About

Headless Google Workspace MDM-security tenant enablement: Terraform + TypeScript scripts that turn on tenant APIs, register apparatus DWD authorizations, provision Pub/Sub rails, and emit a verified handoff packet for a downstream apparatus.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages