Set limits on granting roles

In large organizations, it can be helpful to let teams independently manage the allow policies for their resources. However, letting a principal grant or revoke all IAM roles can greatly increase your security risk.

You can set limits on the roles that a principal can grant and revoke with Identity and Access Management (IAM) Conditions and the iam.googleapis.com/modifiedGrantsByRole API attribute. These limits let you create limited IAM admins who can manage their own team's allow policies, but only within the boundaries that you have set.

Before you begin

Required permissions

To get the permissions that you need to create limited IAM admins for a project, folder, or organization, ask your administrator to grant you the following IAM roles on the resource that you want to create a limited IAM admin for (project, folder, or organization):

  • To create a limited IAM admin for a project: Project IAM Admin (roles/resourcemanager.projectIamAdmin)
  • To create a limited IAM admin for a folder: Folder Admin (roles/resourcemanager.folderAdmin)
  • To create a limited IAM admin for a project, folder, or organization: Organization Admin (roles/resourcemanager.organizationAdmin)

For more information about granting roles, see Manage access to projects, folders, and organizations.

These predefined roles contain the permissions required to create limited IAM admins for a project, folder, or organization. To see the exact permissions that are required, expand the Required permissions section:

Required permissions

The following permissions are required to create limited IAM admins for a project, folder, or organization:

  • To create a limited IAM admin for a project:
    • resourcemanager.projects.getIamPolicy
    • resourcemanager.projects.setIamPolicy
  • To create a limited IAM admin for a folder:
    • resourcemanager.folders.getIamPolicy
    • resourcemanager.folders.setIamPolicy
  • To create a limited IAM admin for an organization:
    • resourcemanager.organizations.getIamPolicy
    • resourcemanager.organizations.setIamPolicy

You might also be able to get these permissions with custom roles or other predefined roles.

Common use cases

The following sections describe how you can use limited role granting to enable self-service management of allow policies.

Create limited IAM admins

Consider a scenario where you want to let a user, Finn ([email protected]), act as a limited IAM admin for your project. You want Finn to be able to grant and revoke only the App Engine Admin (roles/appengine.appAdmin) and App Engine Viewer (roles/appengine.appViewer) roles for your project.

To grant this limited ability, you conditionally grant Finn the Project IAM Admin role (roles/resourcemanager.projectIamAdmin). The Project IAM Admin role allows Finn to grant and revoke IAM roles, and the condition limits which roles Finn can grant and revoke:

{
  "version": 3,
  "etag": "BwWKmjvelug=",
  "bindings": [
    {
      "members": [
        "user:[email protected]"
      ],
      "role": "roles/owner"
    },
    {
      "members": [
        "[email protected]"
      ],
      "role": "roles/resourcemanager.projectIamAdmin",
      "condition": {
        "title": "only_appengine_admin_viewer_roles",
        "description": "Only allows changes to role bindings with the App Engine Admin or Viewer roles",
        "expression":
          "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/appengine.appAdmin', 'roles/appengine.appViewer'])"
      }
    }
  ]
}

This conditional role binding lets Finn do the following:

  • Grant the App Engine Admin and App Engine Viewer roles for the project.
  • Revoke the App Engine Admin and App Engine Viewer roles for the project.
  • Add, remove, or modify conditions for project-level role bindings that grant the App Engine Admin and App Engine Viewer roles.
  • Perform other actions allowed by the Project IAM Admin role that don't modify the project's allow policy. For example, Finn could use the projects.getIamPolicy method to get the project's allow policy.

This conditional role binding does not let Finn do any of the following:

  • Modify allow policies for resources other than the project.
  • Grant roles other than the App Engine Admin or App Engine Viewer roles.
  • Revoke roles other than the App Engine Admin or App Engine Viewer roles.
  • Add, remove, or modify conditions for role bindings that don't grant the App Engine Admin or App Engine Viewer roles.

Allow users to manage limited IAM admins

Consider a scenario where you want to make a user, Lila, a limited IAM admin for her team. You want Lila to be able to grant and revoke only the Compute Admin role (roles/compute.admin) for her project. However, you also want to let Lila select other users to act as limited IAM admins. In other words, you want to let Lila allow other users to grant and revoke only the Compute Admin role.

You might think that the solution is to grant Lila the Project IAM Admin role (roles/resourcemanager.projectIamAdmin), and then give her the ability to grant or revoke that role for others. However, if you grant Lila the Project IAM Admin role, she could remove the condition from her own role and give herself the ability to grant or revoke any IAM role.

To help prevent this privilege escalation, you instead create a Google group, [email protected], for the project's limited IAM admins. Then, you add Lila to the group and make her a group manager.

After you create the group, you conditionally grant the group the Project IAM Admin role (roles/resourcemanager.projectIamAdmin). The Project IAM Admin role allows group members to grant and revoke IAM roles, and the condition limits which roles they can grant and revoke:

