# IAM & Workload Identity

> zop.dev provisions 5 cluster service accounts with keys (cloud_monitoring, cluster_autoscaler, karpenter, sql_proxy, wildcard_dns_solver), 3 custom IAM roles, about 16 IAM bindings, 7 project-level grants at bootstrap, and per-namespace Workload Identity service accounts so pods act as Google identities without exported keys.

Source: https://zop.dev/integrations/provision/gcp/iam-workload-identity
Updated: 2026-08-19

---

Workload Identity lets Kubernetes workloads act as Google service accounts without exported keys. zop.dev provisions the service accounts, custom roles, and bindings that give clusters and workloads exactly the access they need.

## Cluster accounts, custom roles, namespace identities

Five cluster service accounts with keys (cloud_monitoring, cluster_autoscaler, karpenter, sql_proxy, wildcard_dns_solver), three custom IAM roles, about 16 IAM bindings, seven project-level role grants at bootstrap, and per-namespace Workload Identity service accounts.

## Access tiers and per-workload Pub/Sub accounts

```text
user_access admins/editors/viewers; artifact_users; per-workload pubsub_editor accounts.
```

## The iam type covers accounts and role grants

The provisioner API exposes an iam resource type covering service accounts and role grants.

## Two identity models, one cluster

Application workloads get the modern model: per-namespace Workload Identity service accounts,
where a pod proves who it is through the cluster's OIDC trust and no key ever exists to
steal, expire, or rotate. The platform's own components still carry exported keys: the 5 cluster service accounts for
cloud_monitoring, cluster_autoscaler, karpenter, sql_proxy, and wildcard_dns_solver. Those 5 keys are the static credentials in the design: they belong in
the rotation inventory, and they are the honest answer to "is everything here keyless?".
Everything application-facing is; five platform components are not.

## Custom roles instead of primitive ones

The 3 custom IAM roles exist because Google's primitive roles (editor, viewer) grant far more
than any component needs. A custom role names the exact permissions a component uses, which
does two things: it shrinks what a compromised credential is worth, and it documents intent. The
role definition is a readable statement of what the autoscaler or the DNS solver is
supposed to touch. The roughly 16 bindings wire those roles to their accounts at the right
scopes.

## The bootstrap grants set the ceiling

The 7 project-level role grants made at bootstrap define what the platform itself may do in
the project, and every later provision operates inside that ceiling. For an auditor, this is the
right place to start: review those 7 grants and you have reviewed the platform's maximum
authority, before reading anything else.

## IAM costs nothing and gates everything

None of this bills a cent. Accounts, roles, and bindings are all free, at any quantity. The
absence of cost pressure is exactly why identity sprawl happens; the per-namespace scoping
and named custom roles are the structure that keeps "free" from becoming "unaccountable."
