Security

The trust model, in full.

Most platforms ask for a key and promise to be careful. AAD is built so there is no key to leak — and you can read exactly what it can do, because the grant list below is the one in the connect script.

No keys, ever — how impersonation works

AAD control plane
platform SA
mints a short-lived token (≤ 1 h) via iamcredentials
Your GCP project
aad-deployer SA
created by you · lives with you
GKE · Cloud Build · Artifact Registry · Secret Manager
trust edge: roles/iam.serviceAccountTokenCreator — granted by you, in your project, visible in your audit logs, deletable by you at any moment

When AAD deploys, it asks Google for a token that expires within the hour, scoped to one service account that lives in your project. There is no service-account key file — not encrypted, not vaulted, not anywhere — because none is ever created. Every token mint is an iamcredentials.googleapis.com call you can see in your own audit logs.

Every permission, and why

RoleWhy AAD needs it
roles/container.adminCreate and operate the GKE cluster your tenant environments run on.
roles/compute.networkAdminVPC, subnets, router and Cloud NAT — plus load-balancer cleanup on teardown. Narrowed from compute.admin in v2.
roles/compute.securityAdminRemove the firewall rules GKE itself creates (k8s-fw-l7) when an environment is destroyed, so nothing orphans.
roles/artifactregistry.adminThe private Docker registry your app images are built into.
roles/iam.serviceAccountAdminCreate the per-tenant node and workload service accounts.
roles/iam.serviceAccountUserAttach those service accounts to the infrastructure they belong to.
roles/resourcemanager.projectIamAdminBind the node service account's project-level roles. The one broad role in the list — every binding it makes is declared in Terraform, in the open.
roles/secretmanager.adminYour app's runtime secrets are stored in your project's Secret Manager, not ours.
roles/cloudbuild.builds.editorRun image builds inside your project with Cloud Build.
roles/serviceusage.serviceUsageConsumerCall the APIs the connect script enabled. Narrowed from serviceUsageAdmin in v2 — enabling happens in the script, as you.
roles/storage.adminThe staging bucket Cloud Build auto-creates for build sources.
roles/logging.viewerRead container logs so the Logs tab in your console works. Read-only.

What changed in v2

Dropped iam.workloadIdentityPoolAdmin (WIF retired for direct impersonation). Narrowed compute.admin to networkAdmin + securityAdmin, and serviceUsageAdmin to serviceUsageConsumer. The list only ever shrinks or gets a public version bump — your console tells you when a re-run is needed.

Revoke in one command

off-boarding, complete
gcloud iam service-accounts delete \  aad-deployer@YOUR_PROJECT.iam.gserviceaccount.com

That's the whole off-boarding procedure. AAD loses all access instantly. Everything already deployed keeps running — it was always yours.

Continuously verified, not assumed

AAD doesn't check your permissions once and hope. Every connection is probed as the deployer service account — a live testIamPermissions call against your real IAM policy plus an enabled-API diff — and re-proved before every deploy. The result is a visible checklist on your connection page, not a green light on faith.

For enterprise policy admins

Works under constraints/iam.allowedPolicyMemberDomains (domain-restricted sharing). The only cross-org grant the script makes is serviceAccountTokenCreator on the deployer SA to AAD's platform service account — allowlist that one principal and everything else in the script is entirely inside your own project. The script tolerates a blocked grant, tells you exactly which member failed, and is idempotent — safe to re-run after your policy admins approve.

What AAD never gets

  • No service-account keys
  • No org- or billing-level access
  • Nothing outside the one connected project
  • No standing sessions — every token expires within the hour