Simulating policy changes

This page describes how to simulate an Identity and Access Management (IAM) policy change using Policy Simulator. It also explains how to interpret the results of the simulation, and how to apply the simulated policy if you so choose.

Before you begin

Getting permissions

Before you simulate a policy change, you need to make sure you have the appropriate permissions. Certain permissions are required to run a simulation; others are not required, but help you get the most complete results from the simulation.

To learn more about IAM roles, see Understanding roles.

Required target resource permissions

The target resource of the simulation is the resource whose policies you're simulating.

You need the Simulator Admin role (roles/policysimulator.admin) and the Security Reviewer role (roles/iam.securityReviewer) on the target resource, or another role that includes the following permissions:

  • policysimulator.replays.run
  • cloudassets.assets.searchAllResources
  • service.resource.getIamPolicy, where resource is the resource type of the target resource and service is the name of the Google Cloud service that owns that resource.

Required host resource permissions

The host resource of a simulation is the project, folder, or organization that creates and runs the simulation. The host resource does not need to be related to the target resource in any way.

The way you set the host resource depends on the platform you're using.

Console

The host resource is the project, folder, or organization that appears in the resource selector.

To change the host resource, choose a different project, folder, or organization in the resource selector.

gcloud

The host resource is the current quota project. To set the quota project, use the gcloud auth application-default set-quota-project command.

REST

You manually specify the host resource each time you send a request. See Simulating a policy change on this page for details.

You need the Simulator Admin role (roles/policysimulator.admin) on the host resource, or another role that includes the following permissions:

  • policysimulator.replays.create
  • policysimulator.replays.get
  • policysimulator.replayResults.list

To get the most complete results from a simulation, we recommend that you have certain IAM and Google Workspace permissions. If you don't have some or all of these permissions, you can still run a simulation. However, running a simulation without these permissions could result in an increased number of unknown access changes, because you might not be able to retrieve information that could impact the results of the simulation.

We recommend that you have the Security Reviewer role (roles/iam.securityReviewer) for your organization when running a simulation. Alternatively, if you already have the Security Admin role (roles/iam.securityAdmin), you don't need to be granted any additional roles.

These roles give you the following permissions, which help you get the most complete results from the simulation:

  • iam.roles.get and iam.roles.list on any relevant projects, folders, or organizations where custom roles are defined. A project, folder, or organization is relevant if it is an ancestor or descendant of the resource whose policy you're simulating.
  • service.resource.getIamPolicy, where resource is the name of a resource type that can have an IAM policy and service is the name of the Google Cloud service that owns that resource.

    When you run a simulation, we recommend that you have this permission on each resource that fits these criteria:

    • Policy Simulator supports the resource.
    • The resource has an IAM policy that might impact the user's access. This is true if one of the following applies:

      • The resource is a descendant of the resource whose policy you're simulating, and it appears in the relevant access logs.
      • The resource is an ancestor of the resource whose policy you're simulating.

    For example, imagine that you want to simulate a policy for a project. If the access logs include an access attempt for a Cloud Storage bucket in the project, you need the storage.buckets.getIamPolicy permission on that bucket. If the project has a parent folder with an IAM policy, you also need the resourcemanager.folders.getIamPolicy permission on that folder.

We recommend that you have permission to retrieve group membership information for each Google group in the original policy and the proposed policy.

Google Workspace Super Admins and Group Admins typically have access to view group membership. If you are not a Super Admin or Group Admin, ask your Google Workspace administrator to create a custom Google Workspace administrator role that contains the groups.read privilege (located under Admin API Privileges) and grant it to you. This allows you to view the membership of all groups within your domain, and more effectively simulate policy changes.

Simulating a policy change

Simulate a policy change by following these steps.

Console

The following example demonstrates how to simulate a policy change for a project. However, you can simulate a policy change for any resource that has an IAM policy.

Edit a principal's permissions, then, instead of clicking Save, click Simulate:

  1. In the Cloud Console, go to the IAM page.

    Go to the IAM page

  2. Create a proposed policy change by editing an existing principal's permissions:

    1. Locate the principal whose access you want to edit and click the Edit button on the right.
    2. Edit the principal's access by adding a new role, or by revoking or changing an existing role.
  3. To simulate the proposed change, click Simulate.

  4. After several minutes, the Cloud Console will display the results of the simulated policy as a list of access changes. See Understanding Policy Simulator results on this page for more information.

    If there was no change in access between the existing policy and the simulated policy, the Cloud Console will not display any access changes.

gcloud