{
  "version": 3,
  "etag": "BwWKmjvelug=",
  "bindings": [
    {
      "members": [
        "user:[email protected]"
      ],
      "role": "roles/owner"
    },
    {
      "members": [
        "[email protected]"
      ],
      "role": "roles/resourcemanager.projectIamAdmin",
      "condition": {
        "title": "only_compute_admin_role",
        "description": "Only allows changes to role bindings for the Compute Admin role",
        "expression":
          "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/compute.admin'])"
      }
    }
  ]
}

As a member of the [email protected] group, Lila can do the following:

  • Grant the Compute Admin role for the project by adding a new binding for the role, or by adding a principal to an existing binding for the role.
  • Revoke the Compute Admin role by removing an existing binding for the role, or by removing a principal from an existing binding for the role.
  • Modify grants for the Compute Admin role by adding, removing, or modifying conditions attached to bindings for the role.
  • Perform other actions allowed by the Project IAM Admin role that don't modify the project's allow policy. For example, she could use the projects.getIamPolicy method to get the project's allow policy.

As a manager of the [email protected] group, Lila can allow other users to grant or revoke the Compute Admin role by adding them to the [email protected] group.

Lila cannot do the following:

  • Give herself the ability to grant or revoke other roles.
  • Modify allow policies for resources other than the project.
  • Grant roles other than the Compute Admin role.
  • Revoke roles other than the Compute Admin role.
  • Add, remove, or modify conditions for role bindings that don't grant the Compute Admin role.

Limit role granting

The following sections explain how to let principals grant or revoke only certain roles.

Write a condition expression to limit role granting

To limit a principal's ability to grant roles, write a condition expression that specifies the roles a principal can grant or revoke.

Use the following format for your condition expression:

api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(roles)

This expression does the following:

  • Gets the API attribute iam.googleapis.com/modifiedGrantsByRole using the api.getAttribute() function.

    For a request to set the allow policy of a resource, this attribute contains the role names from the bindings that the request modifies. For other types of requests, the attribute is not defined. In these cases, the function returns the default value ([]).

  • Uses the hasOnly() Common Expression Language (CEL) function to define and enforce the roles that the principal is allowed to grant or revoke.

    The input for the hasOnly() function is a list of the roles that the principal is allowed to grant or revoke. If the roles in the iam.googleapis.com/modifiedGrantsByRole attribute are included in this list, the function returns true. If they are not, the function returns false.

    If the iam.googleapis.com/modifiedGrantsByRole attribute contains the default value ([]), the function returns true, because [] does not contain any roles not included in the list.

To customize this expression, replace roles with a list of the roles that the principal is allowed to grant or revoke. For example, to let the principal grant or revoke only the Pub/Sub Editor (roles/pubsub.editor) and Pub/Sub Publisher (roles/pubsub.publisher) roles, use the value ['roles/pubsub.editor', 'roles/pubsub.publisher'].

You can include up to 10 values in the list of allowed roles. All of these values must be string constants.

Logical operators for hasOnly() statements

Don't use the && or || operators to join multiple hasOnly() statements in a single condition. If you do, then requests that grant or revoke multiple roles might fail, even if the principal can grant or revoke those roles individually.

For example, consider the following condition:

api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', [])
    .hasOnly(['roles/pubsub.editor']) ||
api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', [])
    .hasOnly(['roles/pubsub.publisher'])

This condition evaluates to true if a request grants either the roles/pubsub.editor role or the roles/pubsub.publisher role, but it evaluates to false if a request grants both the roles/pubsub.editor role and the roles/pubsub.publisher role.

Limit role granting with conditional role bindings

