IAM Conditions in GCP Explained: Examples, CEL, and Use Cases

Temporary access grants are one of the most common causes of permission creep. You give a contractor access to logs for a three-week engagement. The project ends. Six months later they still have access, but nobody remembered to remove the binding. IAM Conditions solve this by encoding the expiry or scope restriction directly into the binding. When the condition becomes false, the access disappears automatically. No cleanup step, no reminder needed.

What are IAM Conditions in GCP?

IAM Conditions let you attach a conditional rule to a role binding in a GCP IAM policy. Normally, a binding says: “this principal has this role”. With a condition, it says: “this principal has this role, but only when this expression is true”.

The condition is evaluated at request time by Google Cloud. If the expression evaluates to true, the binding applies and access is granted. If the expression evaluates to false, the binding is ignored for that request, as if it were not there at all.

This is useful when you need time-limited access, resource-scoped access within a broader project binding, or context-aware access without creating a separate set of roles. It is not a replacement for correct least privilege design. It is a tool you layer on top of it.

IAM Conditions in simple terms

You are not changing what the role grants. You are adding a rule that controls when the binding is active. The access is real and full-strength when the condition is met. Conditions are not a way to make access “weaker”. They are a way to make it conditional.

Conditions are written in Common Expression Language (CEL), but you do not need to be a programmer to understand them. Most useful conditions are simple one-line expressions like “the current time is before this date” or “the resource name starts with this prefix”.

How IAM Conditions work

When Google Cloud receives an API request, it evaluates the IAM policy on the resource being accessed. If a binding includes a condition, the evaluation follows these steps:

  1. A principal (user, group, or service account) makes a request to a GCP resource.
  2. Google Cloud looks up the IAM policy on that resource and any parent resources.
  3. For each binding that matches the principal and role, it checks whether a condition is present.
  4. If there is a condition, it evaluates the CEL expression against the request attributes (time, resource name, tags).
  5. If the condition evaluates to true, the binding applies and the permission is granted.
  6. If the condition evaluates to false, that binding is skipped. The request is denied by that binding unless another binding covers the permission.

A few things to be clear about, because they trip people up:

  • The condition lives on the binding, not on the role.
  • It does not modify the role or narrow the list of permissions the role grants.
  • It does not replace binding the role at the right resource level. It is an addition to that, not a substitute.
  • If the condition is true, the role applies with full strength.
A broad role is still broad

A condition restricting access to business hours on a binding with roles/editor still grants full project-wide editor access during those hours. Conditions control when the binding is active. They do not change what it grants. Fix the role first. Then add a condition if you need one.

Policy version 3

Conditions require policy version 3. When you add a condition via gcloud, the policy version is automatically upgraded. If you later read and re-apply the policy with a tool that does not support version 3, the conditions may be silently stripped. This is a real risk when mixing Terraform and manual gcloud changes on the same policy. See IAM Policy Structure for more on versions and safe policy updates.

Common attributes used in IAM Conditions

Conditions can only reference attributes that GCP exposes in the request context. The four most commonly used are:

request.time

The timestamp of the API request. Use this for expiry dates, business-hours windows, and date-range restrictions.

request.time < timestamp("2026-09-30T23:59:59Z")
Most universally supported

request.time is supported by the widest range of GCP services. If you are new to IAM Conditions, start here. Expiry dates are the lowest-risk way to experiment with conditions in production.

resource.name

The full resource identifier string, such as projects/_/buckets/my-bucket/objects/reports/q3.csv. Use this to restrict a binding to resources whose name matches a specific prefix. This is the most common way to scope access within a shared bucket without creating a separate one.

resource.name.startsWith("projects/_/buckets/my-bucket/objects/etl-team/")

resource.type

The resource type string, such as storage.googleapis.com/Object or bigquery.googleapis.com/Dataset. Use this when a binding includes a role that covers multiple service types and you want to restrict it to one. Less commonly needed than resource.name.

resource.type == "storage.googleapis.com/Object"

resource.tags

Resource tags attached to a resource (not labels; tags are a separate system in GCP). Use this for environment-based or sensitivity-level access patterns, where you want bindings to apply automatically to resources tagged with a particular value without listing them by name. Requires that resources are consistently tagged before the condition can take effect.

resource.tags["123456789/environment"] == "staging"
Tag support varies

Not every attribute is supported by every GCP service. Most modern services support request.time and resource.name. Support for resource.tags varies more widely. Check the IAM conditions documentation for the specific service before writing tag-based conditions for production use.

IAM Conditions examples

Expiring contractor access

Grant a contractor access to logs for the duration of an audit engagement. When the end date passes, the binding becomes inactive automatically with no manual cleanup required.

gcloud projects add-iam-policy-binding my-app-prod \
  --member="user:contractor@external.com" \
  --role="roles/logging.viewer" \
  --condition="expression=request.time < timestamp('2026-09-30T23:59:59Z'),\
title=expires-end-of-q3-2026,\
description=Temporary access for infrastructure audit ending Q3 2026"