To simulate a policy change, follow the read-modify-write pattern, but simulate the policy instead of writing it.

  1. Read the current policy by running the following command:

    gcloud resource-type get-iam-policy resource-id --format=format > filepath
    

    Replace the following values:

    • resource-type: The type of resource that you want to simulate a policy for. For example, projects.
    • resource-id: The ID of the resource whose policy you want to simulate. For example, my-project.
    • format: The value JSON or YAML.
    • filepath: The path to a new output file for the policy.

    For example, the following command gets the policy for the project my-project in JSON format and saves it to the user's home directory:

    gcloud projects get-iam-policy my-project --format=json > ~/policy.json
    
  2. Modify the JSON or YAML policy returned by the get-iam-policy command to reflect the policy changes you want to simulate.

    There are multiple types of changes you can make to the policy binding. For example, you could add or remove a principal from a role binding, or remove a role binding from the policy.

  3. Run the following command to simulate the policy change:

    gcloud iam simulator replay-recent-access \
        full-resource-name \
        filepath \
        --format=format
    

    Replace the following values:

    • full-resource-name: The full resource name of the resource whose policy you want to simulate.

      The full resource name is a URI consisting of the service name and the path to the resource. For example, if you are simulating a policy for a project, you would use //cloudresourcemanager.googleapis.com/projects/project- id, where project-id is the ID of the project whose policy you're simulating.

      For a list of full resource name formats, see Full resource names.

    • filepath: The path to the file containing the modified policy that you want to simulate. For example, ~/proposed_policy.json.

    • format: The format for the response. For example, json or yaml.

    After several minutes, the command prints a list of replay results explaining how the principal's access would change if the proposed policy were applied. These results also list any errors that occurred during the simulation, inclduing any errors due to unsupported resource types.

    See Understanding Policy Simulator results on this page to learn how to read the results. To learn how to save the simulation results rather than printing them, see Saving simulation results.

    The following is a sample response for a policy simulation involving the user my-user@example.com. In this case, if the proposed change were applied, my-user@example.com would potentially no longer have the resourcemanager.projects.list and resourcemanager.projects.get permissions for the project my-project, and would certainly no longer have the resourcemanager.projects.update permission for the project my-project:

    [
      {
        "accessTuple": {
          "fullResourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
          "permission": "resourcemanager.projects.list",
          "principal": "my-user@example.com"
        },
        "diff": {
          "accessDiff": {
            "accessChange": "ACCESS_MAYBE_REVOKED",
            "baseline": {
              "accessState": "GRANTED"
            },
            "simulated": {
              "accessState": "UNKNOWN_INFO_DENIED",
              "errors": [
                {
                  "code": 7,
                  "details": [
                    {
                      "@type": "type.googleapis.com/google.rpc.ResourceInfo",
                      "description": "Missing permission to retrieve IAM policies above the resource in hierarchy.",
                      "resourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
                      "resourceType": "cloudresourcemanager.googleapis.com/projects"
                    }
                  ],
                  "message": "Missing permission to get relevant IAM policies."
                }
              ],
              "policies": [
                {
                  "access": "UNKNOWN_INFO_DENIED",
                  "policy": {}
                }
              ]
            }
          }
        },
        "lastSeenDate": {
          "day": 12,
          "month": 1,
          "year": 2021
        }
      },
      {
        "accessTuple": {
          "fullResourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
          "permission": "resourcemanager.projects.get",
          "principal": "my-user@example.com"
        },
        "diff": {
          "accessDiff": {
            "accessChange": "ACCESS_MAYBE_REVOKED",
            "baseline": {
              "accessState": "GRANTED"
            },
            "simulated": {
              "accessState": "UNKNOWN_INFO_DENIED",
              "errors": [
                {
                  "code": 7,
                  "details": [
                    {
                      "@type": "type.googleapis.com/google.rpc.ResourceInfo",
                      "description": "Missing permission to view group membership.",
                      "resourceName": "group:everyone@example.com",
                      "resourceType": "Google group"
                    }
                  ],
                  "message": "Missing permission to view group membership."
                },
                {
                  "code": 7,
                  "details": [
                    {
                      "@type": "type.googleapis.com/google.rpc.ResourceInfo",
                      "description": "Missing permission to retrieve IAM policies above the resource in hierarchy.",
                      "resourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
                      "resourceType": "cloudresourcemanager.googleapis.com/projects"
                    }
                  ],
                  "message": "Missing permission to get relevant IAM policies."
                }
              ],
              "policies": [
                {
                  "access": "UNKNOWN_INFO_DENIED",
                  "bindingExplanations": [
                    {
                      "access": "UNKNOWN_INFO_DENIED",
                      "memberships": {
                        "group:everyone@example.com": {
                          "membership": "MEMBERSHIP_UNKNOWN_INFO_DENIED"
                        }
                      },
                      "role": "roles/owner"
                    }
                  ],
                  "fullResourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
                  "policy": {
                    "bindings": [
                      {
                        "members": [
                          "group:everyone@example.com"
                        ],
                        "role": "roles/owner"
                      }
                    ],
                    "etag": "BwWgJSIInYA=",
                    "version": 3
                  }
                },
                {
                  "access": "UNKNOWN_INFO_DENIED",
                  "policy": {}
                }
              ]
            }
          }
        },
        "lastSeenDate": {
          "day": 10,
          "month": 1,
          "year": 2021
        }
      },
      {
        "accessTuple": {
          "fullResourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
          "permission": "resourcemanager.projects.update",
          "principal": "my-user@example.com"
        },
        "diff": {
          "accessDiff": {
            "accessChange": "ACCESS_REVOKED",
            "baseline": {
              "accessState": "GRANTED"
            },
            "simulated": {
              "accessState": "NOT_GRANTED"
            }
          }
        },
        "lastSeenDate": {
          "day": 15,
          "month": 1,
          "year": 2021
        }
      },
      {
        "accessTuple": {},
        "error": {
          "code": 12,
          "details": [
            {
              "@type": "type.googleapis.com/google.rpc.ErrorInfo",
              "domain": "policysimulator.googleapis.com",
              "metadata": {
                "permission": "storage.objects.create"
              },
              "reason": "UNSUPPORTED_RESOURCE"
            },
            {
              "@type": "type.googleapis.com/google.rpc.ErrorInfo",
              "domain": "policysimulator.googleapis.com",
              "metadata": {
                "permission": "storage.objects.setIamPolicy"
              },
              "reason": "UNSUPPORTED_RESOURCE"
            },
            {
              "@type": "type.googleapis.com/google.rpc.ErrorInfo",
              "domain": "policysimulator.googleapis.com",
              "metadata": {
                "permission": "storage.objects.delete"
              },
              "reason": "UNSUPPORTED_RESOURCE"
            },
            {
              "@type": "type.googleapis.com/google.rpc.ErrorInfo",
              "domain": "policysimulator.googleapis.com",
              "metadata": {
                "permission": "storage.objects.update"
              },
              "reason": "UNSUPPORTED_RESOURCE"
            },
            {
              "@type": "type.googleapis.com/google.rpc.ErrorInfo",
              "domain": "policysimulator.googleapis.com",
              "metadata": {
                "permission": "pubsub.topics.publish"
              },
              "reason": "UNSUPPORTED_RESOURCE"
            },
            {
              "@type": "type.googleapis.com/google.rpc.ErrorInfo",
              "domain": "policysimulator.googleapis.com",
              "metadata": {
                "permission": "storage.objects.list"
              },
              "reason": "UNSUPPORTED_RESOURCE"
            },
            {
              "@type": "type.googleapis.com/google.rpc.ErrorInfo",
              "domain": "policysimulator.googleapis.com",
              "metadata": {
                "permission": "storage.objects.getIamPolicy"
              },
              "reason": "UNSUPPORTED_RESOURCE"
            },
            {
              "@type": "type.googleapis.com/google.rpc.ErrorInfo",
              "domain": "policysimulator.googleapis.com",
              "metadata": {
                "permission": "storage.objects.get"
              },
              "reason": "UNSUPPORTED_RESOURCE"
            }
          ],
          "message": "Simulator does not yet support all resource types for 8 removed permissions."
        }
      }
    ]
    

    If there was no change in access between the existing policy and the simulated policy, the command prints No access changes found in the replay.

