Mastering IAM in GCP: A Practical Guide to Securing Your Cloud

Introduction

Identity and Access Management (IAM) in Google Cloud Platform (GCP) is the backbone of secure cloud operations. It determines who can do what, where — across your entire GCP organization. Whether you're deploying a serverless app, managing infrastructure via Terraform, or granting temporary developer access, IAM decisions can either safeguard or expose your environment.

This blog post is a hands-on guide to mastering IAM in GCP — not just from a theoretical angle, but through the lens of real-world security practices, tooling tips, and hard-earned lessons from working with complex GCP organizations.

We'll cover:

  • How GCP's resource hierarchy affects permission scopes

  • When to use basic vs predefined vs custom roles

  • Why resource-level IAM and conditions are powerful (but nuanced)

  • Best practices for managing service accounts, impersonation, and privileged access

  • Tools to audit, visualize, and analyze IAM at scale

  • And a few sharp edges and gotchas you absolutely want to avoid

Whether you're securing a single project or hundreds of environments across multiple teams, this post will help you apply the principle of least privilege, reduce attack surface, and gain confidence in your IAM posture.

Understanding GCP IAM Hierarchy

Before assigning roles or setting up policies, it’s essential to understand how GCP structures its resources. IAM in GCP is hierarchical, and the level at which you grant a permission directly impacts its scope and reach.

Here’s the core hierarchy:

Each level inherits IAM policies from the level above, unless explicitly overridden. Let’s break it down:

Organization Level

  • Top-most node in GCP’s resource hierarchy.

  • Represents your company, typically tied to a G Suite or Cloud Identity domain.

  • IAM policies at this level apply across all folders, projects, and resources.

  • Use Cases:

    • Global auditors (e.g., security team)

    • Organization-wide constraints or viewer roles

    • Policy Analyzer usage limits (20 free queries/day apply at this level)

Folder Level

  • Folders help group projects by business unit, environment, or team.

  • IAM applied here flows down to all projects and resources under it.

  • Highly recommended for large organizations.

Use Case Example: Give devops@example.com viewer access to all “production” projects by applying a viewer role at the “Production” folder level.

Project Level

  • Every GCP service (VMs, Cloud Run, GCS, BigQuery, etc.) lives inside a project.

  • Most teams manage IAM primarily at this level.

  • Be careful: giving a user Editor here is essentially full control over all services in the project.

Resource Level

  • Some services support fine-grained IAM (e.g., individual Cloud Storage buckets, Cloud Secrets, BigQuery datasets, Pub/Sub topics).

  • Use this level to enforce least privilege access when the project-wide role is too broad.

We’ll explore resource-level IAM in more depth later, but it’s important to know it exists as part of this hierarchy.

💡 Quick Tip: Prefer Project Isolation Over Broad IAM: Don’t overload a single project with multiple teams or services. Instead, create separate projects to isolate environments like dev, test, and prod. Benefits: Cleaner IAM scoping, Cheaper logging (50 GiB free logging/project/month), Easier billing separation, Reduced blast radius in case of incidents

Roles: Basic, Predefined, and Custom

GCP IAM revolves around roles, which are collections of permissions that define what actions a principal (user, group, or service account) can perform.

There are three types of roles in GCP:

Basic Roles (Avoid if Possible)

  • roles/owner, roles/editor, roles/viewer

  • These are legacy, project-wide roles that grant very broad access

  • Easy to assign, but dangerous in production — e.g., Editor gives full control over most services

🛑 Security Concern: These roles violate the principle of least privilege and are often the source of over-permissioned accounts.

Predefined Roles (Prefer When Possible)

  • Created and maintained by Google

  • Designed for specific services or job functions

  • Examples:

    • roles/cloudsql.admin

    • roles/storage.objectViewer

    • roles/logging.viewer

These roles are well-scoped and a good starting point for secure access control.

Custom Roles (For Fine-Grained Control)

  • Define your own roles with only the permissions you need

  • Useful when predefined roles are too broad or don’t match your use case

  • Can be created at the organization or project level

💡 Quick Tip: Find roles by permission

If you know a permission you need (e.g., storage.objects.get), paste it directly into the IAM role search bar in the console. It will return all roles that include that permission — both predefined and custom.

IAM Conditions

IAM Conditions allow you to add context-aware constraints to role bindings — bringing an extra layer of control on top of traditional IAM.

Instead of simply saying “user X can access resource Y”, you can now say things like:

  • “User X can access resource Y, \*but only during business hours”*,

  • “...only from the company VPN”, or

  • “...only for this specific bucket or instance”.

This feature is key to implementing least privilege access more precisely.

Common Use Cases

Time-Bound Access

Useful for short-term debugging or incident response. No need to remember to revoke access later.

"condition": {
  "title": "Temporary Access",
  "expression": "request.time < timestamp('2025-06-30T00:00:00Z')"
}

Resource-Specific Access

Grant permissions only for a specific resource, like a bucket or service.

"condition": {
  "title": "Specific Bucket Access",
  "expression": "resource.name == 'projects/_/buckets/my-critical-bucket'"
}

IP-Based Restrictions

Restrict access to known corporate IP ranges (e.g., VPN egress).

"condition": {
  "title": "Corp IP Only",
  "expression": "request.remoteIp.startsWith('203.0.113.')"
}

Identity-Aware Proxy host-based restrictions

See my other post :-)

Gotchas and Pain Points