The title field appears in the IAM Console and in audit logs. Always write a descriptive title that explains the intent and expected expiry. The next reviewer should not have to guess why the condition exists.

Restricting a service account to one folder in a shared bucket

An ETL pipeline shares a bucket with other teams. Rather than giving it a separate bucket, you can restrict it to only access objects under its own prefix using a resource name condition on the binding.

gcloud storage buckets add-iam-policy-binding gs://my-app-prod-shared \
  --member="serviceAccount:etl-job@my-app-prod.iam.gserviceaccount.com" \
  --role="roles/storage.objectAdmin" \
  --condition="expression=resource.name.startsWith(\
'projects/_/buckets/my-app-prod-shared/objects/etl-team/'),\
title=restrict-to-etl-prefix,\
description=ETL job is restricted to the etl-team/ prefix in the shared bucket"
Simpler alternative to consider

If this ETL job is the only workload needing a prefix restriction, a dedicated bucket scoped to that service account is simpler and harder to misconfigure. Use prefix conditions when sharing a bucket genuinely makes operational or cost sense, not just because it is possible.

Business-hours access to a sensitive dataset

Restrict an analyst’s BigQuery access to weekday business hours in a specific timezone, reducing the risk window for credential misuse outside working hours.

# Set this condition expression on the BigQuery dataset IAM binding.
# This restricts access to Monday-Friday, 09:00-18:00 London time.
#
# expression:
#   request.time.getDayOfWeek("Europe/London") >= 1 &&
#   request.time.getDayOfWeek("Europe/London") <= 5 &&
#   request.time.getHours("Europe/London") >= 9 &&
#   request.time.getHours("Europe/London") < 18
#
# getDayOfWeek returns 0 for Sunday, 6 for Saturday.
# Adjust the timezone string to match the user's actual working hours.

When to use IAM Conditions

IAM Conditions are well suited to these situations:

  • Temporary access that should expire automatically. Contractors, incident responders, or auditors who need short-term access are the ideal use case. Set an expiry date in the condition and you never have to remember to revoke it.

  • Restricting access within a shared resource. When multiple teams share a bucket and giving each team its own bucket is not practical, a resource.name prefix condition can fence each team into its own area of the bucket.

  • Time-windowed access for sensitive operations. Restricting production database access to business hours, or limiting deployment permissions to your change-window schedule, adds a timing control without extra infrastructure.

  • Tag-based access without managing explicit resource lists. When your resources are consistently tagged by environment or sensitivity, resource.tags conditions let bindings apply automatically to the right resources without listing them by name.

  • Adding a time dimension without creating additional roles. When you already have the right role at the right scope and you just need to limit when it is active, a condition is the correct tool.

When not to use IAM Conditions

Conditions are not the right tool in every situation. Know when something else solves the problem more cleanly:

Do not use a condition to fix a role that is too broad

Conditions do not narrow what a role grants. If roles/editor is too permissive, the fix is a narrower predefined role from the predefined role list. Adding a time or resource condition on top of an overly broad role still grants that broad access whenever the condition is true.

  • When you should just bind the role at a narrower resource level. If a service account needs access to one specific bucket, grant the role on that bucket directly using a resource-level binding. That is simpler, clearer, and harder to get wrong than a project-level binding with a resource name condition.

  • When you need organisation-wide guardrails. If you want to prevent anyone from creating resources in a specific region, or block public access to Cloud Storage across all projects, those are governance constraints. Use Organisation Policies rather than conditions on individual bindings.

  • When simpler IAM design would be clearer. A conditional binding is harder to read, audit, and debug than a plain binding. If a simpler approach (narrower role, lower resource scope, or a separate service account) achieves the same outcome, prefer it.

  • When the service does not support conditions. Using conditions on a binding that includes permissions from an unsupported service may result in the condition being silently ignored, granting broader access than intended. Verify service support first.

IAM Conditions vs narrower resource scoping

This is the most common source of confusion. Both approaches can restrict which resources an identity can access, but they work differently and have different trade-offs.

Resource-level IAM binding

You bind a role directly on a specific resource, for example granting roles/storage.objectViewer on a single bucket rather than at project level. This is the simplest and most readable approach. GCP enforces the scope automatically; there is no expression to maintain or debug.

# Grant access to one specific bucket only
gcloud storage buckets add-iam-policy-binding gs://my-team-data \
  --member="serviceAccount:analytics@my-app-prod.iam.gserviceaccount.com" \
  --role="roles/storage.objectViewer"

Project-level binding with a resource name condition

You bind the role at project level but add a condition that restricts it to resources matching a name prefix. This achieves a similar effect but requires correctly written CEL, is harder to read in the policy output, and applies to all resources in the project that share the prefix, even ones you did not intend.

Use resource-level binding when you can. Use a resource name condition when you genuinely cannot use a resource-level binding, such as when you want to grant access to an object prefix within a Cloud Storage bucket rather than the bucket itself.

The right order of operations

Choose the correct role first. Then bind it at the narrowest applicable resource level. Then, if you also need a time or context dimension, add a condition. Do not use a condition to compensate for a binding that is already too broad.