REST

To simulate a policy change, follow the read-modify-write, pattern, but instead of writing the policy, create and run a simulation.

  1. Read the IAM policy for the resource.

    The Resource Manager API's projects.getIamPolicy method gets a project's IAM policy.

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

    • PROJECT_ID: Your Google Cloud project ID. Project IDs are alphanumeric strings, like my-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"
          ]
        },
        {
          "role": "roles/iam.securityReviewer",
          "members": [
            "user:fatima@example.com"
          ]
        }
      ]
    }
    

  2. Modify the returned policy to reflect the changes that you want to simulate.

    There are multiple types of changes you can make to the policy binding. For example, you could add or remove a principal from a role binding, or remove a role binding from the policy.

  3. Create a simulation, or Replay, with the modified policy.

    The Policy Simulator API's replays.create method creates a Replay for a project, folder, or organization.

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

    • host-resource-type: The type of the resource that will host the Replay. This value must be projects, folders, or organizations.
    • host-resource-id: The ID of the host resource, for example, my-project.
    • target-full-resource-name: The full resource name of the resource whose policy you want to simulate. This resource can be any resource that accepts IAM policies, and does not need to be related to the host resource in any way.

      The full resource name is a URI consisting of the service name and the path to the resource. For example, if you are simulating a policy for a project, you would use //cloudresourcemanager.googleapis.com/projects/project- id, where project-id is the ID of the project whose policy you're simulating.

      For a full list of resource name formats, see Full resource names.

    • policy: The policy that you want to simulate. For an example of a policy, see the policy reference.

      To simulate multiple policies, include multiple "object-full-resource-name" : policy pairs in the request body.

    HTTP method and URL:

    POST https://policysimulator.googleapis.com/v1/host-resource-type/host-resource-id/locations/global/replays

    Request JSON body:

    {
      "config": {
        "policyOverlay": {
          "target-full-resource-name" : policy
        }
      }
    }
    

    To send your request, expand one of these options:

    The response contains the name of an operation representing your Replay:

    {
      "name": "operations/6de23e63-f61a-4b8c-b502-34d717d2d7f8",
      "metadata": {
        "type_url": "type.googleapis.com/google.cloud.policysimulator.v1.ReplayOperationMetadata"
      }
    }
    

  4. Poll the operations.get method until the Replay is complete.

    To poll an operation, we recommended that you repeatedly invoke the operations.get method until the response includes the field "done": true and a name field with the name of the completed Replay. Use truncated exponential backoff to introduce a delay between each request.

    The Policy Simulator API's operations.get method gets the state of a Replay.

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

    • operation-name: The name of a Replay operation, including the operations prefix. Copy this value from the name field of a replays.create response. For example: operations/6de23e63-f61a-4b8c-b502-34d717d2d7f8

    HTTP method and URL:

    GET https://policysimulator.googleapis.com/v1/operation-name

    To send your request, expand one of these options:

    Ongoing operations return a response like the following:

    {
      "name": "operations/42083b6b-3788-41b9-ae39-e97d7615a22d",
      "metadata": {
        "@type": "type.googleapis.com/google.cloud.policysimulator.v1.ReplayOperationMetadata",
        "startTime": "2021-01-15T05:34:14.732Z"
      }
    }
    

    Completed operations return a response like the following:

    {
      "name": "operations/89ab4892-9605-4c84-aedb-4fce4fc5195b",
      "metadata": {
        "@type": "type.googleapis.com/google.cloud.policysimulator.v1.ReplayOperationMetadata",
        "startTime": "2021-01-15T05:40:15.922Z"
      },
      "done": true,
      "response": {
        "@type": "type.googleapis.com/google.cloud.policysimulator.v1.Replay",
        "replay": {
          "name": "projects/my-project/locations/global/replays/89ab4892-9605-4c84-aedb-4fce4fc5195b",
          "state": SUCCEEDED,
          "config": {},
          "resultsSummary": {
            "logCount": 1319,
            "unchangedCount": 1169,
            "differenceCount": 149,
            "errorCount": 1,
            "oldestDate": {
              "year": 2020,
              "month": 10,
              "day": 15
            },
            "newestDate": {
              "year": 2021,
              "month": 1,
              "day": 12
            }
          }
        }
      }
    }
    

  5. Get the results of the Replay.

    The Policy Simulator API's replays.results.list method gets the results of a Replay.

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

    • replay-name: The name of the Replay that you want to retrieve results for. Copy this value from the response.replay.name field of an operations.get response. Include any resource type and location prefixes. For example, "projects/my-project/locations/global/replays/89ab4892-9605-4c84-aedb-4fce4fc5195b"
    • page-size: Optional. The maximum number of results to return from this request. If not specified, the server will determine the number of results to return. If the number of results is greater than the page size, the response contains a pagination token that you can use to retrieve the next page of results.
    • page-token: Optional. The pagination token returned in an earlier response from this method. If specified, the list of results will start where the previous request ended.

    HTTP method and URL:

    GET https://policysimulator.googleapis.com/v1/replay-name/results?pageSize=page-size&pageToken=page-token

    To send your request, expand one of these options:

    The response contains a list of results explaining how the principal's access would change if the proposed policy were applied. These results also list any errors that occurred during the simulation, most notably, any errors due to unsupported resource types

    See Understanding Policy Simulator results on this page to learn how to read the results.

    The following is a sample response for a policy simulation involving the user my-user@example.com. In this case, if the proposed change were applied, my-user@example.com would potentially no longer have the resourcemanager.projects.list and resourcemanager.projects.get permissions for the project my-project, and would certainly no longer have the resourcemanager.projects.update permission for the project my-project:

    {
      "replayResults": [
        {
          "accessTuple": {
            "fullResourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
            "permission": "resourcemanager.projects.list",
            "principal": "my-user@example.com"
          },
          "lastSeenDate": {
            "day": 27,
            "month": 3,
            "year": 2020
          },
          "diff": {
            "accessDiff": {
              "accessChange": "ACCESS_MAYBE_REVOKED",
              "baseline": {
                "accessState": "GRANTED"
              },
              "simulated": {
                "accessState": "UNKNOWN_INFO_DENIED",
                "errors": [
                  {
                    "code": 7,
                    "message": "Missing permission to get relevant IAM policies.",
                    "details": [
                      {
                        "@type": "type.googleapis.com/google.rpc.ResourceInfo",
                        "description": "Missing permission to retrieve IAM policies above the resource in hierarchy.",
                        "resourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
                        "resourceType": "cloudresourcemanager.googleapis.com/projects"
                      }
                    ]
                  }
                ],
                "policies": [
                  {
                    "access": "UNKNOWN_INFO_DENIED",
                    "policy": {}
                  }
                ]
              }
            }
          }
        },
        {
          "accessTuple": {
            "fullResourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
            "permission": "resourcemanager.projects.get",
            "principal": "my-user@example.com"
          },
          "lastSeenDate": {
            "day": 27,
            "month": 3,
            "year": 2020
          },
          "diff": {
            "accessDiff": {
              "accessChange": "ACCESS_MAYBE_REVOKED",
              "baseline": {
                "accessState": "GRANTED"
              },
              "simulated": {
                "accessState": "UNKNOWN_INFO_DENIED",
                "errors": [
                  {
                    "code": 7,
                    "message": "Missing permission to view group membership.",
                    "details": [
                      {
                        "@type": "type.googleapis.com/google.rpc.ResourceInfo",
                        "description": "Missing permission to view group membership.",
                        "resourceName": "group:everyone@example.com",
                        "resourceType": "Google group"
                      }
                    ]
                  },
                  {
                    "code": 7,
                    "message": "Missing permission to get relevant IAM policies.",
                    "details": [
                      {
                        "@type": "type.googleapis.com/google.rpc.ResourceInfo",
                        "description": "Missing permission to retrieve IAM policies above the resource in hierarchy.",
                        "resourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
                        "resourceType": "cloudresourcemanager.googleapis.com/projects"
                      }
                    ]
                  }
                ],
                "policies": [
                  {
                    "access": "UNKNOWN_INFO_DENIED",
                    "bindingExplanations": [
                      {
                        "access": "UNKNOWN_INFO_DENIED",
                        "memberships": {
                          "group:everyone@example.com": {
                            "membership": "MEMBERSHIP_UNKNOWN_INFO_DENIED"
                          }
                        },
                        "role": "roles/owner"
                      }
                    ],
                    "fullResourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
                    "policy": {
                      "bindings": [
                        {
                          "members": [
                            "group:everyone@example.com"
                          ],
                          "role": "roles/owner"
                        }
                      ],
                      "etag": "BwWgJSIInYA=",
                      "version": 3
                    }
                  },
                  {
                    "access": "UNKNOWN_INFO_DENIED",
                    "policy": {}
                  }
                ]
              }
            }
          }
        },
        {
          "accessTuple": {
            "fullResourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
            "permission": "resourcemanager.projects.update",
            "principal": "my-user@example.com"
          },
          "lastSeenDate": {
            "day": 27,
            "month": 3,
            "year": 2020
          },
          "diff": {
            "accessDiff": {
              "accessChange": "ACCESS_REVOKED",
              "baseline": {
                "accessState": "GRANTED"
              },
              "simulated": {
                "accessState": "NOT_GRANTED"
              }
            }
          }
        },
        {
          "accessTuple": {},
          "error": {
            "code": 12,
            "message": "Simulator does not yet support all resource types for 8 removed permissions.",
            "details": [
              {
                "@type": "type.googleapis.com/google.rpc.Status",
                "code": 12,
                "message": "Simulator does not yet support all resource types for 8 removed permissions.",
                "details": [
                  {
                    "@type": "type.googleapis.com/google.rpc.ErrorInfo",
                    "domain": "policysimulator.googleapis.com",
                    "metadata": {
                      "permission": "storage.objects.create"
                    },
                    "reason": "UNSUPPORTED_RESOURCE"
                  },
                  {
                    "@type": "type.googleapis.com/google.rpc.ErrorInfo",
                    "domain": "policysimulator.googleapis.com",
                    "metadata": {
                      "permission": "storage.objects.setIamPolicy"
                    },
                    "reason": "UNSUPPORTED_RESOURCE"
                  },
                  {
                    "@type": "type.googleapis.com/google.rpc.ErrorInfo",
                    "domain": "policysimulator.googleapis.com",
                    "metadata": {
                      "permission": "storage.objects.delete"
                    },
                    "reason": "UNSUPPORTED_RESOURCE"
                  },
                  {
                    "@type": "type.googleapis.com/google.rpc.ErrorInfo",
                    "domain": "policysimulator.googleapis.com",
                    "metadata": {
                      "permission": "storage.objects.update"
                    },
                    "reason": "UNSUPPORTED_RESOURCE"
                  },
                  {
                    "@type": "type.googleapis.com/google.rpc.ErrorInfo",
                    "domain": "policysimulator.googleapis.com",
                    "metadata": {
                      "permission": "pubsub.topics.publish"
                    },
                    "reason": "UNSUPPORTED_RESOURCE"
                  },
                  {
                    "@type": "type.googleapis.com/google.rpc.ErrorInfo",
                    "domain": "policysimulator.googleapis.com",
                    "metadata": {
                      "permission": "storage.objects.list"
                    },
                    "reason": "UNSUPPORTED_RESOURCE"
                  },
                  {
                    "@type": "type.googleapis.com/google.rpc.ErrorInfo",
                    "domain": "policysimulator.googleapis.com",
                    "metadata": {
                      "permission": "storage.objects.getIamPolicy"
                    },
                    "reason": "UNSUPPORTED_RESOURCE"
                  },
                  {
                    "@type": "type.googleapis.com/google.rpc.ErrorInfo",
                    "domain": "policysimulator.googleapis.com",
                    "metadata": {
                      "permission": "storage.objects.get"
                    },
                    "reason": "UNSUPPORTED_RESOURCE"
                  }
                ]
              }
            ]
          }
        }
      ],
      "nextPageToken": "AWukk3zjv80La+chWx6WNt7X8czGPLtP792gRpkNVEV/URZ/VdWzxmuJKr"
    }
    

    If there was no change in access between the existing policy and the simulated policy, the request returns an empty list ({}).

