This topic describes how to manage access to specific resources using conditional role bindings in your allow policies. By using resource attributes in a condition expression, you can grant a sub scope of the role binding based on the resource name, resource type, and/or Google Cloud service.
Before you begin
- Read the Identity and Access Management (IAM) Conditions overview to understand the basics of IAM conditional role bindings.
- Review the resource attributes that can be used in a condition expression.
- The resource name attribute can control access to the
following Google Cloud services:
- Apigee
- Application Integration
- Backup and DR Service
- BigQuery
- BigQuery Reservation API
- Bigtable
- Binary Authorization
- Cloud Deploy
- Cloud Key Management Service
- Cloud Logging
- Cloud SQL
- Cloud Storage
- Compute Engine
- Dataform
- Google Kubernetes Engine
- Integration Connectors
- Google Cloud Managed Service for Apache Kafka
- Pub/Sub Lite
- Secret Manager
- Spanner
Required roles
To get the permissions that you need to manage conditional role bindings, ask your administrator to grant you the following IAM roles:
-
To manage access to projects:
Project IAM Admin (
roles/resourcemanager.projectIamAdmin
) on the project -
To manage access to folders:
Folder Admin (
roles/resourcemanager.folderAdmin
) on the folder -
To manage access to projects, folders, and organizations:
Organization Admin (
roles/resourcemanager.organizationAdmin
) on the organization -
To manage access to almost all Google Cloud resources:
Security Admin (
roles/iam.securityAdmin
) on the project, folder, or organization whose resources you want to manage access to
For more information about granting roles, see Manage access to projects, folders, and organizations.
These predefined roles contain the permissions required to manage conditional role bindings. To see the exact permissions that are required, expand the Required permissions section:
Required permissions
The following permissions are required to manage conditional role bindings:
-
To manage access to projects:
-
resourcemanager.projects.getIamPolicy
on the project -
resourcemanager.projects.setIamPolicy
on the project
-
-
To manage access to folders:
-
resourcemanager.folders.getIamPolicy
on the folder -
resourcemanager.folders.setIamPolicy
on the folder
-
-
To manage access to organizations:
-
resourcemanager.organizations.getIamPolicy
on the organization -
resourcemanager.organizations.setIamPolicy
on the organization
-
You might also be able to get these permissions with custom roles or other predefined roles.
Grant access to a group of resources based on resource name prefixes
A conditional role binding can be used to grant access to principals for resources whose resource names match a prefix, such as Compute Engine virtual machine (VM) instances whose names start with a certain string. The resource name prefix is typically used to group resources intended for certain functionality or that have certain properties.
Consider the following example: The software company ExampleCo runs workloads
on certain VM instances that may operate on sensitive healthcare data. Other
non-sensitive workloads must run in the same project, and ExampleCo wants to
ensure its developers have limited access to VM instances that operate on
sensitive data. To accomplish this goal, data-sensitive VM instances are named
with a sensitiveAccess
prefix, and other VM instances are named with a
devAccess
prefix. Then, conditional role bindings are used to ensure that the
developers can remain productive with normal devAccess
VM instances, but
without granting them access to sensitiveAccess
VM instances.
While you can use the resource.name
condition attribute alone to manage
access, it's common to use the resource.type
and resource.service
attributes
as well. When you use these additional attributes, you make it less likely that
a condition will affect access to different resource types with similar names.
The example in this section controls access using both the resource.name
and
resource.type
attributes.
To grant access based on a name prefix to Compute Engine disks and instances in a project:
Console
In the Google Cloud console, go to the IAM page.
From the list of principals, locate the desired principal and click the
button.From the Edit permissions panel, locate the desired role to configure a condition for. Then under IAM condition (optional), click Add IAM condition.
In the Edit condition panel, enter a title and optional description for the condition.
You can add a condition expression using either the Condition builder or the Condition editor. The condition builder provides an interactive interface to select your desired condition type, operator, and other applicable details about the expression. The condition editor provides a text-based interface to manually enter an expression using CEL syntax.
Condition builder:
- Click the Add drop-down and click Grouped conditions.
- From the Condition type drop-down, select Resource > Type.
- From the Operator drop-down, select is.
- From the Resource Type drop-down, select compute.googleapis.com/Disk.
- Click the first Add button immediately beneath the condition you just entered to add another clause to the expression.
- From the Condition type drop-down, select Resource > Name.
- From the Operator drop-down, select Starts with.
- In the Value field, enter the
resource name
in the appropriate format, such as
projects/project-123/zones/us-central1-a/disks/devAccess
for a disk whose name starts withdevAccess
. - To the left of each condition type, click And to ensure both clauses must be true.
- Click the Add button directly above the Save button to add another grouped set of conditions.
- From the Condition type drop-down, select Resource > Type.
- From the Operator drop-down, select is.
- From the Resource Type drop-down, select compute.googleapis.com/Instance.
- Click the first Add button immediately beneath the condition you just entered add another clause to the expression.
- From the Condition type drop-down, select Resource > Name.
- From the Operator drop-down, select Starts with.
- In the Value field, enter the
resource name
in the appropriate format, such as
projects/project-123/zones/us-central1-a/instances/devAccess
for an instance whose name starts withdevAccess
. - To the left of each condition type, click And to ensure both clauses must be true.
- Click the Add button directly above the Save button to add the third grouped set of conditions.
- To ensure this condition does not impact other resources, add the following clauses as well: From the Condition type drop-down, select Resource > Type.
- From the Operator drop-down, select is not.
- From the Resource Type drop-down, select compute.googleapis.com/Disk.
- Click the first Add button immediately beneath the condition you just entered add another clause to the expression.
- From the Condition type drop-down, select Resource > Type.
- From the Operator drop-down, select is not.
- From the Resource Type drop-down, select compute.googleapis.com/Instance.
- To the left of each condition type, click And to ensure both clauses must be true.
When you're finished, the condition builder should look similar to the following:
Click Save to apply the condition.
Once the Edit condition panel is closed, click Save again from the Edit permissions panel to update your allow policy.
Condition editor:
Click the Condition editor tab and enter the following expression:
(resource.type == "compute.googleapis.com/Disk" && resource.name.startsWith("projects/project-123/regions/us-central1/disks/devAccess")) || (resource.type == "compute.googleapis.com/Instance" && resource.name.startsWith("projects/project-123/zones/us-central1-a/instances/devAccess")) || (resource.type != "compute.googleapis.com/Disk" && resource.type != "compute.googleapis.com/Instance")
After entering your expression, you can optionally choose to lint the CEL syntax by clicking Run Linter above the text box on the top-right.
Click Save to apply the condition.
Once the Edit condition panel is closed, click Save again from the Edit permissions panel to update your allow policy.
gcloud
Allow policies are set using the read-modify-write pattern.
Execute the gcloud projects get-iam-policy
command to get the current allow policy for the project. In the following
example, the JSON version of the allow policy is downloaded to a path on disk.
Command:
gcloud projects get-iam-policy project-id --format=json > filepath
The JSON format of the allow policy is downloaded:
{
"bindings": [
{
"members": [
"user:project-owner@example.com"
],
"role": "roles/owner"
},
{
"members": [
"group:devs@example.com"
],
"role": "roles/compute.instanceAdmin"
}
],
"etag": "BwWKmjvelug=",
"version": 1
}
To configure the allow policy with a resource name prefix condition, add the following highlighted condition expression. The gcloud CLI updates the version automatically:
{ "bindings": [ { "members": [ "user:project-owner@example.com" ], "role": "roles/owner" }, { "members": [ "group:devs@example.com" ], "role": "roles/compute.instanceAdmin", "condition": { "title": "Dev_access_only", "description": "Only access to devAccess* VMs", "expression": "(resource.type == 'compute.googleapis.com/Disk' && resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) || (resource.type == 'compute.googleapis.com/Instance' && resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) || (resource.type != 'compute.googleapis.com/Instance' && resource.type != 'compute.googleapis.com/Disk')" } } ], "etag": "BwWKmjvelug=", "version": 3 }
Next, set the new allow policy by executing the
gcloud projects set-iam-policy
command:
gcloud projects set-iam-policy project-id filepath
The new conditional role binding will grant devs@example.com
permissions in
the following way:
All disk and instance permissions are granted only if the resource name starts with
devAccess
All other permissions in the Instance Admin role are granted for all other resource types
REST
Use the read-modify-write pattern to allow access to specific resources.
First, read the allow policy for the project:
The Resource Manager API's
projects.getIamPolicy
method gets a project's allow policy.
Before using any of the request data, make the following replacements:
PROJECT_ID
: Your Google Cloud project ID. Project IDs are alphanumeric strings, likemy-project
.POLICY_VERSION
: The policy version to be returned. Requests should specify the most recent policy version, which is policy version 3. See Specifying a policy version when getting a policy for details.
HTTP method and URL:
POST https://cloudresourcemanager.googleapis.com/v1/projects/PROJECT_ID:getIamPolicy
Request JSON body:
{ "options": { "requestedPolicyVersion": POLICY_VERSION } }
To send your request, expand one of these options:
You should receive a JSON response similar to the following:
{ "version": 1, "etag": "BwWKmjvelug=", "bindings": [ { "role": "roles/owner", "members": [ "user:project-owner@example.com" ] }, { "members": [ "group:devs@example.com" ], "role": "roles/compute.instanceAdmin" } ] }
Next, modify the allow policy so that it allows access to specific
resources. Make sure to change the version
field to the value
3
:
{ "version": 3, "etag": "BwWKmjvelug=", "bindings": [ { "role": "roles/owner", "members": [ "user:project-owner@example.com" ] }, { "role": "roles/compute.instanceAdmin", "members": [ "group:devs@example.com" ], "condition": { "title": "Dev_access_only", "description": "Only access to devAccess* VMs", "expression": "(resource.type == 'compute.googleapis.com/Disk' && resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) || (resource.type == 'compute.googleapis.com/Instance' && resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) || (resource.type != 'compute.googleapis.com/Instance' && resource.type != 'compute.googleapis.com/Disk')" } } ] }
Finally, write the updated allow policy:
The Resource Manager API's
projects.setIamPolicy
method sets the allow policy in the request as the project's new allow policy.
Before using any of the request data, make the following replacements:
PROJECT_ID
: Your Google Cloud project ID. Project IDs are alphanumeric strings, likemy-project
.
HTTP method and URL:
POST https://cloudresourcemanager.googleapis.com/v1/projects/PROJECT_ID:setIamPolicy
Request JSON body:
{ "policy": { "version": 3, "etag": "BwWKmjvelug=", "bindings": [ { "role": "roles/owner", "members": [ "user:project-owner@example.com" ] }, { "role": "roles/compute.instanceAdmin", "members": [ "group:devs@example.com" ], "condition": { "title": "Dev_access_only", "description": "Only access to devAccess* VMs", "expression": "(resource.type == 'compute.googleapis.com/Disk' && resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) || (resource.type == 'compute.googleapis.com/Instance' && resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) || (resource.type != 'compute.googleapis.com/Instance' && resource.type != 'compute.googleapis.com/Disk')" } } ] } }
To send your request, expand one of these options:
The response contains the updated allow policy.
Extract values from resource names
The previous examples show boolean comparisons between the resource name, or the beginning of the resource name, and another value. In some cases, though, you might need to compare a value with a specific part of the resource name that is not at the beginning of the name.
You can use the extract()
function and specify an
extraction template to extract the relevant part of the resource name as a
string. If necessary, you can convert the extracted string to another type, such
as a timestamp. After you extract a value from the resource name, you can
compare that value with other values.
The following examples show condition expressions that use the
extract()
function. For details about the extract()
function, see the
IAM Conditions attribute reference.
Example: Match orders from the last 30 days
Suppose you store order information in multiple Cloud Storage buckets, and the objects in each bucket are organized by date. A typical object name might look similar to this example:
projects/_/buckets/acme-orders-aaa/objects/data_lake/orders/order_date=2019-11-03/aef87g87ae0876
You want to allow a principal to access any order from the last 30 days. The
following condition matches the Cloud Storage objects for these orders. It
uses the duration()
and date()
functions to subtract 30 days (2,592,000
seconds) from the request time, then compare that timestamp with the order date:
resource.type == 'storage.googleapis.com/Object' &&
request.time - duration('2592000s') < date(resource.name.extract('/order_date={date_str}/'))
For details about the date()
and duration()
functions, see the
date/time attribute reference.
Example: Match Compute Engine VMs in any location
Suppose you want to grant a project-level role to a principal for any
Compute Engine VM whose name starts with dev-
, regardless of the VM's
location. You also want the principal to be able to use that role for all other
resource types.
The resource name for a VM uses a format similar to
projects/project-id/zones/zone-id/instances/instance-name
.
The following condition evaluates to true
for VMs with an instance name that
starts with the string dev-
, and for all resource types other than VMs:
resource.type != 'compute.googleapis.com/Instance' ||
resource.name.extract('/instances/{name}').startsWith('dev-')
The text in curly brackets identifies the part of the resource name that is
extracted for comparison. In this example, the extraction template extracts any
characters after the first occurrence of the string /instances/
.
Important usage considerations for resource-based conditions
When adding a resource-based condition, it's important to consider how the condition will affect the principal's permissions.
Custom roles
Consider the following example, which involves
custom roles. An admin wants to create a
custom role that grants access to create VM instances, but only allows the user
to create VM instances in a project with a resource name that starts with the
name prefix staging
, using the disks with the same name prefix.
To accomplish this goal, ensure that the granted role contains the required permissions to create a VM instance, which means permissions on disk and instance resource types. Then, ensure that the condition expression checks the resource name for both disks and instances. Beyond these two types, other permissions in the role are not granted.
The following condition expression will result in unexpected behavior. Permissions to operate on Compute Engine VMs are blocked:
resource.type == 'compute.googleapis.com/Disk' &&
resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')
The following condition expression includes both disks and instances, and will manage access based on the resource name for these two types:
(resource.type == 'compute.googleapis.com/Disk' &&
resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')) ||
(resource.type == 'compute.googleapis.com/Instance' &&
resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/staging'))
The following condition expression includes both disks and instances, and will manage access based on the resource name for these two types. For any other resource type, the condition expression grants the role regardless of the resource name:
(resource.type == 'compute.googleapis.com/Disk' &&
resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')) ||
(resource.type == 'compute.googleapis.com/Instance' &&
resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/staging')) ||
(resource.type != 'compute.googleapis.com/Disk' &&
resource.type != 'compute.googleapis.com/Instance')
Parent-only permissions
In Google Cloud's resource hierarchy, some of the permissions in a role
that affect a child resource are intended to be enforced at the parent level
only. For example, to list crypto keys for Cloud KMS, the user
must be granted the cloudkms.cryptokeys.list
permission on the key ring that
contains the cryptographic keys, not the keys themselves. These kinds of
permissions are called parent-only permissions, and only apply to list
operations.
To properly grant access to *.*.list
permissions when using conditions, the
condition expression should set the resource.service
and resource.type
attributes according to the parent resource type of the target resources to be
listed.
Consider the following examples. Using the Compute Engine example above,
the following expression prevents access to compute.disks.list
and
compute.instances.list
permissions, since the resource on which this
permissions are checked have resource.type
attribute value of
cloudresourcemanager.googleapis.com/Project
.
(resource.type == 'compute.googleapis.com/Disk' &&
resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
(resource.type == 'compute.googleapis.com/Instance' &&
resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess'))
It is common that these list
permissions are granted together with other
permissions for regular operations on the resource. To increase to scope of
grant in this case, you can either extend the scope for the
cloudresourcemanager.googleapis.com/Project
type only, or extend the scope to
all other permissions not of type instance or disk.
(resource.type == 'compute.googleapis.com/Disk' &&
resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
(resource.type == 'compute.googleapis.com/Instance' &&
resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
resource.type == 'cloudresourcemanager.googleapis.com/Project'
or
(resource.type == 'compute.googleapis.com/Disk' &&
resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
(resource.type == 'compute.googleapis.com/Instance' &&
resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
(resource.type != 'compute.googleapis.com/Disk' &&
resource.type != 'compute.googleapis.com/Instance')