IAM Conditions vs Organisation Policies

  • IAM Conditions control when an individual binding is active. They are attached to a specific role grant for a specific principal. They say: “this identity has this role, but only when X is true.”

  • Organisation Policies enforce governance constraints across all projects in a folder or organisation, regardless of identity. They say: “this operation is not allowed for anyone.” For example: no public Cloud Storage buckets, no resources outside approved regions, no service account key creation.

If you want to prevent a class of action across your whole organisation and not just for one principal, use an Organisation Policy. IAM Conditions cannot replicate that. For the specific case of location restrictions, see Restricting Resource Locations.

How to test and troubleshoot IAM Conditions

Conditions add complexity. A mistake can either grant broader access than intended or block legitimate access silently. Test carefully before rolling out to production.

  • Test with a low-risk principal first. Before applying a condition-bearing binding to a production service account, test the expression on a non-critical identity to confirm it evaluates as expected.

  • Verify the policy version is 3. Run gcloud projects get-iam-policy PROJECT_ID and check the version field. If you see version: 1 on a policy that should contain a condition, the condition has been stripped. See Managing IAM with gcloud for safe policy read and write patterns.

  • Check denied requests in audit logs. When a condition evaluates to false, the denial is recorded in the Policy Denied audit log. The log entry includes the condition expression and its result, which tells you exactly why the request was blocked.

  • Confirm service support before production rollout. For services that do not fully support conditions, the condition may be ignored and grant broader access than intended.

  • Keep expressions simple and focused. A single-purpose expression is easier to verify than one combining time, resource name, and tags. If you need multiple constraints, consider whether splitting them across separate bindings is clearer.

# Find Policy Denied log entries to see blocked conditional access
gcloud logging read \
  'logName="projects/my-app-prod/logs/cloudaudit.googleapis.com%2Fpolicy"' \
  --project=my-app-prod \
  --limit=20 \
  --format='table(timestamp,protoPayload.authenticationInfo.principalEmail,protoPayload.status.message)'

For broader strategies around reading and acting on audit log data, see Cloud Audit Logs and Detecting Suspicious Activity with Logs.

Common mistakes

  1. Using a condition instead of narrowing the resource scope. If you want to restrict a service account to one bucket, bind the role on that bucket directly. A project-level binding with a resource.name condition achieves a similar effect but is harder to read, easier to get wrong, and breaks in subtle ways if the resource name format changes.

  2. Thinking the condition narrows the role’s permissions. A condition only controls whether the binding is active. When the condition evaluates to true, the full role applies with all its permissions. If the role is too broad, the fix is a narrower role, not a condition.

  3. Writing complex CEL expressions. Expressions combining time, resource name, and tags in one block are hard to read, harder to test, and easy to get wrong in ways that silently block legitimate access or grant access you did not intend. Keep each condition focused on one thing.

  4. Not writing a descriptive condition title. The title field appears in the Console and in audit logs. Without a clear title like expires-contractor-q3-2026, the next person reviewing the policy cannot tell why the condition was added or when it should expire.

  5. Not testing the expression before production rollout. A typo in a CEL expression, an incorrect resource name format, or an off-by-one error in a time comparison can block access entirely or grant it when it should be denied. Test on a non-production binding first, then check the audit logs to confirm the expression is behaving as expected.

  6. Assuming all services support the same condition attributes. Support for resource.tags in particular varies. Do not assume a condition that works correctly on Cloud Storage will behave identically on a less common service.

Frequently asked questions

What are IAM Conditions in GCP?

IAM Conditions are rules you attach to an IAM role binding. The binding is only active when the condition evaluates to true. You write conditions in Common Expression Language (CEL), and they can test attributes like the time of the request, the resource name, the resource type, or resource tags. They live inside the binding itself and require policy version 3.

What can IAM Conditions check?

The most commonly supported attributes are: request.time (the timestamp of the request), resource.name (the full resource identifier), resource.type (the resource type string), and resource.tags (tags assigned to the resource). Not every attribute is supported by every GCP service, so check service-specific documentation before writing conditions for production use.

Do IAM Conditions replace IAM roles?

No. Conditions only control when a binding is active. They do not change which permissions the role grants or which resources the binding applies to. A broad role like roles/editor is still broad when the condition evaluates to true. Get role selection and resource scope right first, then add conditions to layer on a time or context dimension.

Do IAM Conditions work with all GCP services?

No. A service must explicitly support the IAM conditions framework for conditions on bindings to be evaluated. Most modern GCP services do, but some older or specialised services do not. On unsupported services, a condition on a binding that includes permissions for that service may be ignored. Always verify service support before relying on conditions in production.

What happens when an IAM Condition evaluates to false?

The binding is treated as if it does not exist. The request is denied by that binding unless another binding grants the required permission without a condition. The denial is recorded in the Policy Denied audit log, which includes the condition expression, its result, and the requesting identity. This gives you visibility into exactly when and why conditional access was blocked.

Last verified: 21 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.