While IAM Conditions are powerful, they can be frustrating to work with in practice:

  • Propagation Delay: IAM is a global service — changes may take up to a minute to fully sync, making rapid iteration tedious.

  • Difficult to Test: There's no built-in "dry run" mode, so you often have to test by trying and failing real requests.

  • Limited Coverage: Not all GCP services or permissions support conditions. Support is expanding, but gaps remain.

  • Documentation Could Be Better: The CEL syntax isn’t always intuitive, and complex expressions often require trial and error.

💡 Tip: Use simple conditions first and layer in complexity incrementally. Always validate with least-privileged roles first. And help yourself with audit logs!

Visibility and Auditing Tools

Understanding who has access to what in GCP is critical — but not always straightforward. Thankfully, GCP provides a few tools to help analyze, audit, and inventory IAM bindings across your organization.

Here are three ways to gain visibility into your IAM landscape:

Policy Analyzer

From Google Cloud documentation:

Policy Analyzer for allow policies can help you answer questions like these:

  • Who can access this IAM service account?

  • Who can read data in this BigQuery dataset that contains personally identifiable information (PII)?

  • What roles and permissions does the dev-testers group have on any resource in this project?

  • What Compute Engine virtual machine (VM) instances can Tal delete in project A?

  • Who can access this Cloud Storage bucket at 7 PM?

Pricing:

  • Up to 20 analysis queries per day per org for free

  • For higher usage, you’ll need Security Command Center (SCC) Premium or Enterprise

Great for access reviews, security audits, and identifying overly broad permissions

Asset Inventory

Cloud Asset Inventory provides a snapshot of all resources and their associated IAM policies across an organization.

Use it to:

  • List all IAM bindings across folders, projects, or service accounts

  • Track changes in IAM over time

  • Export IAM policy data to BigQuery, GCS, or Pub/Sub for analysis

(Or, shameless plug, give my little tool a try gcp-iam-dumper)

Best Practices & Security Recommendations

Getting IAM “just right” is hard — but these best practices will help reduce risk, simplify audits, and keep your access model manageable at scale.

Principle of Least Privilege

Always grant the minimum permissions required — no more, no less.

  • Start with Viewer or service-specific roles

  • Use custom roles when predefined roles are too broad

Project Isolation

As mentioned earlier, use separate projects for different environments (e.g., dev/test/prod) or teams. This simplifies IAM and limits blast radius.

Avoid Relying on Default Service Accounts

  • They’re often automatically granted Editor at the project level

  • They are usually used by default in Compute Engine, Cloud Run, and other services

  • Disable them with an organization policy and replace them with dedicated service accounts

Use One Service Account Per Workload

  • Don’t reuse service accounts across unrelated systems — doing so leads to privilege creep, where permissions accumulate over time to support multiple use cases.

  • Isolated service accounts are easier to scope, monitor, and rotate.

  • They also make incident response and auditing much more straightforward — you know exactly what workload is doing what.

Avoid Exporting Service Account Keys

If your workload runs inside GCP (e.g., on GKE, Cloud Run, or Compute Engine), do not export service account keys. Instead:

  • On GKE, use Workload Identity Federation to bind Kubernetes service accounts to GCP service accounts.

  • Use the metadata server to securely obtain identity tokens and access tokens. It’s usually automatically used in any GCP service if you rely on google cloud programming libraries.

Exported service account keys are a major security liability — they’re hard to rotate and easy to leak.

Understand Service Account User vs Service Account Token Creator

RolePurpose
roles/iam.serviceAccountUserLets a principal act as the service account (e.g., for deploying a Cloud Run service). If a VM is running with a service account, and users can SSH into that VM, they need to have the Service Account User role on the service account itself. Why? Because once inside the VM, a user can access credentials via the metadata server and perform actions as that service account — so GCP requires explicit permission to use it.
roles/iam.serviceAccountTokenCreatorAllows generating tokens on behalf of a service account (used for impersonation)

⚠️ Be extra-careful if you see “Service Account Token Creator” granted beyond resource-level. It’s generally a red flag. For example, if a principal is granted that role at a project’s level, it can impersonate ALL service accounts in that project.

Conclusion

IAM in GCP is deceptively simple on the surface — but as your organization grows, it quickly becomes one of the most critical (and complex) parts of your cloud security posture.

By understanding how roles flow through GCP’s resource hierarchy, applying the principle of least privilege, managing service accounts responsibly, and using tools like Policy Analyzer, Asset Inventory, and even your own automation (like gcp-iam-dumper), you can take full control of your IAM strategy.

Key takeaways:

  • Avoid overpowered roles like Editor and Owner

  • Prefer project isolation over sprawling, monolithic projects

  • Don’t export service account keys if the workload runs in GCP

  • Use resource-level IAM and conditions when appropriate — but test carefully

  • Regularly audit permissions and automate visibility where possible

IAM isn’t just about access — it’s about risk management, accountability, and scaling security with confidence.

0
Subscribe to my newsletter

Read articles from Thibaut Tauveron directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Thibaut Tauveron
Thibaut Tauveron

👋 Hi, I’m a cloud engineer and cybersecurity enthusiast based in Zürich. I’ve worn many hats over the years—developer, DevOps consultant, SRE, cloud architect—and what ties it all together is a passion for building secure, scalable systems that just work. I write about cloud infrastructure, DevSecOps, and anything that helps teams move faster without breaking things. I believe in automation, simplicity, and sharing knowledge—whether through blog posts, open source, or mentoring.