Understanding Policy Simulator results

Policy Simulator reports the impact of a proposed policy change as a list of access changes. Each access change represents an access attempt from the last 90 days that would have a different outcome under the proposed policy than under the current policy.

Policy Simulator also lists any errors that occurred during the simulation, which helps you identify potential gaps in the simulation.

The presentation of these changes and errors depends on the platform you're using.

Console

The Policy Simulator results page displays the results of the simulation in several different sections:

  • Policy Overview: This section lists the original role that the principal had on the resource, as well as the role they will have in the proposed policy. The original role is listed under Existing policy, and the proposed role is listed under Proposed policy.

  • Summary of access changes: This section displays the number of access attempts from the past 90 days that would have gone differently if the proposed policy had been in place. This summary includes any potential or unknown access changes, as well as any errors that occurred during the simulation.

  • Access changes over the past 90 days: This section lists each access attempt from the past 90 days that would have gone differently if the proposed policy had been in place. Each entry, or access change, includes the type of the access change, along with the resource, principal, and permission involved in the access attempt.

    There are several different types of access changes:

    Access change Details
    Access revoked The principal had access under the current policy, but will no longer have access after the proposed change.
    Access potentially revoked

    This result can occur for the following reasons:

    • The principal had access under the current policy, but their access under the proposed policy is unknown.
    • The principal's access under the current policy is unknown, but they will not have access after the proposed change.
    Access gained The principal did not have access under the current policy, but will have access after the proposed change.
    Access potentially gained

    This result can occur for the following reasons:

    • The principal did not have access under the current policy, but their access after the proposed change is unknown.
    • The principal's access under the current policy is unknown, but they will have access after the proposed change.
    Access unknown The principal's access under both the current policy and proposed policy is unknown, and the proposed changes might affect the principal's access.
    Error An error occurred during the simulation.

    To view additional details about an access change, click on the access change. This opens the Access change details panel, which displays additional information about the access change, including the principal's existing access, the principal's proposed access, and additional details about the access change result.

