E. Access & Resource Governance
Wasteology now has a company-wide Azure governance standard (lat.md/cloud-governance.md) that defines the authoritative model for who can access what, how grants are made, and how changes are tracked. This section translates that standard into concrete day-one guidance for new developers and a clear decision framework for the product owner and COO — including the two-layer access model, role tiers, sanctioned tooling, when to engage ABS, and the ongoing review cadence.
The goal is a single reference every developer and future PO can consult when granting, requesting, or reviewing access — so that least-privilege defaults are established on day one and access does not accumulate silently over time.
1. The Two-Layer Access Model
Current state
Access to Wasteology resources is controlled at two independent layers. Historically these were managed ad hoc — some developers had broad portal access, and database role assignments were made directly in psql without a record. Both layers must be checked when auditing "who has access," because a principal can hold one without the other.
Recommended standard
Treat the two layers as distinct and govern them with different tools:
| Layer | What it controls | Governed by | Tooling |
|---|---|---|---|
| Control-plane (Azure RBAC) | Who can view, manage, or modify Azure resources (subscriptions, resource groups, apps, key vaults) | User Access Administrator or Owner role holders | ops azure access [--sub] [--resource] [--principal] |
| Data-plane (database) | Who can connect to a database and read/write rows or manage objects | Database role assignments (Postgres) or AAD admin grants | ops db CLI or wdp-palantiri DB Access Manager (page 07) |
How it works
Control-plane grants are inherited top-down: a role at subscription scope covers every resource group and resource beneath it. Narrower scope is always preferred — grant at the resource or resource-group level, not the subscription, unless the principal genuinely needs subscription-wide access.
Data-plane grants are independent of RBAC. For AAD-enabled Postgres servers (pg-wasteology, app-wasteology-prod, wg-pg-orbit-prod), a developer's Azure AD identity can be used as the login — but they still need a database role grant before they can read or write any rows. The two password-only servers (billing-modernization-dev/prd) are not governed by AAD at all and are managed via native Postgres credentials stored in Key Vault.
Why both matter: A developer with Owner on the Production subscription can still be locked out of prod database rows if they have no role grant in Postgres. Conversely, a database role grant does not give someone the ability to manage the Azure Container App that runs wdp-palantiri — that requires RBAC. Audits must check both.
The current model relies on kgray as the sole approver for both layers in most cases. As the team grows, decide whether TJ Lewis (tlewis) should be elevated to a co-approver on the DBACCESS_MUTATION_ALLOWLIST (currently Kent Gray,TJ Lewis in the deployed environment) — and whether a second UAA in the prefect-resources RG is warranted. The governance standard recommends monthly access reviews; the PO should confirm who runs this review during the transition.
2. Access Tiers
Current state
Three informal tiers exist in practice (read-only analyst, developer with write, platform admin) but they have not been formally defined or consistently applied. The ops db CLI introduced a four-tier model for Postgres in 2026-Q1; Azure RBAC assignments have been made on a per-case basis.
Recommended standard
Define three access tiers that map to both layers. Assign every user to the narrowest tier that covers their role.
Tier Definitions
| Tier | Azure RBAC role | DB role(s) | Grants |
|---|---|---|---|
| Read | Reader (specific RG or resource) | wasteology_readonly + explicit grant-schema per schema needed | SELECT on granted schemas; read the Azure resource in portal and ops azure |
| Write | Contributor (scoped to specific RG) | wasteology_readonly + grant-schema --access readwrite on specific schemas | INSERT/UPDATE/DELETE on granted schemas; deploy container apps, push images, run pipelines within scope |
| Admin | Contributor (broader) + User Access Administrator (specific RGs only, via ABS/CGreco) | wasteology_admin role membership + azure_pg_admin (where needed) | ALL PRIVILEGES on all tables/sequences/schemas; can manage roles, extensions, all non-system DB objects; can assign Azure roles within their UAA scope |
Who Approves What
| Role requested | Approver | Method |
|---|---|---|
| Read — DB (dev) | kgray or tlewis | ops db create-user or DB Access Manager page 07 |
| Read — DB (prod) | kgray | ops db create-user --conn WASTE_PG |
| Read — Azure RBAC | ABS / CGreco / ashull | Request via ABS (see Section 5); kgray can approve within Prod prefect-resources RG only |
| Write — DB | kgray | ops db grant-schema --access readwrite |
| Write — Azure RBAC (scoped RG) | ABS / CGreco / ashull | ABS request; kgray can approve within Prod prefect-resources RG |
Admin — DB (wasteology_admin) | kgray | ops db add-to-role --role wasteology_admin |
Admin — DB (azure_pg_admin) | kgray | ops db add-to-role --role azure_pg_admin; treat as superuser-equivalent — grant deliberately |
| Admin — Azure RBAC (Owner / UAA) | ABS / CGreco | ABS request; never self-grant |
| New subscription / tenant-level role | ABS | ABS request only |
Postgres Role Reference
| Postgres role | Access level | Who gets it |
|---|---|---|
wasteology_readonly | SELECT on wasteology_ops schema only | Default for all read-tier users; other schemas require explicit grant-schema |
wasteology_admin | ALL PRIVILEGES on all tables/sequences/schemas | Senior devs with write access to shared schemas |
azure_pg_admin | Postgres superuser equivalent (manage roles, extensions, all objects) | Platform admin only; grant deliberately |
Schema owner | DROP rights via object ownership transfer | Explicitly scoped; only kgray/tlewis via DBACCESS_OWNER_ALLOWLIST |
Decide whether new developers default to read-tier (SELECT only, schemas granted on request) or write-tier (DML on dev DB). The current recommendation is read-tier by default with a fast-path to write on dev — this limits blast radius during onboarding while keeping developers unblocked. Prod write access requires explicit approval in all cases.
3. New-Developer Onboarding Checklist
Current state
No formal onboarding checklist exists. Access has been granted reactively — developers request what they need, and grants accumulate. There is no standard day-one set of access nor a formal offboarding revoke process.
Recommended standard
Apply the checklist below on every new-developer start. The platform owner (kgray) is the approver for all steps unless noted.
Day-One Access (all new developers)
- ADO membership: Add to the
wasteologyADO organization (project:Wasteology). Grants access to all ADO repos. Done in ADO Organization Settings → Users. - Repo access (ADO): Confirm the developer can clone and push to their assigned repos. Default:
wg-orchestration,wdp-palantiri, and any project-specific repos. Branch policies are enforced at the PR level. - Dev DB — read grant: Run
ops db create-user <email-prefix> --schema wasteology_ops --access read(targetsWASTE_PG_DEVby default). This creates the user and adds them towasteology_readonly. Grant additional schemas as needed withops db grant-schema. - Dev DB — dev write (if applicable): Run
ops db grant-schema <user> --schema <target_schema> --access readwritefor schemas the developer will actively write to in dev. Do not grant readwrite on prod schemas on day one. - Key Vault / secrets: Provide access to
wg-orchestration-kvsecrets via the.envfile shared via Bitwarden (or the team vault, not email). The developer does not need Azure RBAC on the Key Vault itself unless they are running infra CI pipelines. - wdp-palantiri (dashboard): No RBAC required — the dashboard is open internally. If the developer needs to apply DB mutations via the UI, add their
displayNametoDBACCESS_MUTATION_ALLOWLISTin the container app environment. - Prefect Cloud: Invite to the Prefect Cloud workspace as
Editor(for flow deployment) orViewer(for monitoring only). Done in Prefect Cloud → Members. - dbt Cloud (if applicable): Invite via dbt Cloud → Account Settings → Users. Set their connection string parameter to
options=-c wdp.mart_schema=dbt_{username}and add a row tolat.md/database-schemas.md#dbt_{username} — Personal Developer Schemas. - Least-privilege default: Do not grant
wasteology_admin,azure_pg_admin, or any Azure RBACContributor/Owneron day one. Escalate only when a specific task requires it.
Standard Access NOT Granted on Day One
The following require explicit justification and approval:
- Prod database write access (
--conn WASTE_PG+--access readwrite) wasteology_adminorazure_pg_adminrole membership- Azure RBAC
Contributoror higher on any resource group - Access to BINS password-only servers (
billing-modernization-dev/prd) — credentials from Bitwarden "BINS Engagement" vault, by request - Orbit DB (
wg-pg-orbit-prod) — AAD-only server, requires ABS for subscription-level RBAC
Offboarding (same day as departure)
- Revoke DB roles:
ops db remove-from-role <user> --role <role>for each role. Runops db grants <user>first to enumerate all current grants. - Revoke DB schema grants:
ops db grant-schema <user> --schema <s> --access noneor equivalent revoke for each schema. - Remove from ADO: Remove from the
wasteologyADO organization. - Remove from Prefect Cloud: Revoke workspace membership.
- Remove from dbt Cloud: Revoke via dbt Cloud UI.
- Rotate any shared credentials the user knew: If the developer had access to any service account passwords or Key Vault secrets that are not user-scoped (e.g.,
SLING_WASTE_PG_PASSWORDfor a shared service account), rotate those secrets and updatewg-orchestration-kvand~/.sling/.envon all machines. - Audit RBAC: Run
ops azure access --principal <upn>across all three subscriptions to confirm no residual RBAC assignments. - Record in audit log: The
wg_digests.dbaccess_audit_logtable captures allops dbmutations automatically. Manual RBAC revokes should be noted in the git history oflat.md/cloud-governance.md.
The offboarding rotation step for shared credentials is currently manual. If the team grows beyond 4–5 developers sharing a Postgres service-account password, consider switching all service accounts to AAD-only auth (managed identities or per-user AAD login) to eliminate the rotation burden on offboarding. wg-pg-orbit-prod already uses AAD-only; this would extend that pattern to pg-wasteology.
4. How to Grant Access (Sanctioned Tooling)
Current state
Grants have been made via psql directly, via the Azure portal, and via az CLI — with no consistent audit trail. The DB Access Manager (page 07) and ops db CLI were built to consolidate this.
Recommended standard
Use only the sanctioned paths below. Portal-direct grants are not sanctioned because they bypass the audit trail.
Postgres — ops db CLI
# Create a new user and add to wasteology_readonly (read-tier onboarding)
ops db create-user <email-prefix> --schema wasteology_ops --access read
# Grant additional schema to an existing user
ops db grant-schema <user> --schema <schema> --access read|readwrite
# Add to a role (e.g., wasteology_admin or azure_pg_admin)
ops db add-to-role <user> --role wasteology_admin
ops db add-to-role <user> --role azure_pg_admin --conn WASTE_PG_DEV
# Remove from a role (offboarding / privilege reduction)
ops db remove-from-role <user> --role wasteology_admin
# Inspect current state
ops db grants <user> # all table-level privileges for a user
ops db roles # list roles and members
ops db audit-roles # find schemas missing baseline grants
Default target is WASTE_PG_DEV (dev). Always pass --conn WASTE_PG for production Postgres.
The DB Access Manager (wdp-palantiri page 07) provides the same operations with a visual access matrix, dry-run preview, typed confirmation, and automatic write to wg_digests.dbaccess_audit_log. Use the CLI for scripting and automation; use the page for interactive reviews and one-off changes.
Azure RBAC
Azure RBAC assignments must be made by a User Access Administrator or Owner. The people who currently hold those roles:
| Person | Role | Scope |
|---|---|---|
| ABS (external partner) | User Access Administrator | All three subscriptions |
| CGreco / CGreco-a | User Access Administrator | All three subscriptions |
| ashull | Owner | All three subscriptions |
| kgray | User Access Administrator | Prod prefect-resources RG only |
For any grant that kgray cannot make unilaterally (anything outside Prod prefect-resources RG), request from ABS or ashull using the template in Section 5.
Always specify the narrowest scope that works: resource > resource group > subscription. Avoid subscription-wide grants unless the developer genuinely needs access to all resources in that subscription.
Audit Trail
All DB grants and revokes via ops db or the DB Access Manager are recorded in wg_digests.dbaccess_audit_log (one row per SQL statement, with actor, operation, target user/schema/role, access level, DB env, SQL executed, success flag, and timestamp). Azure RBAC changes are recorded in Azure's native Activity Log, queryable via ops azure access. Provisioning decisions (new resources, RBAC grants) should also be recorded as git-reviewed edits to lat.md/cloud-governance.md.
5. The ABS Resource-Request Process
Current state
No formal template or escalation path exists for tenant-level or subscription-level requests. Requests have been made ad hoc via email or Teams message.
Recommended standard
Maintain a clear boundary between what the team self-serves and what requires ABS engagement.
What the Team Can Self-Serve
| Action | Tooling |
|---|---|
| Postgres user creation and schema grants (dev + prod) | ops db CLI |
| Postgres role membership changes | ops db CLI |
Container App and supporting resource deployment into wdp-palantiri-rg (Prod sub) | /infra Bicep (infra/) via ADO pipeline or az deployment group what-if/create |
Azure RBAC within Prod prefect-resources RG | kgray directly (UAA on that RG) |
| Image push to ACRs, Container App image updates | az containerapp update or build scripts |
| Prefect Cloud workspace membership | Prefect Cloud UI (any Editor) |
When to Request via ABS
Engage ABS for any of the following:
- New Azure subscription — any new subscription under the Wasteology tenant
- New resource group in Dev or Prod subscription — ABS or ashull must create it and assign initial RBAC
- Subscription-wide RBAC grants — e.g., giving a developer Reader on the entire Prod subscription
- Tenant-level role assignments — e.g., Global Admin, Directory Reader
- App registration / service principal creation — the tenant has a directory restriction that blocks app registrations; all non-human identities must be managed identities (no SP registration). If an SP is unavoidable, ABS must request via the tenant admin.
- Managed identity creation outside
wdp-palantiri-rg— managed identities in other RGs or subscriptions require ABS for initial provisioning - Cleanup of stale SPs or expired credentials — ABS holds the permissions to hard-delete app registrations
- Cost Management reader grants at subscription scope — needed to give the ADO CI identity (
wg-azure-infra-identity) full snapshot capability; currently pending ABS grant
Information to Include in Every ABS Request
Submit requests to ABS (and CC ashull) with the following fields filled in. Incomplete requests will be returned.
ABS Access / Resource Request Template
Subject: Wasteology Azure Request — [brief description]
Requestor: [name + email]
Date needed by: [YYYY-MM-DD]
Urgency: [routine | blocking a deploy | security remediation]
--- REQUEST TYPE (check one) ---
[ ] New subscription
[ ] New resource group
[ ] RBAC role assignment
[ ] Managed identity creation
[ ] SP registration (explain why managed identity is insufficient)
[ ] Credential rotation / cleanup
[ ] Tenant-level role assignment
[ ] Other: ___
--- DETAILS ---
Principal (who needs access):
Name/UPN or managed identity name: ___
Object ID (if known): ___
Resource (what they need access to):
Subscription(s): [ ] dev (669a4356) [ ] prod (a5a6818e) [ ] orbit (60e2b27e)
Resource group (if scoped): ___
Specific resource name/ID (if scoped further): ___
Role requested: ___
(Reader / Contributor / Owner / User Access Administrator / Key Vault Secrets User / other)
Justification (one sentence — what breaks without this):
___
Least-privilege check:
Can a narrower scope work? (e.g., RG instead of sub) ___
Is a time-bound grant sufficient? ___
Is a managed identity preferred over a user grant? ___
Related ticket / ADO work item (if any): ___
Approved by (platform owner): kgray@wasteologygroup.com
Submit via Teams to ABS with ashull CC'd. Expected turnaround: 1–2 business days for routine requests, same day for security remediations.
The current ABS engagement is informal. As team size grows, consider formalizing requests through a shared ADO queue so all pending and completed access changes are visible to the PO. A lightweight ADO Task in the Wasteology project with the access-request tag would suffice to start — this also gives the monthly access review a single place to look.
6. RACI for Access Approval and Review Cadence
Current state
Access approval is informal and concentrated in kgray. No regular review cadence is documented, and there is no explicit RACI. Service principal and credential expiry tracking is manual.
Recommended standard
RACI
| Activity | Responsible | Accountable | Consulted | Informed |
|---|---|---|---|---|
| DB read-tier grant (dev) | kgray / tlewis | kgray | — | New developer |
| DB write/admin grant | kgray | kgray | ashull (for prod) | New developer |
| Azure RBAC grant (within kgray's UAA scope) | kgray | kgray | — | Developer, ABS |
| Azure RBAC grant (outside kgray's scope) | ABS / ashull | ashull | kgray | Developer |
| New subscription / resource group | ABS | ashull | kgray | Platform owner |
| Managed identity creation | ABS | ashull | kgray | Developer |
| SP registration (requires ABS) | ABS | ashull | kgray | Platform owner |
| Monthly access review | kgray | Platform owner (PO designates) | ashull | COO |
| Quarterly SP / credential cleanup | kgray | Platform owner | ABS | COO |
| Cost review (weekly via digest) | kgray | Platform owner | — | COO |
Review Cadence
| Review | Cadence | Owner | Tooling |
|---|---|---|---|
| Cost review | Weekly (surfaced in morning standup digest automatically) | kgray | ops digest standup → AZURE GOVERNANCE block; ops azure costs |
| Access review (RBAC + DB roles) | Monthly | kgray | `ops azure access --sub dev |
| Service-principal & credential cleanup | Quarterly | kgray | ops azure identities; check expiry flags from ops azure drift |
| Drift alert review | Daily (automatic via digest pipeline) | kgray (acts on findings) | ops azure drift — flags new SPs, new Owner/UAA grants, expiring credentials, cost spikes >20% |
How Drift Detection Works
The ops azure snapshot command (run daily in the ADO digest pipeline) captures subscription state to wg_digests.azure_governance_snapshots. ops azure drift diffs the two most recent snapshots and emits findings:
- Absolute (no prior snapshot needed): credentials expiring within 30 days, already-expired credentials
- Relative (needs prior snapshot): new service principals, new Owner or User Access Administrator grants, month-to-date cost spikes over 20%
These findings appear in the AZURE GOVERNANCE block of the daily morning brief. Any finding other than a known/expected change requires same-day investigation.
Note: Full daily drift capture is currently gated on granting wg-azure-infra-identity (the ADO CI managed identity) Reader + Cost Management Reader on all three subscriptions and Application.Read.All on Microsoft Graph. Until ABS completes this grant, daily captures are partial and the dashboard relies on manually authenticated snapshots. This ABS request should be prioritized as part of new-developer onboarding infrastructure.
Decide who owns the monthly access review after kgray. The platform owner role is the natural candidate, but this person needs either UAA in prefect-resources RG or a standing relationship with ABS/ashull to act on findings. If the incoming PO does not have Azure access, the review becomes advisory only — which defeats the purpose. Recommend granting the PO read-only access (Reader on all three subscriptions) as a first step so they can independently verify the access matrix without needing to request data from kgray.