Skip to content

Google Cloud — folder canon, IAM, and operational rules

Org: ucca.edu.au (org ID 731589017464, customer ID C02dhi61x) Last updated: 2026-05-07 (post GCP-FOLDER-CANON-01) Brief in archive: docs/briefs/archive/gcp-folder-canon-01.md v1.2


Folder hierarchy

ucca.edu.au
├── rtopacks/                    → admin@rtopacks.com.au
├── ucca-online/                 → admin@ucca.online
├── ucca-edu-au/                 → tim@ucca.edu.au
├── tooling/                     → tim@ucca.edu.au
├── archive/                     → tim@ucca.edu.au only
└── personal-or-experimental/    → tim@ucca.edu.au only (sub-folder owners get Editor)
    ├── hunter-richards/         → Dom Hunter + Antony Richards (Editor)
    ├── arrowaccountants/        → Kevin Singh (Editor)
    └── tim-personal/            → tim@ucca.edu.au
Folder ID
rtopacks/ 648604026052
ucca-online/ 321449398372
ucca-edu-au/ 600535416745
tooling/ 756590331639
archive/ 205278468367
personal-or-experimental/ 911973651944
personal-or-experimental/hunter-richards/ 756755166908
personal-or-experimental/arrowaccountants/ 638641471978
personal-or-experimental/tim-personal/ 6914142780

tim@ucca.edu.au retains Organization Administrator at org level as the org-wide recovery backstop, inherited everywhere.


Principle

Concern Mechanism
Entity ownership (RTOpacks, UCCA Online, etc.) Folders
Environment scope (shared, prod, dev) Naming convention + labels

Folders are entity-scoped only. Environment information never goes into folder hierarchy.


Project naming + labels

Project ID patterns: - <entity>-<purpose> — serves both prod and dev (e.g. rtopacks-maps) - <entity>-<purpose>-<env> — environment-scoped (e.g. rtopacks-billing-prod) - <scope>-<purpose> — tooling/meta (e.g. gcp-canon-tooling)

Project IDs are immutable post-creation. To rename: create-new with canonical ID, migrate, delete old.

Mandatory labels per project: entity (rtopacks/ucca-online/ucca-education/tooling/archive/personal), environment (shared/prod/dev), and consumer (free-text, optional). Firebase auto-labels (firebase=enabled) coexist via merge-not-replace. Label updates require the v3 Cloud Resource Manager REST API — gcloud projects update does not support --update-labels.


Containment for personal-or-experimental/

Org-policy billing constraints are unavailable on this org (v1 deprecated, v2 missing the constraint). The brief's containment intent is satisfied by IAM state — no contained user holds any role on any org billing account, so they cannot link org billing regardless of project Owner status. This is structural, not a gap.

Trigger condition that would activate the gap: granting any role on any org-owned billing account to a principal whose primary GCP access is under personal-or-experimental/. If that ever happens, BILLING-CONSTRAINT-GAP-01 must be revisited before the grant lands.


Soft-delete restore prerequisites

If you are about to restore a soft-deleted project from this org, check this table first.

Project SA Key ID Created Required pre-restore action
still-vim-413000 ucca-learnworlds@still-vim-413000.iam.gserviceaccount.com dc96863ecf3558d00c1254afc614d5795cb806ce 2024-02-01 Delete key, disable SA, before any production use
ucca-drive-proxy drive-proxy@ucca-drive-proxy.iam.gserviceaccount.com unknown (cannot query post-soft-delete) unknown Verify SA key inventory; if any USER_MANAGED keys exist, delete + disable SA before any production use
mobile-ucca-college firebase-adminsdk-9ofdn@mobile-ucca-college.iam.gserviceaccount.com unknown (cannot query post-soft-delete) unknown Vendor (LearnWorlds) had Editor on this Firebase-tagged mobile project; standard Firebase Admin SDK pattern requires JSON key export to vendor backend; likely a USER_MANAGED key exists. Verify and delete + disable before any production use.

Omitted from table: seven other soft-deleted projects host firebase-adminsdk SAs that could in principle have USER_MANAGED keys exported, but share the same low-likelihood pattern (Firebase-tagged at creation, no surfaced active consumer in Stage 1 audit, no other custom SAs, no API key activity, no vendor IAM grants): groupon-usa, ucca-education-aa0ee, dlongglobal-com, dlongglobal-net, dlongglobal-org, jimmykuo-com-au-ec206, ttca-edu-au. Realistic likelihood of a backend service holding their JSON key: essentially zero. Caveat: if you're restoring one of these seven, verify SA key inventory before any production use, treating the project as if it were on this table.


SA roles inventory

gcp-canon-01-agent@gcp-canon-tooling.iam.gserviceaccount.com is the persistent bounded-access SA for org-wide GCP operational work. Hosted in gcp-canon-tooling. Key vaulted in 1Password as item "GCP — gcp-canon-01-agent SA key" — retrieve via op CLI for SA auth setup; no persistent local copy.

11 current roles at org level:

  • Read-only (4): roles/viewer, roles/iam.securityReviewer, roles/billing.viewer, roles/resourcemanager.organizationViewer
  • Resource management (5): roles/resourcemanager.folderAdmin, roles/resourcemanager.projectMover, roles/resourcemanager.projectDeleter, roles/resourcemanager.projectIamAdmin, roles/orgpolicy.policyAdmin
  • IAM management (2): roles/iam.securityAdmin, roles/iam.serviceAccountKeyAdmin

Future briefs may add or trim roles based on task scope. AGENT-ROLES-AUDIT-01 (queued) is a short review to identify any genuinely audit-only roles that could be trimmed without affecting ongoing operational use.


Three lessons (for future GCP briefs)

  1. API enablement is per-quota-project. SAs hosted in tooling projects need every used Google API enabled on that project, not the org. SERVICE_DISABLED errors quote the quota project's number — easy to misread as a permission error.
  2. Org policy v2 catalog can lack constraints documented in v1. Reads via v1 may show a constraint with empty etag; writes only work via v2. Verify in the v2 catalog before designing org policies — billing.allowedBillingAccountsList is a known gap.
  3. gcloud syntax for in-project SA operations requires explicit --project= even when the SA email contains the project ID. Without it, gcloud reports "Failed to find attribute [project]" rather than parsing from the email.