gcloud

When you use the replay-recent-access command, the gcloud tool's response contains a list of replayResults.

Each replay result describes an access attempt whose result would have been different if the proposed policy had been in place at the time of the attempt. For example, the following replay result shows that my-user@example.com used the resourcemanager.projects.update permission in the past to perform an action in the project my-project. However, if the proposed policy had been in place, they would have been denied access.

{
  "accessTuple": {
    "fullResourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
    "permission": "resourcemanager.projects.update",
    "principal": "my-user@example.com"
  },
  "lastSeenDate": {
    "day": 15,
    "month": 1,
    "year": 2021
  },
  "diff": {
    "accessDiff": {
      "baseline": {
        "accessState": "GRANTED"
      },
      "simulated": {
        "accessState": "NOT_GRANTED"
      },
      "accessChange": "ACCESS_REVOKED"
    }
  }
}

Each replay result has the following fields:

  • accessTuple: The access attempt that the result relates to. This field includes the resource, permission, and principal that were involved in the access attempt.

  • lastSeenDate: The date that the access attempt was last made.

  • diff.accessDiff or error: If the replay of an access attempt is successful, the result contains a diff.accessDiff field that reports the difference between the results of the access attempt under the current policy and under the proposed policy. If the replay attempt is not successful, the replay result contains an error field with a description of the error. To learn more about simulation errors, see Errors on this page.

Each access diff has the following components:

  • baseline: The access result when using the current policy. This is reported as one of the following values: GRANTED, NOT_GRANTED, UNKNOWN_CONDITIONAL, or UNKNOWN_INFO_DENIED. If the result is UNKNOWN_CONDITIONAL or UNKNOWN_INFO_DENIED, the response will also list any errors associated with the unknown information, as well as the policies associated with that error. For more information about UNKNOWN values, see Unknown results on this page.
  • simulated: The access result when using the proposed policy. This is reported as one of the following values: GRANTED, NOT_GRANTED, UNKNOWN_CONDITIONAL, or UNKNOWN_INFO_DENIED. If the result is UNKNOWN_CONDITIONAL or UNKNOWN_INFO_DENIED, the response will also list any errors associated with the unknown information, as well as the policies associated with that error. For more information about UNKNOWN values, see Unknown results on this page.
  • accessChange: The change between the baseline access state and the simulated access state. Refer to the following table for a list of potential values:

    Access change Details
    ACCESS_REVOKED The principal had access under the current policy, but will no longer have access after the proposed change.
    ACCESS_MAYBE_REVOKED

    This result can occur for the following reasons:

    • The principal had access under the current policy, but their access under the proposed policy is unknown.
    • The principal's access under the current policy is unknown, but they will not have access after the proposed change.
    ACCESS_GAINED The principal did not have access under the current policy, but will have access after the proposed change.
    ACCESS_MAYBE_GAINED

    This result can occur for the following reasons:

    • The principal did not have access under the current policy, but their access after the proposed change is unknown.
    • The principal's access under the current policy is unknown, but they will have access after the proposed change.
    UNKNOWN_CHANGE The principal's access under both the current policy and proposed policy is unknown, and the proposed changes might affect the principal's access.

