In almost every Google Cloud assessment we run, exported service account keys are on the list. Not because teams are careless, but because a JSON key is the path of least resistance: it works from anywhere, it never expires, and it takes thirty seconds to create.
Those same three properties are what make it a problem. A key that works from anywhere works from an attacker’s laptop. A key that never expires outlives the pipeline, the project and often the employee. And a key created in thirty seconds is rarely inventoried.
What replaces them
Google Cloud has keyless alternatives for essentially every legitimate use case:
- Workloads inside GCP — attach a service account directly to the Compute Engine instance, Cloud Run service or Cloud Function. No key involved.
- Workloads in GKE — GKE Workload Identity binds a Kubernetes service account to a Google service account, so pods get short-lived credentials without a mounted secret.
- CI/CD and external workloads — Workload Identity Federation lets GitHub Actions, GitLab CI or an AWS workload exchange its own OIDC token for short-lived Google credentials. No secret is stored in the CI system at all.
- Human access —
gcloud auth loginand short-lived impersonation via--impersonate-service-account, never a downloaded key.
A migration that does not break production
The failure mode here is enabling the org policy constraint first and finding out afterwards which pipelines depended on a key.
1. Inventory before you touch anything. List every key across every project and, critically, find out when each was last used. Key usage shows up in Cloud Audit Logs; querying authentication events by key ID tells you which keys are live and which are abandoned.
2. Delete the dead ones. In most estates a substantial fraction of keys have not authenticated in months. Disable rather than delete first — a disabled key can be re-enabled quickly if something unexpected surfaces — then delete after a quiet period.
3. Migrate by consumer, not by key. Group the remaining keys by what uses them: CI pipelines, in-cluster workloads, external SaaS integrations, developer laptops. Each group has one target pattern, so you solve it once and apply it repeatedly.
4. Run both paths briefly. For CI, configure Workload Identity Federation alongside the existing key, switch the pipeline over, and watch for authentication failures for a cycle or two before removing the key.
5. Close the door. Once the estate is clean, enforce
constraints/iam.disableServiceAccountKeyCreation at the organisation or folder
level, with a documented exception process and a short list of exempt projects
if you genuinely have an integration that cannot federate.
6. Watch for regressions. A log-based alert on service account key creation events catches the exception that gets granted and never revisited.
The part teams underestimate
Federation is the easy half. The harder half is that migrating forces you to answer a question the key was letting you avoid: what should this workload actually be allowed to do?
A long-lived key attached to a broadly-privileged service account hides a lot of over-permissioning. When you rebuild the trust path, you get an opportunity to scope the identity properly — one service account per workload, predefined or custom roles instead of Editor, IAM conditions where they apply. That is where most of the real risk reduction comes from.
Treat the key removal as the visible outcome and the least-privilege work as the actual project, and the effort pays for itself well beyond the audit finding it closes.