Google Cloud Platform ships with more than 180 predefined IAM roles spanning upwards of 100 services, plus three primitive roles — Owner, Editor, and Viewer — that predate fine-grained access control entirely. Yet audits of production GCP environments still turn up service accounts holding roles/editor on entire projects, and human users retaining Owner access months after a project migration wrapped up. GCP IAM least privilege isn't a settings toggle; it's an ongoing discipline of matching every principal's permissions to the narrowest set of actions it actually performs, then re-verifying that match as workloads change. A single over-scoped service account key, if leaked through a public repo or a misconfigured CI job, can hand an attacker read/write access to every BigQuery dataset, Compute Engine instance, and Cloud Storage bucket in a project. This post walks through what least privilege means concretely in GCP, when to reach for custom roles instead of predefined ones, how much you can trust IAM Recommender, and where service accounts specifically go wrong.
What does GCP IAM least privilege actually mean in practice?
In practice, GCP IAM least privilege means every identity — human or service account — holds only the permissions required for its current job, scoped to the smallest resource hierarchy level that makes sense (resource, then project, then folder, then organization). Google's own IAM documentation defines over 10,000 individual permissions (things like storage.objects.get or compute.instances.start), and a role is simply a named bundle of them. The primitive Owner role alone grants roughly 8,000 of those permissions across every service in a project — including the ability to delete the project itself, modify IAM bindings, and exfiltrate any data stored in it. A backend engineer who needs to read Cloud SQL data for debugging does not need Owner; they need roles/cloudsql.viewer, or narrower still, a custom role limited to cloudsql.instances.get and cloudsql.databases.list. The gap between what's granted and what's used is what security teams call the "permissions gap," and in most GCP environments we've assessed, that gap is measured in the thousands of unused permissions per project, not dozens.
Predefined vs custom roles GCP: which should you use, and when?
Predefined roles should be your default, and custom roles should be the deliberate exception for the roughly 10-20% of cases where predefined granularity isn't tight enough. Google maintains predefined roles like roles/pubsub.publisher or roles/storage.objectViewer specifically so most teams never have to build their own — they're versioned, updated automatically as new permissions are added to a service, and battle-tested across millions of projects. The predefined vs custom roles GCP decision comes down to blast radius: if roles/bigquery.dataViewer grants read access to every dataset in a project but your ETL job only needs one dataset, a predefined role is too broad, and that's when a custom role — built from the exact permission list via gcloud iam roles create — earns its maintenance cost. The tradeoff is real: custom roles don't automatically pick up new permissions when Google updates a service, so a role built in January 2024 can silently become stale by the time that service ships new API methods a year later. Google explicitly recommends starting from a predefined role's permission list and trimming it down, rather than building a custom role from a blank page, precisely to avoid missing dependent permissions that break functionality in subtle ways.
What is IAM Recommender GCP and how accurate are its suggestions?
IAM Recommender GCP is Google's built-in tool that analyzes up to 90 days of actual permission usage (via Policy Analyzer and Cloud Audit Logs) and proposes narrower role bindings for principals it judges over-permissioned. If a service account granted roles/editor has only ever called three Cloud Storage read methods and two Pub/Sub publish methods in that window, Recommender will suggest replacing the broad role with a tighter predefined or custom equivalent that covers the observed calls, and it now supports one-click "Apply" for many recommendations directly in the console or via the gcloud recommender command. It's a strong starting point, not a finish line: because it only sees 90 days of activity, it will confidently recommend revoking permissions used by a quarterly batch job, an annual compliance export, or a disaster-recovery runbook that hasn't fired since last year's fire drill. Teams that blindly auto-apply IAM Recommender GCP suggestions without cross-referencing infrequent-but-critical workflows have broken exactly those jobs — usually discovered when the DR failover doesn't work during the incident it was meant to handle. Treat Recommender output as a prioritized worklist for human review, not an automation target, unless you've explicitly excluded low-frequency critical paths.
Why do least privilege service accounts matter more than user permissions?
Least privilege service accounts matter more than human user permissions because service account keys are long-lived, frequently embedded in code or CI configuration, and rarely protected by MFA or session expiry the way console logins are. A human's overprivileged Owner grant is bounded by that person's login session and (ideally) 2FA; a service account key baked into a Terraform variable file or a GitHub Actions secret has no such friction and can be extracted, copied, and reused indefinitely once it leaks. Google's own guidance now steers teams away from long-lived JSON key files entirely in favor of Workload Identity Federation, which issues short-lived, auto-rotating credentials tied to a specific GitHub repo, Kubernetes namespace, or CI job — eliminating the exported-key problem outright. Where static service account keys are unavoidable, the default project-editor service account that GCP auto-creates for every new project (the PROJECT_NUMBER-compute@developer.gserviceaccount.com default) is the single most common finding in cloud security assessments: it's granted Editor by default, it's easy to forget, and it's frequently the identity attached to Compute Engine VMs by teams who never revisited the default. Auditing and locking down default service accounts, and issuing purpose-built ones scoped to a single Cloud Function or pipeline, closes off one of the most exploited paths into GCP environments.
How do you migrate from primitive roles without breaking production?
You migrate from primitive roles safely by running in "detect, don't enforce" mode first — using Policy Simulator to test a proposed role change against 90 days of historical activity before it ever touches production traffic. Policy Simulator, part of the same Policy Intelligence suite as IAM Recommender, replays real Cloud Audit Log entries against a candidate policy and reports exactly which calls would have been denied, letting you catch a missed permission before an on-call engineer catches it at 2 a.m. The typical rollout sequence: (1) inventory every principal holding Owner, Editor, or Viewer at the project or folder level, (2) run IAM Recommender and Policy Simulator against each, (3) replace primitive roles with the narrowest predefined role that Simulator confirms as safe, escalating to custom roles only where predefined options remain too broad, and (4) set a 90-day recurring review cadence, since new hires, new services, and new pipelines will reintroduce broad grants faster than most teams expect. Organizations that skip the Simulator step and cut permissions directly in production consistently report the same failure mode — a batch job or scheduled function breaks silently days or weeks later, once it hits a code path the trimming didn't account for.
How Safeguard Helps
Safeguard continuously maps every GCP service account, its attached roles, and the actual API calls it makes against your software supply chain — CI/CD pipelines, build systems, and deployment tooling — so you can see exactly where GCP IAM least privilege has drifted without waiting for a quarterly audit. Instead of treating IAM hygiene as a separate workstream from supply chain security, Safeguard correlates over-permissioned service accounts with the pipelines and third-party integrations that use them, flagging the cases that matter most: a CI runner with roles/editor that only needs to push container images, or a default compute service account still attached to production VMs. Safeguard surfaces these findings alongside dependency and build-provenance risk in a single view, so security teams can prioritize the service accounts whose blast radius overlaps with an actual attack path — not just the ones with the largest raw permission count. For teams already running IAM Recommender, Safeguard adds the missing context Recommender can't see on its own: which of those flagged service accounts sit inside your software delivery pipeline, and therefore which least-privilege fixes actually reduce supply chain risk versus general cloud hygiene backlog.