REST

When you call the replays.results.list method, the response contains a list of replayResults.

Each replay result describes an access attempt whose result would have been different if the proposed policy had been in place at the time of the attempt. For example, the following replay result shows that my-user@example.com used the resourcemanager.projects.update permission in the past to perform an action in the project my-project. However, if the proposed policy had been in place, they would have been denied access.

{
  "accessTuple": {
    "fullResourceName": "//cloudresourcemanager.googleapis.com/projects/my-project",
    "permission": "resourcemanager.projects.update",
    "principal": "my-user@example.com"
  },
  "lastSeenDate": {
    "day": 15,
    "month": 1,
    "year": 2021
  },
  "diff": {
    "accessDiff": {
      "baseline": {
        "accessState": "GRANTED"
      },
      "simulated": {
        "accessState": "NOT_GRANTED"
      },
      "accessChange": "ACCESS_REVOKED"
    }
  }
}

Each replay result has the following fields:

  • accessTuple: The access attempt that the result relates to. This field includes the resource, permission, and principal that were involved in the access attempt.

  • lastSeenDate: The date that the access attempt was last made.

  • diff.accessDiff or error: If the replay of an access attempt is successful, the result contains a diff.accessDiff field that reports the difference between the results of the access attempt under the current policy and under the proposed policy. If the replay attempt is not successful, the replay result contains an error field with a description of the error. To learn more about simulation errors, see Errors on this page.

Each access diff has the following components:

  • baseline: The access result when using the current policy. This is reported as one of the following values: GRANTED, NOT_GRANTED, UNKNOWN_CONDITIONAL, or UNKNOWN_INFO_DENIED. If the result is UNKNOWN_CONDITIONAL or UNKNOWN_INFO_DENIED, the response will also list any errors associated with the unknown information, as well as the policies associated with that error. For more information about UNKNOWN values, see Unknown results on this page.
  • simulated: The access result when using the proposed policy. This is reported as one of the following values: GRANTED, NOT_GRANTED, UNKNOWN_CONDITIONAL, or UNKNOWN_INFO_DENIED. If the result is UNKNOWN_CONDITIONAL or UNKNOWN_INFO_DENIED, the response will also list any errors associated with the unknown information, as well as the policies associated with that error. For more information about UNKNOWN values, see Unknown results on this page.
  • accessChange: The change between the baseline access state and the simulated access state. Refer to the following table for a list of potential values:

    Access change Details
    ACCESS_REVOKED The principal had access under the current policy, but will no longer have access after the proposed change.
    ACCESS_MAYBE_REVOKED

    This result can occur for the following reasons:

    • The principal had access under the current policy, but their access under the proposed policy is unknown.
    • The principal's access under the current policy is unknown, but they will not have access after the proposed change.
    ACCESS_GAINED The principal did not have access under the current policy, but will have access after the proposed change.
    ACCESS_MAYBE_GAINED

    This result can occur for the following reasons:

    • The principal did not have access under the current policy, but their access after the proposed change is unknown.
    • The principal's access under the current policy is unknown, but they will have access after the proposed change.
    UNKNOWN_CHANGE The principal's access under both the current policy and proposed policy is unknown, and the proposed changes might affect the principal's access.

Unknown results

If an access result is unknown, it means that Policy Simulator did not have enough information to fully evaluate the access attempt.

Console

If an access result is unknown, the access change details panel reports the reason it was unknown, plus the specific roles, policies, group memberships, and conditionals it was unable to access or evaluate.

There are several reasons that a result can be unknown:

  • Role info denied: The principal running the simulation did not have permission to see the role details for one or more of the roles being simulated.
  • Unable to access policy: The principal running the simulation did not have permission to get the IAM policy for one or more of the resources involved in the simulation.
  • Membership info denied: The principal running the simulation did not have permission to view the members of one or more of the groups included in the proposed policy.
  • Unsupported condition: There is a conditional role binding in the policy that is being tested. Policy Simulator does not support conditions, so the binding could not be evaluated.

gcloud

In the gcloud tool, the simulation results will report the reason that the result is unknown in the access diff.

The reason that the access result is unknown will be one of the following:

  • UNKNOWN_INFO_DENIED: The user does not have permission to access information that is necessary for evaluating the access state. This can happen for any of the following reasons:

    • The user does not have permission to retrieve the policy that is being simulated, or they don't have permission to retrieve policies for resources in the access logs.
    • The user does not have permission to see into a group membership.
    • The user can't retrieve the necessary role information.

    To learn what information was missing, see the error information following the reported access state.

  • UNKNOWN_CONDITIONAL: There is a conditional role binding in the policy that is being tested. Policy Simulator does not support conditions, so the binding could not be evaluated.

If the result is unknown, the accessDiff field of the policy (baseline or simulated) contains an errors field describing why the information was unknown, and a policies field listing the policies associated with the errors. For more information about errors, see Errors on this page.

REST

In the REST API, the simulation results will report the reason that the result is unknown in the access diff.

The reason that the access result is unknown will be one of the following:

  • UNKNOWN_INFO_DENIED: The user does not have permission to access information that is necessary for evaluating the access state. This can happen for any of the following reasons:

    • The user does not have permission to retrieve the policy that is being simulated, or they don't have permission to retrieve policies for resources in the access logs.
    • The user does not have permission to see into a group membership.
    • The user can't retrieve the necessary role information.

    To learn what information was missing, see the error information following the reported access state.

  • UNKNOWN_CONDITIONAL: There is a conditional role binding in the policy that is being tested. Policy Simulator does not support conditions, so the binding could not be evaluated.