To allow a principal to grant or revoke only certain roles, use the condition expression from the preceding section to create a conditional role binding. Then, add the conditional role binding to a resource's allow policy.

  1. Select a resource that represents the scope that you want to let a principal grant and revoke roles for:

    • If you want to let a principal grant and revoke certain roles for all resources within an organization, select an organization.
    • If you want to let a principal grant and revoke certain roles for all resources within an folder, select a folder.
    • If you want to let a principal grant and revoke certain roles for all resources within a project, select a project.
  2. Select a role that allows a principal to set the allow policy for the resource type you selected (project, folder, or organization). To follow the principle of least privilege, choose one of the following predefined roles:

    • Projects: Project IAM Admin (roles/resourcemanager.projectIamAdmin)
    • Folders: Folder IAM Admin (roles/resourcemanager.folderIamAdmin)
    • Organizations: Organization Admin (roles/resourcemanager.organizationAdmin).

    Alternatively, choose a custom role that includes the resourcemanager.resource-type.setIamPolicy and resourcemanager.resource-type.getIamPolicy permissions, where resource-type is project, folder, or organization.

  3. Conditionally grant a principal your chosen role on the project, folder, or organization you selected.

    The new allow policy is applied, and your principal can modify bindings for only the roles you have allowed.

    Console

    1. In the Google Cloud console, go to the IAM page.

      Go to the IAM page

    2. Make sure the name of your project, folder, or organization appears in the resource selector at the top of the page. The resource selector tells you what project, folder, or organization you are currently working in.

      If you don't see the name of your resource, click the resource selector, then select your resource.

    3. In the list of principals, locate the principal that will grant and revoke roles, and click the button.

    4. In the Edit permissions panel, select the role you chose previously. Then under IAM condition (optional), click Add IAM condition.

    5. In the Edit condition panel, enter a title and optional description for the condition.

    6. Click the Condition editor tab and enter the expression you wrote in Writing a condition expression to limit role granting. This expression limits which roles the principal can grant or revoke.

      For example, the following condition expression limits the principal to granting and revoking the Pub/Sub Editor (roles/pubsub.editor) and Pub/Sub Publisher (roles/pubsub.publisher) roles:

      api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/pubsub.editor', 'roles/pubsub.publisher'])

      Warning: Don't include the following types of roles in the list of allowed roles:

      • Roles with permissions to grant and revoke IAM roles (that is, roles with permission names that end in setIamPolicy).
      • Custom roles that the limited IAM admin can modify. For example, if the limited IAM admin also has the Role Administrator role (roles/iam.roleAdmin) on a project, don't allow them to grant or revoke project-level custom roles.

      Limited IAM admins who can grant and revoke these types of roles can give themselves permission to grant and revoke all IAM roles. See Writing a condition expression to limit role granting for more information.

    7. Click Save to apply the condition.

    8. After the Edit condition panel is closed, click Save in the Edit permissions panel to update your allow policy.

    gcloud

    Allow policies are set using the read-modify-write pattern.

    First, read the allow policy for the resource:

    Execute the get-iam-policy command. This command gets the current allow policy for the resource.

    Command:

    gcloud resource-type get-iam-policy resource-id --format=json > path

    Replace the following values:

    • resource-type: The resource type that you want to let a principal grant or revoke roles for. Use one of the following: projects, resource-manager folders, or organizations.
    • resource-id: Your Google Cloud project, folder, or organization ID.
    • path: The path of the file to download the allow policy to.

    The allow policy is saved in JSON format, for example:

    {
      "bindings": [
        {
          "members": [
            "user:[email protected]"
          ],
          "role": "roles/owner"
        }
      ],
      "etag": "BwWKmjvelug=",
      "version": 1
    }
    

    Next, modify the allow policy.

    To let a principal only modify bindings for certain roles, add the highlighted conditional role binding:

    {
      "bindings": [
        {
          "members": [
            "user:[email protected]"
          ],
          "role": "roles/owner"
        },
        {
          "members": [
            "principal"
          ],
          "role": "role",
          "condition": {
            "title": "title",
            "description": "description",
            "expression":
              "expression"
          }
        }
      ],
      "etag": "BwWKmjvelug=",
      "version": 3
    }

    Replace the following values:

    • principal: The principal that will grant or revoke certain roles. For example, user:[email protected]. To see the formats of each principal type, see the Binding reference.
    • role: The role you chose in the preceding steps. This role must include the setIamPolicy permission for the resource type you chose.
    • title: A string briefly describing the condition. For example, only_pubsub_roles.
    • description: Optional. An additional description for the condition. For example, Only allows granting/revoking the Pub/Sub editor and publisher roles.
    • expression: The expression you wrote in Writing a condition expression to limit role granting. This expression limits which roles that the principal can grant or revoke.

      For example, the following condition expression limits the principal to granting and revoking the Pub/Sub Editor (roles/pubsub.editor) and Pub/Sub Publisher (roles/pubsub.publisher) roles:

      api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/pubsub.editor', 'roles/pubsub.publisher'])

      Warning: Don't include the following types of roles in the list of allowed roles:

      • Roles with permissions to grant and revoke IAM roles (that is, roles with permission names that end in setIamPolicy).
      • Custom roles that the limited IAM admin can modify. For example, if the limited IAM admin also has the Role Administrator role (roles/iam.roleAdmin) on a project, don't allow them to grant or revoke project-level custom roles.

      Limited IAM admins who can grant and revoke these types of roles can give themselves permission to grant and revoke all IAM roles. See Writing a condition expression to limit role granting for more information.

    Finally, write the updated allow policy:

    Set the new allow policy by executing the set-iam-policy command for the resource:

    gcloud resource-type set-iam-policy resource-id path

    Replace the following values:

    • resource-type: The resource type that you want to let a principal grant or revoke roles for. Use one of the following: projects, resource-manager folders, or organizations.
    • resource-id: Your Google Cloud project, folder, or organization ID.
    • path: A path to the file containing the updated allow policy.

    The new allow policy is applied, and the principal will be able to modify bindings for only the roles you have allowed.

    REST

    Allow policies are set using the read-modify-write pattern.

    First, read the allow policy for the resource:

    The Resource Manager API's getIamPolicy method gets a project's, folder's, or organization's allow policy.

    Before using any of the request data, make the following replacements:

    • API_VERSION: The API version to use. For projects and organizations, use v1. For folders, use v2.
    • RESOURCE_TYPE: The resource type whose policy you want to manage. Use the value projects, folders, or organizations.
    • RESOURCE_ID: Your Google Cloud project, organization, or folder ID. Project IDs are alphanumeric strings, like my-project. Folder and organization IDs are numeric, like 123456789012.
    • 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/API_VERSION/RESOURCE_TYPE/RESOURCE_ID:getIamPolicy

    Request JSON body:

    {
      "options": {
        "requestedPolicyVersion": POLICY_VERSION
      }
    }
    

    To send your request, expand one of these options:

    The response contains the resource's allow policy. For example:

    {
      "version": 1,
      "etag": "BwWKmjvelug=",
      "bindings": [
        {
          "role": "roles/owner",
          "members": [
            "user:[email protected]"
          ]
        }
      ]
    }
    

    Next, modify the allow policy.

    Add a conditional role binding that lets a principal grant and revoke only certain roles. Make sure to change the version field to the value 3:

    {
      "version": 3,
      "etag": "BwWKmjvelug=",
      "bindings": [
        {
          "members": [
            "user:[email protected]"
          ],
          "role": "roles/owner"
        },
        {
          "members": [
            "PRINCIPAL"
          ],
          "role": "ROLE",
          "condition": {
            "title": "TITLE",
            "description": "DESCRIPTION",
            "expression":
              "EXPRESSION"
          }
        }
      ]
    }
    • PRINCIPAL: The principal that will grant or revoke certain roles. For example, user:[email protected]. To see the formats of each principal type, see the Binding reference.
    • ROLE: The role you chose in the preceding steps. This role must include the setIamPolicy permission for the resource type you chose.
    • TITLE: A string briefly describing the condition. For example, only_pubsub_roles.
    • DESCRIPTION: Optional. An additional description for the condition. For example, Only allows granting/revoking the Pub/Sub editor and publisher roles.
    • EXPRESSION: The expression you wrote in Writing a condition expression to limit role granting. This expression limits which roles that the principal can grant or revoke.

      For example, the following condition expression limits the principal to granting and revoking the Pub/Sub Editor (roles/pubsub.editor) and Pub/Sub Publisher (roles/pubsub.publisher) roles:

      api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/pubsub.editor', 'roles/pubsub.publisher'])

      Warning: Don't include the following types of roles in the list of allowed roles:

      • Roles with permissions to grant and revoke IAM roles (that is, roles with permission names that end in setIamPolicy).
      • Custom roles that the limited IAM admin can modify. For example, if the limited IAM admin also has the Role Administrator role (roles/iam.roleAdmin) on a project, don't allow them to grant or revoke project-level custom roles.

      Limited IAM admins who can grant and revoke these types of roles can give themselves permission to grant and revoke all IAM roles. See Writing a condition expression to limit role granting for more information.

    Finally, write the updated allow policy:

    The Resource Manager API's setIamPolicy method sets the allow policy in the request as the new allow policy for the project, folder, or organization.

    Before using any of the request data, make the following replacements:

    • API_VERSION: The API version to use. For projects and organizations, use v1. For folders, use v2.
    • RESOURCE_TYPE: The resource type whose policy you want to manage. Use the value projects, folders, or organizations.
    • RESOURCE_ID: Your Google Cloud project, organization, or folder ID. Project IDs are alphanumeric strings, like my-project. Folder and organization IDs are numeric, like 123456789012.
    • POLICY: A JSON representation of the policy that you want to set. For more information about the format of a policy, see the Policy reference.

      For example, to set the policy shown in the previous step, replace POLICY with the following:

      {
        "version": 3,
        "etag": "BwWKmjvelug=",
        "bindings": [
          {
            "members": [
              "user:[email protected]"
            ],
            "role": "roles/owner"
          },
          {
            "members": [
              "principal"
            ],
            "role": "role",
            "condition": {
              "title": "title",
              "description": "description",
              "expression":
                "expression"
            }
          }
        ]
      }

    HTTP method and URL:

    POST https://cloudresourcemanager.googleapis.com/API_VERSION/RESOURCE_TYPE/RESOURCE_ID:setIamPolicy

    Request JSON body:

    {
      "policy": POLICY
    }
    

    To send your request, expand one of these options:

    The response contains the updated allow policy.

What's next