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
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
| Role | Why AAD needs it |
|---|---|
| roles/container.admin | Create and operate the GKE cluster your tenant environments run on. |
| roles/compute.networkAdmin | VPC, subnets, router and Cloud NAT — plus load-balancer cleanup on teardown. Narrowed from compute.admin in v2. |
| roles/compute.securityAdmin | Remove the firewall rules GKE itself creates (k8s-fw-l7) when an environment is destroyed, so nothing orphans. |
| roles/artifactregistry.admin | The private Docker registry your app images are built into. |
| roles/iam.serviceAccountAdmin | Create the per-tenant node and workload service accounts. |
| roles/iam.serviceAccountUser | Attach those service accounts to the infrastructure they belong to. |
| roles/resourcemanager.projectIamAdmin | Bind 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.admin | Your app's runtime secrets are stored in your project's Secret Manager, not ours. |
| roles/cloudbuild.builds.editor | Run image builds inside your project with Cloud Build. |
| roles/serviceusage.serviceUsageConsumer | Call the APIs the connect script enabled. Narrowed from serviceUsageAdmin in v2 — enabling happens in the script, as you. |
| roles/storage.admin | The staging bucket Cloud Build auto-creates for build sources. |
| roles/logging.viewer | Read 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
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