If the result is unknown, the accessDiff field of the policy (baseline or simulated) contains an errors field describing why the information was unknown, and a policies field listing the policies associated with the errors. For more information about errors, see Errors on this page.

Errors

Policy Simulator also reports any errors that occurred during the simulation. It's important to review these errors so that you understand the potential gaps in the simulation.

Console

There are several types of errors that Policy Simulator might report:

  • Operation errors: The simulation could not be run. Policy Simulator reports operation errors at the top of the results page.

    If the error message states the simulation could not be run because there are too many logs in your project or organization, then you cannot run a simulation on the resource.

    If you get this error for another reason, try running the simulation again. If you still cannot run the simulation, contact policy-simulator-feedback@google.com.

  • Replay errors: A replay of a single access attempt was unsuccessful, so Policy Simulator could not determine if the result of the access attempt would change under the proposed policy.

    The Cloud Console lists replay errors in the Access changes over the past 90 days table. The Access change details panel for each error includes an error message to help you understand the issue, as well as the resource and permission that were being simulated when the error occurred.

  • Unsupported resource type errors: The proposed policy affects permissions associated with an unsupported resource type, which Policy Simulator cannot simulate.

    Policy Simulator lists these permissions in the simulation results so that you know which permissions it was unable to simulate.

gcloud

In the gcloud tool's simulation results, errors can appear in two places:

  • The replayResult.error field: If the replay attempt was not successful, Policy Simulator reports the error in the replayResult.error field. If a replay result contains this field, it does not contain a diff field.
  • The replayResult.diff.accessDiff.policy-type.errors field, where policy-type is baseline or simulated. If the replay attempt was successful, but the result was UNKNOWN_INFO_DENIED or UNKNOWN_CONDITIONAL, Policy Simulator reports the reason that the result was unknown in this field.

Policy Simulator generates the following types of errors:

Error Error code Details
GENERIC_INTERNAL_ERROR 13 The simulation failed due to an internal error. To resolve, try running the simulation again. If the simulation still fails, contact policy-simulator-feedback@google.com.
INVALID_ACCESS_TUPLE 3 Policy Simulator could not replay the access attempt because it contained an invalid permisison, resource name, or principal.
OUT_OF_RANGE_GROUP_TOO_LARGE 11 Policy Simulator could not evaluate the principal's membership in the group because the group has too many subgroups. This error is associated with UNKNOWN_INFO_DENIED access changes.
PERMISSION_DENIED_ON_GROUP_MEMBERSHIP 7 Policy Simulator could not evaluate a user's access because the caller does not have permission to view group membership. This error is associated with UNKNOWN_INFO_DENIED access changes.
PERMISSION_DENIED_ON_IAM_POLICY 7 Policy Simulator could not evaluate a user's access because the caller does not have permission to retrieve an IAM policy. This type of error is associated with UNKNOWN_INFO_DENIED access changes.
PERMISSION_DENIED_ON_IAM_ROLE 7 Policy Simulator could not evaluate a user's access because the caller does not have permission to retrieve the permissions in an IAM role. This type of error is associated with UNKNOWN_INFO_DENIED access changes.
PERMISSION_DENIED_ON_PARENT_IAM_POLICY 7 Policy Simulator could not evaluate a user's access because the caller does not have permission to retrieve an ancestor resource's IAM policy. This type of error is associated with UNKNOWN_INFO_DENIED access changes.
UNIMPLEMENTED_MEMBER_TYPE 12 The access tuple contains a principal type that Policy Simulator does not support.
UNIMPLEMENTED_MEMBER 12 The access tuple contains a principal that Policy Simulator does not support.
UNIMPLEMENTED_CONDITION 12 The access tuple contains a condition, which Policy Simulator does not support. This type of error is associated with UNKNOWN_CONDITIONAL access changes.
LOG_SIZE_TOO_LARGE 8 The resource is associated with too many access logs, so Policy Simulator could not run the simulation. See Maximum log replay size on the Policy Simulator concepts page for details.
UNSUPPORTED_RESOURCE 12

The proposed policy changes permissions associated with unsupported resource types. This error appears in the replayResult.error field and contains a list of the permissions associated with unsupported resource types. For example:

"error": {
  "code": 12,
  "details": [
    {
      "@type": "type.googleapis.com/google.rpc.ErrorInfo",
      "domain": "policysimulator.googleapis.com",
      "metadata": {
        "permission": "storage.objects.create"
      },
      "reason": "UNSUPPORTED_RESOURCE"
    },
    {
      "@type": "type.googleapis.com/google.rpc.ErrorInfo",
      "domain": "policysimulator.googleapis.com",
      "metadata": {
        "permission": "storage.objects.setIamPolicy"
      },
      "reason": "UNSUPPORTED_RESOURCE"
    },
    {
      "@type": "type.googleapis.com/google.rpc.ErrorInfo",
      "domain": "policysimulator.googleapis.com",
      "metadata": {
        "permission": "storage.objects.get"
      },
      "reason": "UNSUPPORTED_RESOURCE"
    }
  ],
  "message": "unsupported-permissions-error-message"
}

For more information about unsupported resource types, see Support levels for resource types on the Policy Simulator concepts page.

REST

In REST API simulation results, errors can appear in two places:

  • The replayResult.error field: If the replay attempt was not successful, Policy Simulator reports the error in the replayResult.error field. If a replay result contains this field, it does not contain a diff field.
  • The replayResult.diff.accessDiff.policy-type.errors field, where policy-type is baseline or simulated. If the replay attempt was successful, but the result was UNKNOWN_INFO_DENIED or UNKNOWN_CONDITIONAL, Policy Simulator reports the reason that the result was unknown in this field.

Policy Simulator generates the following types of errors:

