SysEngineering

Multi-Tenant Google Group Inventory

group_inventory_job.py is the read-only Cloud Run Job companion to the Google Workspace Admin Assistant and HR distro check. It reuses the assistant’s existing tenant routes, per-tenant service-account secrets, and domain-wide delegation.

It does not change Google Groups. It reads direct membership and roles, writes actual state to columns A:B in each email-named extraction tab, and compares that membership with HR Check column C. It also publishes two long-form tabs:

Each group tab ends its Google inventory in column A with a Last updated on bold timestamp in Eastern time after two blank rows. Column C independently ends with the HR sync’s own timestamp using the same spacing and emphasis, making the freshness of both sources visible.

If any group or tenant fails, the job exits nonzero and changes no sheet output. Writing is disabled by default.

Credential separation

This keeps central-sheet write access separate from tenant Directory access.

Configuration

The job uses the assistant’s existing variables:

Job-specific variables:

Variable Required Purpose
DESTINATION_SPREADSHEET_ID Yes Extraction workbook ID.
GROUP_INVENTORY_WRITE_ENABLED No Defaults to false. Set true only after dry-run review.
GROUP_INVENTORY_INCLUDE_DERIVED No Defaults to false, matching direct Admin Console membership.
GROUP_TARGETS_JSON For writes Explicit groups. Required in write mode; dry runs may otherwise discover every email-named tab.
LOG_LEVEL No Defaults to INFO.

Optional explicit target examples:

[
  "staff@example.org",
  {
    "group": "employees@other.org",
    "workspace": "other-prod"
  }
]

Use the explicit workspace override only when the group-address domain does not uniquely resolve through the existing route table.

Automatic discovery is allowed only in dry-run mode and inventories every email-named tab, including tabs the HR sync intentionally ignores. Publishing always requires GROUP_TARGETS_JSON, preventing a new or legacy tab from silently entering the write scope. Reconciliation is against the post-HR-sync column C values, not directly against the Active EE tab, so schedule this job after the HR sync.

First dry run

Deploy this as a separate job in the existing gw-admin-assistant project. Use the same runtime service account and route-secret environment as the assistant, plus the destination spreadsheet ID. Start with only the 11th Hour and Hyperion groups and keep writes disabled. From this integration directory:

First, share the extraction workbook as Editor with 1039144753775-compute@developer.gserviceaccount.com. Viewer is sufficient for the dry run, but Editor is required for the later A:B pilot; granting Editor once avoids a second permissions change.

Copy-Item group_inventory_pilot.env.yaml.example group_inventory_pilot.env.yaml
# Replace REPLACE_WITH_EXTRACTION_SPREADSHEET_ID in the copied file.

gcloud.cmd run jobs deploy google-workspace-group-inventory `
  --project gw-admin-assistant `
  --region us-central1 `
  --source . `
  --command python `
  --args group_inventory_job.py `
  --service-account 1039144753775-compute@developer.gserviceaccount.com `
  --env-vars-file group_inventory_pilot.env.yaml

Preserve the assistant’s GW_ROUTE_SA_SECRET_OVERRIDES value if the target list includes its sandbox route. Then execute and inspect logs:

gcloud.cmd run jobs execute google-workspace-group-inventory `
  --project gw-admin-assistant `
  --region us-central1 `
  --wait

The dry-run summary contains counts only; it does not log the membership roster. Resolve every tenant/group error before enabling publishing.

HR-facing sync status

Published runs maintain a _GW Status dashboard with one row per managed group tab. It records the latest attempt, last successful refresh, Google and HR counts, difference count, and a short actionable status. Managed group tabs use the same health colors:

On a partial tenant failure, successful group tabs are updated atomically while the failed tab’s A:B values and prior success metadata are preserved. The status dashboard and tab colors are still updated, and the Cloud Run execution exits non-zero so operations can alert on the degraded run.

Production schedule

After a clean dry run:

  1. Set GROUP_INVENTORY_WRITE_ENABLED=true.
  2. Execute once manually and review A:B on the group tabs plus both output tabs. Column C must remain unchanged.
  3. Add a Cloud Scheduler trigger for Monday after the HR Apps Script run. Use a separate time window (for example, 10:00 AM Eastern) so the HR and Google inventory writers never overlap.
  4. Alert on nonzero Cloud Run Job executions.

Do not add Directory write scopes or group mutation calls to this job. Any future enforcement workflow needs a separate approval and design review.