# 62 Config-as-code with reconciliation

> v0.1.3 · role: Prevent · [policy: #27 · #34, #32](https://docs.google.com/spreadsheets/d/1nOztaPd1Y7eNeRSR_hdovYy-ncpx-bAx/edit?usp=sharing&ouid=115159875779023172526&rtpof=true&sd=true)

The tenant's security settings are kept as desired state in a reviewed git repository — GAM batch files, Policy API JSON — and a scheduled reconciler fetches live state, diffs it against the repo, and either reports or converges. A delta git did not author is either an outage or an intruder. The reconciler runs as a service account bound to a custom role scoped to exactly the settings it manages — never Super Admin for Admin SDK work. Policy API reads are the exception: only a super administrator can use the Cloud Identity Policy API, so that portion needs a separately scoped super-admin credential.

## Caveats

- Not every Workspace setting is exposed by an API — the repo is a partial truth, and the gaps must be documented rather than assumed covered.
- The reconciler is itself a top-tier persistence target — a converge-mode reconciler with a domain-wide delegation service account means whoever owns the repo owns the tenant.
- Converging before the dry-runs are clean will enforce your mistake tenant-wide — stay in report-only until a clean streak, then converge.

## Setup steps

1. Put the desired-state config in git: GAM batch files and/or Policy API JSON for 2SV enforcement, OAuth allowlist, sharing settings, routing, roles.

   - **One repo, reviewed PRs, tagged releases** = the source of truth

   docs: [Policy API overview](https://docs.cloud.google.com/identity/docs/concepts/overview-policies)

2. Create a custom admin role scoped to exactly the settings the reconciler manages, and bind it to the reconciler's service account. — `Account › Admin roles`

   Custom role = only the privileges under management; never Super Admin

   docs: [Assign specific admin roles](https://knowledge.workspace.google.com/admin/users/assign-specific-admin-roles) · [Create, edit, and delete custom admin roles](https://knowledge.workspace.google.com/admin/users/create-edit-and-delete-custom-admin-roles)

3. The reconciler calls the Admin SDK as ITSELF: a service account can hold an admin role directly, which is what makes step 2's custom role effective. Keep the Admin SDK service account out of domain-wide delegation — DWD grants access to all users' data — and confirm its client id is absent from the DWD list. Exception: Cloud Identity Policy API reads require super-admin access via domain-wide delegation, so keep that credential separate from the Admin SDK reconciler. — `Security › Access and data control › API controls › Manage Domain Wide Delegation`

   DWD list: Admin SDK reconciler's client id = absent (it authenticates as itself with the role from step 2); Policy API reads use a separate super-admin DWD credential

   docs: [Control API access with domain-wide delegation](https://knowledge.workspace.google.com/admin/apps/control-api-access-with-domain-wide-delegation)

4. Run the reconciler on a schedule: fetch live state, diff against the repo, and either report or converge.

   Mode = report-only first; converge only after a clean dry-run streak

   docs: [Listing and getting policies](https://docs.cloud.google.com/identity/docs/how-to/list-get-policies)

5. Fail the pipeline on any live-state delta that git did not author — that delta is either an outage or an intruder.

## Ongoing maintenance

- **[automatable: script]** Per schedule: run the reconciler and commit the state.
- **[automatable: AI agent]** Per diff: review whether it is sanctioned change or drift.

## How to verify

1. Run the reconciler in dry-run mode against the live tenant — zero diff means the code matches reality; any diff is either drift or an undocumented change.

## Settings screens

- Security > Access and data control > API controls > Manage Domain Wide Delegation (confirm the reconciler is NOT delegated for Admin SDK work — it authenticates as itself)
  - console: https://admin.google.com/ac/owl/domainwidedelegation
  - screenshot: ../screenshots/admin.google.com/ac/owl/domainwidedelegation.png
- Account > Admin roles (least-privilege custom role for the reconciler)
  - console: https://admin.google.com/ac/list/roles
  - screenshot: ../screenshots/admin.google.com/ac/list/roles.png