Error Error code Details
GENERIC_INTERNAL_ERROR 13 The simulation failed due to an internal error. To resolve, try running the simulation again. If the simulation still fails, contact policy-simulator-feedback@google.com.
INVALID_ACCESS_TUPLE 3 Policy Simulator could not replay the access attempt because it contained an invalid permisison, resource name, or principal.
OUT_OF_RANGE_GROUP_TOO_LARGE 11 Policy Simulator could not evaluate the principal's membership in the group because the group has too many subgroups. This error is associated with UNKNOWN_INFO_DENIED access changes.
PERMISSION_DENIED_ON_GROUP_MEMBERSHIP 7 Policy Simulator could not evaluate a user's access because the caller does not have permission to view group membership. This error is associated with UNKNOWN_INFO_DENIED access changes.
PERMISSION_DENIED_ON_IAM_POLICY 7 Policy Simulator could not evaluate a user's access because the caller does not have permission to retrieve an IAM policy. This type of error is associated with UNKNOWN_INFO_DENIED access changes.
PERMISSION_DENIED_ON_IAM_ROLE 7 Policy Simulator could not evaluate a user's access because the caller does not have permission to retrieve the permissions in an IAM role. This type of error is associated with UNKNOWN_INFO_DENIED access changes.
PERMISSION_DENIED_ON_PARENT_IAM_POLICY 7 Policy Simulator could not evaluate a user's access because the caller does not have permission to retrieve an ancestor resource's IAM policy. This type of error is associated with UNKNOWN_INFO_DENIED access changes.
UNIMPLEMENTED_MEMBER_TYPE 12 The access tuple contains a principal type that Policy Simulator does not support.
UNIMPLEMENTED_MEMBER 12 The access tuple contains a principal that Policy Simulator does not support.
UNIMPLEMENTED_CONDITION 12 The access tuple contains a condition, which Policy Simulator does not support. This type of error is associated with UNKNOWN_CONDITIONAL access changes.
LOG_SIZE_TOO_LARGE 8 The resource is associated with too many access logs, so Policy Simulator could not run the simulation. See Maximum log replay size on the Policy Simulator concepts page for details.
UNSUPPORTED_RESOURCE 12

The proposed policy changes permissions associated with unsupported resource types. This error appears in the replayResult.error field and contains a list of the permissions associated with unsupported resource types. For example:

"error": {
  "code": 12,
  "details": [
    {
      "@type": "type.googleapis.com/google.rpc.ErrorInfo",
      "domain": "policysimulator.googleapis.com",
      "metadata": {
        "permission": "storage.objects.create"
      },
      "reason": "UNSUPPORTED_RESOURCE"
    },
    {
      "@type": "type.googleapis.com/google.rpc.ErrorInfo",
      "domain": "policysimulator.googleapis.com",
      "metadata": {
        "permission": "storage.objects.setIamPolicy"
      },
      "reason": "UNSUPPORTED_RESOURCE"
    },
    {
      "@type": "type.googleapis.com/google.rpc.ErrorInfo",
      "domain": "policysimulator.googleapis.com",
      "metadata": {
        "permission": "storage.objects.get"
      },
      "reason": "UNSUPPORTED_RESOURCE"
    }
  ],
  "message": "unsupported-permissions-error-message"
}

For more information about unsupported resource types, see Support levels for resource types on the Policy Simulator concepts page.

Applying a simulated policy change

To apply a simulated policy change, follow these steps:

Console

  1. Click Apply proposed changes.

  2. In the confirmation dialog, click Apply to confirm the change.

gcloud

Use the set-iam-policy command, and provide a path to the JSON file that contains the simulated policy that you want to apply:

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

Provide the following values:

  • resource-type: The resource type whose policy you want to update. For example, projects.
  • resource-id: The ID of the resource whose policy you want to update. For example, my-project.
  • filepath: The path to a file that contains the updated policy.

The response contains the updated policy. If you treat IAM policies as code and store them in a version-control system, you should store the policy that the gcloud tool returns, not the JSON file that contains the simulated policy.

REST

Set the proposed policy as the resource's new policy.

The Resource Manager API's projects.setIamPolicy method sets the policy in the request as the project's new IAM policy.

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

  • PROJECT_ID: Your Google Cloud project ID. Project IDs are alphanumeric strings, like my-project.
  • 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.

HTTP method and URL:

POST https://cloudresourcemanager.googleapis.com/v1/projects/PROJECT_ID:setIamPolicy

Request JSON body:

{
  "policy": {
    POLICY
  }
}

To send your request, expand one of these options:

The response contains the updated policy.


Saving simulation results

If you are using the gcloud tool, you can save Policy Simulator results as JSON, YAML, or CSV files.

Saving as JSON or YAML

To save a simulation's results as a JSON or YAML file, add the following flag to the replay-recent-access command when running the simulation:

--output=output-format > filename

Replace the following values:

  • output-format: The language of the exported file, either json or yaml.
  • filename: A name for the exported file.

Saving as CSV

To save a CSV file, add the following flag to the replay-recent-access command when running the simulation:

--flatten="diffs[]" --format=csv(output-fields) > filename

Replace the following values:

  • output-fields: A comma-separated list of the fields that you want to include in the exported results. For example, diffs.accessTuple.principal, diffs.accessTuple.permission.
  • filename: A name for the exported file.

Optionally, you can add additional fields, such as errors[] to the --flatten flag. Adding fields to the --flatten flag allows the elements in those fields to be listed on separate lines in the CSV file.

The following is an example of a replay-recent-access command that saves the most important fields of the simulation results as the CSV file simulation-results.csv:

gcloud iam simulator replay-recent-access --flatten="diffs[]" \
    --format="csv(diffs.accessTuple.principal, diffs.accessTuple.permission, \
    diffs.accessTuple.fullResourceName, diffs.diff.accessDiff.accessChange, \
    diffs.diff.accessDiff.baseline.accessState, \
    diffs.diff.accessDiff.simulated.accessState)" \
    //cloudresourcemanager.googleapis.com/projects/my-project \
    proposed-policy.json > simulation-results.csv

This example simulates proposed-policy.json for the project my-project and saves the results as simulation-results.csv. This CSV file contains the following fields: principal, permission, resource, access change, baseline access state, and simulated access state.

For more information on formatting with the gcloud tool, see formats.

What's next