Service account keys are the GCP credential that leaks. Here is how to stop creating them
Almost every Google Cloud incident we have investigated started with a JSON key file. Workload Identity Federation removes the need for them entirely, and the migration is smaller than you think.
A Google Cloud service account key is a JSON file containing a private key that does not expire, is not bound to any network, and authenticates as the service account from anywhere on the internet. It ends up in a CI variable, a developer's Downloads folder, a Slack thread and eventually a public repository.
Every GCP security review we run finds keys. The useful question is not "do you have them" but "can you stop needing them", and in 2026 the answer is almost always yes.
Replace keys with federation, in this order
CI/CD pipelines — Workload Identity Federation. GitHub Actions, GitLab, Bitbucket and Azure DevOps can all present an OIDC token that GCP exchanges for a short-lived access token. You create a workload identity pool, a provider for your CI system, and an attribute condition that pins it to your repository and branch. That last part matters: a provider without an attribute condition will accept a token from any repository on that CI platform, which is a worse position than the key you replaced.
The migration is roughly an hour per pipeline, and it deletes the whole category.
GKE workloads — Workload Identity. Pods assume a Kubernetes service account that is bound to a Google service account. No key, no metadata-server tricks, no secret to rotate. Enable it on the cluster and node pools, annotate the Kubernetes service account, and remove the mounted key file.
Humans — impersonation, not keys. A developer who needs to run a script as a service account uses --impersonate-service-account, which mints a token for an hour and leaves an audit trail with their own identity attached. The permission to impersonate (roles/iam.serviceAccountTokenCreator) is itself an auditable grant, which is exactly what you want.
Workloads outside Google Cloud — federation too. AWS, Azure and any OIDC-capable identity provider can federate into GCP. On-premises systems with no identity provider are the genuine remaining case for keys; scope those service accounts to a single project and rotate on a schedule you actually run.
Then close the door
iam.disableServiceAccountKeyCreation as an organisation policy constraint, with exceptions listed at folder level for the handful of legacy systems that need them. Pair it with iam.disableServiceAccountKeyUpload. Without the constraint, keys come back within a quarter, because the console makes creating one a two-click action and the error message when a pipeline fails suggests it.
IAM Conditions: the underused control
Once credentials are short-lived, conditions let you narrow what they can do without writing custom roles.
- Time-bound access. A grant with
request.time < timestamp("2026-10-01T00:00:00Z")expires by itself. This is how temporary elevated access should work: the contractor's access does not need an offboarding ticket because it stops on its own. - Resource-scoped bindings.
resource.name.startsWith("projects/_/buckets/prod-reports")turns a project-wide Storage Admin into an admin of one bucket. - Tag-based conditions for granting access to everything labelled with a given environment, which scales better than enumerating resources.
Conditions do not apply to every service, so check support before relying on one as a security boundary rather than a convenience.
The findings that repeat
Across GCP reviews, four things show up nearly every time:
roles/editoron the default compute service account. Every VM in the project runs as it by default, so any code execution on any VM is project-wide write access. Disable the automatic grant with theiam.automaticIamGrantsForDefaultServiceAccountsconstraint and give each workload its own service account.roles/ownergranted to individuals rather than to a break-glass group with a condition and an alert.- Service accounts with
roles/iam.serviceAccountTokenCreatoron themselves or on more privileged accounts, which is a privilege escalation path scanners frequently miss. - Keys older than a year that no audit log shows in use. Those you can simply delete — check
iam.googleapis.comdata access logs first.
We cover all four in the security assessment, and the open-source scanners in our tooling piece flag the first and last without any licence.
What to do this week
List every service account key in the organisation: gcloud iam service-accounts keys list across your projects, filtered to user-managed keys. For each one, write down what uses it. The ones where nobody knows are the ones to delete first, and the CI ones are an hour each to replace.