GCP IAM Mistakes That Create Security Risks — What I Find in Real Environments
IAM is the most consequential layer of any GCP environment. Get it right and you have a defensible, auditable access model. Get it wrong and you have a platform where a single compromised credential — or a single misconfigured role — can cascade into a serious incident.
I review GCP IAM configurations regularly as part of architecture reviews and security assessments for engineering teams across Canada and the USA. The same mistakes appear repeatedly. None of them are exotic. Most were made quickly during a build phase and never revisited.
Here is what I find, why it matters, and what to do about it.
Mistake 1 — Primitive Roles on Production Projects
Primitive roles — Owner, Editor, Viewer — were GCP’s original access model. They are blunt instruments. Owner grants full control over every service in a project. Editor grants read and write access to almost everything. Neither is appropriate for production environments.
What I find in practice: service accounts with Editor roles created during initial setup because it was the fastest way to get something working. User accounts with Owner on production projects because someone needed to debug an issue once and the role was never revoked.
Why it matters: a compromised Editor service account can read and modify data across all services in the project — Cloud Storage, BigQuery, Cloud SQL, Secret Manager. There is no blast radius containment. One compromised credential is a full project breach.
The fix: audit all IAM bindings on production projects using `gcloud projects get-iam-policy PROJECT_ID` and identify any principal with Owner or Editor. Replace with the minimum predefined or custom role required for the specific function. This is a one-time remediation that significantly reduces your blast radius.
Mistake 2 — Service Account Keys in Version Control and CI/CD
Service account keys are JSON credential files that authenticate as a service account. They are powerful, long-lived, and frequently mishandled. I find them in GitHub repositories, in CI/CD environment variables, hardcoded in application config files, and in Terraform state stored in GCS without encryption.
A single leaked service account key with Editor permissions on a production project is a critical incident. Keys stored in version control are often exposed in git history even after deletion — and git history is public if the repository is public.
Why it matters: unlike user credentials, service account keys do not expire by default and do not trigger login alerts. A leaked key can be used silently for months.
The fix: migrate CI/CD pipelines to Workload Identity Federation (https://buoyantcloudtech.com/gcp-workload-identity-federation-migration/) — no keys, no secrets, federated identity via OIDC. Migrate GKE workloads to Workload Identity. Enforce the `iam.disableServiceAccountKeyCreation` org policy to prevent new keys from being created across the organisation. Rotate and disable any existing keys immediately.
Mistake 3 — Service Accounts With Overly Broad Roles Across Projects
Service accounts used by applications often accumulate roles over time. A background job that originally needed to read from one Cloud Storage bucket ends up with Storage Admin on the entire project because it was faster to grant a broad role than to figure out the minimum required.
What I find: service accounts with roles like `roles/storage.admin`, `roles/bigquery.admin`, or `roles/cloudsql.admin` when the actual requirement is read access to a specific bucket, dataset, or database.
Why it matters: over-permissioned service accounts are silent risk. If the workload using the account is compromised — a vulnerability in the application, a supply chain attack on a dependency — the attacker inherits the full scope of the service account’s permissions.
The fix: audit service account role bindings and apply least-privilege. For Cloud Storage, use `roles/storage.objectViewer` scoped to the specific bucket rather than project-level Storage Admin. For BigQuery, use dataset-level permissions rather than project-level. For Cloud SQL, use IAM database authentication with a specific database user rather than broad Cloud SQL Admin.
Mistake 4 — No Separation Between Service Accounts for Different Environments
Production, staging, and development workloads sharing the same service accounts — or service accounts with permissions spanning multiple environments — is a common finding in platforms that grew quickly without a formal IAM design.
What happens in practice: a service account created for a development environment ends up being used in production because it was convenient. It has broader permissions than a production-scoped account would because it was never hardened. A compromise in the development environment now has a path to production data.
Why it matters: environment separation in IAM is a SOC 2 requirement and a basic security control. It prevents a compromise in a lower environment from propagating upward.
The fix: create separate service accounts per environment, per workload, with permissions scoped to that environment only. Enforce this through the project structure in the GCP Landing Zone Blueprint (https://buoyantcloudtech.com/gcp-landing-zone-blueprint/) — production, non-production, and development in separate projects with separate IAM boundaries.
Mistake 5 — IAM Bindings on Resources Instead of Projects or Folders
GCP allows IAM bindings at the resource level — on individual Cloud Storage buckets, BigQuery datasets, Pub/Sub topics. This is sometimes the right approach for fine-grained access. It becomes a problem when it is used inconsistently, without documentation, and accumulates over time into an access model that no one fully understands.
What I find: environments where access is granted partly at the org level, partly at the project level, and partly at the resource level, with no consistent model. Auditing effective access for any given principal requires traversing the entire IAM hierarchy.
Why it matters: inherited and effective permissions are non-obvious when bindings exist at multiple levels. Misunderstanding effective access leads to both over-permissioning (thinking a principal has less access than it does) and under-permissioning (blocking legitimate access unexpectedly).
The fix: establish a clear IAM governance model — define which access is granted at which level, document it, and enforce it via Terraform. Resource-level bindings should be intentional exceptions, not the default pattern.
Mistake 6 — No Audit Logging on Sensitive Services
Cloud Audit Logs capture Admin Activity automatically. Data Access logs — which record who read or wrote data in Cloud Storage, BigQuery, Cloud SQL, and other data services — are disabled by default for most services because of cost concerns.
The result: you have no record of who accessed what data. In a security incident, you cannot determine what was exfiltrated. In a SOC 2 audit, you cannot evidence data access controls.
Why it matters: data access logging is not optional for regulated industries. PIPEDA, HIPAA, and SOC 2 all require evidence that data access is monitored and auditable. “We have the access controls but no logs” is not a passing answer.
The fix: enable Data Access audit logs for Cloud Storage, BigQuery, Cloud SQL, and Secret Manager across all production projects. Export logs to a centralised BigQuery dataset with retention aligned to compliance requirements. Set up alerts on anomalous access patterns — large data exports, access from unexpected principals, access at unusual times.
What a Typical IAM Review Finds
In my experience, a GCP environment that has never had a formal IAM review typically has 4-6 of the above patterns present simultaneously. The most common combination: primitive roles on production projects, service account keys in CI/CD, and data access logging disabled. These three together mean broad access, no key rotation controls, and no visibility into what is being accessed.
Fixing all three typically takes less than a sprint for a focused engineering team. The remediation is not technically complex — it is prioritisation that most teams never make time for.
Get a Second Set of Eyes on Your GCP IAM
If you are running GCP and have never done a formal IAM review, the risk is real and the fix is achievable. I run short GCP security and architecture audits for engineering teams in Toronto, across Canada, and in the USA — reviewing IAM configuration, org policy, audit logging, and network segmentation, then sharing findings and a prioritised remediation plan.
If you want a second set of eyes on your setup, reach out and we can start with a short conversation about where you are. https://buoyantcloudtech.com/contact-gcp-consulting/
More about my background and approach: https://buoyantcloudtech.com/about/
FAQ
How do I audit all IAM bindings in my GCP organisation?
Use `gcloud asset search-all-iam-policies –scope=organizations/ORG_ID` to export all IAM bindings across the org. For a more structured review, export to BigQuery via Cloud Asset Inventory and query for specific role patterns — primitive roles, cross-project service account bindings, and bindings to external identities.
What is the difference between a service account and a user account in GCP IAM?
User accounts represent human identities — engineers, operators, administrators. Service accounts represent non-human identities — applications, workloads, CI/CD pipelines. The key difference for security: service account keys are long-lived credentials that don’t expire, making key management critical. Workload Identity Federation eliminates the need for keys entirely.
How often should GCP IAM be reviewed?
At minimum, quarterly for production environments. Access accumulates over time — people change roles, projects evolve, and permissions granted for temporary purposes are never revoked. Automated tooling like Cloud Asset Inventory combined with Security Command Center can provide continuous monitoring between formal reviews.
Is Workload Identity Federation difficult to implement?
No — for GitHub Actions and GKE, the implementation is well-documented and typically takes a few hours per pipeline. I covered the full migration approach at https://buoyantcloudtech.com/gcp-workload-identity-federation-migration/. The harder part is organisational — getting teams to retire existing service account keys once WIF is in place.
Related Reading
– The SCALE Framework: https://buoyantcloudtech.com/scale-framework-gcp-architecture/
– Workload Identity Federation Migration: https://buoyantcloudtech.com/gcp-workload-identity-federation-migration/
– GCP Privileged Access Manager and SOC 2: https://buoyantcloudtech.com/gcp-privileged-access-manager-soc2/
– GCP Landing Zone Blueprint: https://buoyantcloudtech.com/gcp-landing-zone-blueprint/
– VPC Service Controls: https://buoyantcloudtech.com/gcp-vpc-service-controls-security-consulting/
– DevSecOps Without a Security Team: https://buoyantcloudtech.com/devsecops-without-a-security-team-gcp/
– DevSecOps & Cloud Security Services: https://buoyantcloudtech.com/cloud-service/devsecops-cloud-security/
Book a Free GCP Architecture Review: https://buoyantcloudtech.com/contact-gcp-consulting/