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
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 rolesUse 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
Role | Purpose |
roles/iam.serviceAccountUser | Lets 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.serviceAccountTokenCreator | Allows 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
andOwner
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.
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.