> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Google.iam.admin.v1.createrole remediation

### Event Information

* **Event purpose & scope**
  * `google.iam.admin.v1.CreateRole` is emitted when a new **custom IAM role** is created in a GCP project or organization via the IAM Admin API.
  * It typically appears in Cloud Audit Logs (Admin Activity) and contains details such as `name`, `stage`, and the list of `includedPermissions`.

* **Risk & governance implications**
  * Creating custom roles can **expand or refine access** beyond standard predefined roles, which directly impacts least-privilege posture.
  * From a compliance viewpoint (e.g., ISO 27001, SOC 2, PCI DSS, HIPAA), this event is sensitive and should be monitored to detect introduction of overly permissive roles or unauthorized privilege escalation paths.

* **Practical actions & controls**
  * Continuously export and monitor these events (via Cloud Logging → Log Router → SCC / SIEM) and alert on:
    * Roles created with broad permissions (e.g., `*Admin`, `*Owner`, or high-impact APIs like `iam.roles.*`, `resourcemanager.*`).
  * Enforce approval workflows and change management around custom-role creation (e.g., via policy-as-code with Terraform + code review, and Organization Policy constraints), and periodically review custom roles against least-privilege and regulatory requirements.

### Examples

* **Creation of Over-Privileged Custom Role (e.g., `roles/custom.orgAdmin`)**
  * Includes permissions like `resourcemanager.organizations.setIamPolicy`, `iam.serviceAccounts.actAs`, `iam.roles.update`
  * Violates least privilege (ISO 27001 A.9 / SOC2 CC6) and enables lateral movement and privilege escalation across the org
  * Mitigation: Require change control + approval for new roles, enable IAM Recommender, restrict `iam.roles.create` to a tightly-controlled admin group

* **Backdoor Role for Service Account Impersonation**
  * Custom role with `iam.serviceAccounts.getAccessToken`, `iam.serviceAccounts.signJwt`, `iam.serviceAccounts.actAs` created and bound to a low-profile identity
  * Enables silent persistence and data exfiltration via impersonated high‑priv SA; impacts PCI-DSS 7.1 / HIPAA access control
  * Mitigation: Monitor `google.iam.admin.v1.CreateRole` logs for SA-related permissions, alert on roles containing sensitive IAM permissions, and enforce SCP/org policy to block dangerous permission sets

* **Role Enabling Unrestricted Data Access (e.g., Wide Storage / BigQuery Read)**
  * Custom role with `storage.objects.list/get` on `*` buckets or `bigquery.tables.getData` on critical datasets created for a generic “analytics” group
  * Breaks data minimization and segregation (GDPR Art. 25 / 32; SOC2 CC6.6); enables bulk data exfiltration from regulated datasets
  * Mitigation: Pre-approved role catalog, mandatory data owner approval for roles including data-read permissions, and continuous review of bindings to sensitive projects/folders/org-level resources

### Remediation

#### Using Console

* **Over-Privileged Custom Role (e.g., `roles/custom.orgAdmin`)**
  * In GCP Console, go to **IAM & Admin → Roles**, filter for **Custom** and locate the role (e.g., `roles/custom.orgAdmin`); click the role → **Permissions** tab → remove high‑risk permissions like `resourcemanager.organizations.setIamPolicy`, `iam.roles.*`, `iam.serviceAccounts.actAs`, then **Save** a reduced version or create a new, narrower role and migrate bindings before deleting the old one.
  * Go to **IAM & Admin → IAM**, filter by **Role** = the over-privileged custom role, and systematically remove bindings from identities that don’t require it; replace with least‑privilege predefined roles or approved custom roles, documenting approvals per change‑control requirements (ISO 27001 A.9 / SOC2 CC6).
  * In **IAM & Admin → IAM Recommender** (or **Recommendations**), enable and review right‑sizing recommendations for roles, and in **IAM & Admin → Roles** restrict **Role Administrator / Role Creator** (e.g., `roles/iam.roleAdmin`, custom creators) to a tightly‑controlled admin group only.

* **Backdoor Role for Service Account Impersonation**
  * In **IAM & Admin → Roles**, search custom roles for permissions like `iam.serviceAccounts.getAccessToken`, `iam.serviceAccounts.signJwt`, `iam.serviceAccounts.actAs`; open each flagged role and either remove these permissions or delete the role if not strictly justified, then replace with safer alternatives.
  * In **IAM & Admin → IAM**, filter by **Role** to find which identities are bound to these backdoor‑style roles; immediately remove bindings from low‑profile identities, rotate any impacted service account keys/tokens, and review **Logs Explorer** for `google.iam.credentials.*` usage (to assess potential PCI‑DSS / HIPAA impact).
  * At the org level, use **Organization Policies** (e.g., `constraints/iam.allowedPolicyMemberDomains`, `constraints/iam.disableServiceAccountKeyCreation`) and, if using Google Cloud Organization Policy SCP‑like controls, define guardrails that disallow roles containing the combination of sensitive IAM permissions from being created or bound without central security approval.

* **Role Enabling Unrestricted Data Access (Storage / BigQuery)**
  * In **IAM & Admin → Roles**, identify any “analytics” or wide data‑read custom roles containing `storage.objects.list`, `storage.objects.get`, `bigquery.tables.getData` across broad resources; clone and trim these to dataset/bucket‑specific or column‑level (via BQ views) access, then phase out the wide role by updating all bindings.
  * For Storage: go to **Cloud Storage → Buckets → \[sensitive bucket] → Permissions**, and remove the generic “analytics” group or wide custom role where present; for BigQuery: open **BigQuery → \[project] → \[dataset] → Share** and remove overly broad groups/roles, granting granular dataset/table access aligned with GDPR/SOC2 data minimization.
  * Implement a pre‑approved role catalog by documenting allowed data‑access roles; then periodically use **IAM & Admin → IAM → Download** (export IAM policy) or **Cloud Asset Inventory** to list and review bindings granting data‑read permissions to sensitive projects/folders/org resources, and remove or narrow any that go beyond the catalog, with mandatory data‑owner sign‑off for any exceptions.

#### Using CLI

* **Over-Privileged Custom Role (e.g., `roles/custom.orgAdmin`)**
  * Identify & review risky custom roles (e.g., with `resourcemanager.organizations.setIamPolicy`, `iam.serviceAccounts.actAs`, `iam.roles.update`):
    ```bash theme={null}
    gcloud iam roles list --organization=ORG_ID --format="value(name)" \
    | xargs -I{} gcloud iam roles describe {} --organization=ORG_ID \
      --format="json(name,includedPermissions)" \
    | jq 'select(.includedPermissions[] | IN("resourcemanager.organizations.setIamPolicy","iam.serviceAccounts.actAs","iam.roles.update"))'
    ```
  * Replace with least‑privilege roles, remove dangerous permissions, and enforce change control:
    * Update role: `gcloud iam roles update ROLE_ID --organization=ORG_ID --remove-permissions=PERM1,PERM2`
    * Lock down creation:
      ```bash theme={null}
      gcloud organizations add-iam-policy-binding ORG_ID \
        --member="group:controlled-admins@org.com" \
        --role="roles/iam.organizationRoleAdmin"
      ```
  * Enable continuous tuning and monitoring:
    * Turn on IAM Recommender for projects/org; require CAB approval for new org‑level roles; add org policy to restrict who can create/modify custom roles.

***

* **Backdoor Role for Service Account Impersonation**
  * Detect suspicious custom roles (SA impersonation/signing permissions):
    ```bash theme={null}
    gcloud iam roles list --organization=ORG_ID --format="value(name)" \
    | xargs -I{} gcloud iam roles describe {} --organization=ORG_ID --format="json(name,includedPermissions)" \
    | jq 'select(.includedPermissions[] | IN("iam.serviceAccounts.getAccessToken","iam.serviceAccounts.signJwt","iam.serviceAccounts.actAs"))'
    ```
  * Remove or constrain backdoor roles and bindings:
    ```bash theme={null}
    # Strip SA-impersonation permissions
    gcloud iam roles update ROLE_ID --organization=ORG_ID \
      --remove-permissions=iam.serviceAccounts.getAccessToken,iam.serviceAccounts.signJwt,iam.serviceAccounts.actAs

    # Review & clean bindings
    gcloud organizations get-iam-policy ORG_ID --format=json \
      | jq '.bindings[] | select(.role=="organizations/ORG_ID/roles/ROLE_ID")'
    gcloud organizations remove-iam-policy-binding ORG_ID \
      --member="user:LOW_PROFILE@org.com" \
      --role="organizations/ORG_ID/roles/ROLE_ID"
    ```
  * Prevent re‑creation:
    * Add alerting on `google.iam.admin.v1.CreateRole` and on roles containing `iam.serviceAccounts.*` sensitive perms; implement org policy / SCP equivalent (for multi‑cloud) to disallow those permissions in custom roles except in a whitelisted project/folder.

***

* **Role Enabling Unrestricted Data Access (Storage / BigQuery)**
  * Discover overly broad data-read custom roles and where they’re bound:
    ```bash theme={null}
    # List custom roles with storage or BQ data-read perms
    gcloud iam roles list --organization=ORG_ID --format="value(name)" \
    | xargs -I{} gcloud iam roles describe {} --organization=ORG_ID --format="json(name,includedPermissions)" \
    | jq 'select(.includedPermissions[] | test("storage.objects.list|storage.objects.get|bigquery.tables.getData"))'

    # For a given role, list project-level bindings
    gcloud projects list --format="value(projectId)" \
    | xargs -I{} gcloud projects get-iam-policy {} --format=json \
      | jq '.bindings[] | select(.role=="organizations/ORG_ID/roles/ANALYTICS_ROLE")'
    ```
  * Replace with cataloged, pre‑approved least‑privilege analytics roles and require data owner approval for regulated datasets:
    * Create constrained roles (per dataset/bucket):
      ```bash theme={null}
      gcloud iam roles update ANALYTICS_ROLE --organization=ORG_ID \
        --remove-permissions=storage.objects.list,storage.objects.get,bigquery.tables.getData
      ```
    * Grant fine‑grained access at resource level instead:
      ```bash theme={null}
      # Storage: per-bucket
      gcloud storage buckets add-iam-policy-binding gs://SENSITIVE_BUCKET \
        --member="group:analytics@org.com" \
        --role="roles/storage.objectViewer"

      # BigQuery: per-dataset
      bq update --dataset --description="Approved analytics access" \
        project:dataset
      ```
  * Continuously review bindings to sensitive resources (PCI / HIPAA / GDPR datasets):
    * Periodically export IAM:
      ```bash theme={null}
      gcloud asset search-all-iam-policies --scope=organizations/ORG_ID \
        --query='policy.bindings.role:"roles/bigquery.dataViewer" OR policy.bindings.role:"roles/storage.objectViewer"' \
        --format=json
      ```
    * Compare against approved data owner list; remove unauthorized bindings and document in access review records for ISO 27001 / SOC 2 evidence.

#### Using Python

* **Detect & flag over‑privileged custom roles (org-wide)**
  * Use Cloud Asset Inventory + IAM Admin API to list custom roles and highlight dangerous permissions (e.g., `resourcemanager.organizations.setIamPolicy`, `iam.serviceAccounts.actAs`, `iam.roles.update`, wide data‑read perms):
  ```python theme={null}
  from googleapiclient.discovery import build
  from google.oauth2 import service_account

  SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]
  SA_KEY = "service-account.json"
  ORG_ID = "123456789012"

  DANGEROUS_PERMS = {
      "resourcemanager.organizations.setIamPolicy",
      "iam.serviceAccounts.actAs",
      "iam.serviceAccounts.getAccessToken",
      "iam.serviceAccounts.signJwt",
      "iam.roles.update",
      "iam.roles.create",
      "storage.objects.list",
      "storage.objects.get",
      "bigquery.tables.getData",
  }

  def get_iam_service():
      creds = service_account.Credentials.from_service_account_file(
          SA_KEY, scopes=SCOPES
      )
      return build("iam", "v1", credentials=creds)

  def list_custom_roles(org_id):
      svc = get_iam_service()
      roles = []
      req = svc.organizations().roles().list(
          parent=f"organizations/{org_id}", view="FULL"
      )
      while req is not None:
          resp = req.execute()
          roles.extend(resp.get("roles", []))
          req = svc.organizations().roles().list_next(req, resp)
      return [r for r in roles if r.get("name", "").startswith(f"organizations/{org_id}/roles/")]

  def find_dangerous_roles(org_id):
      roles = list_custom_roles(org_id)
      flagged = []
      for r in roles:
          perms = set(r.get("includedPermissions", []))
          hit = perms & DANGEROUS_PERMS
          if hit:
              flagged.append({
                  "name": r["name"],
                  "title": r.get("title"),
                  "stage": r.get("stage"),
                  "dangerous_permissions": list(hit)
              })
      return flagged

  if __name__ == "__main__":
      risky = find_dangerous_roles(ORG_ID)
      for r in risky:
          print(r)
  ```
  * Use this output to drive change control reviews required by ISO 27001 A.9 / SOC2 CC6; deprecate / delete or reduce these roles and enforce that `iam.roles.create` / `iam.roles.update` are only granted to a controlled admin group.

* **Hunt for backdoor SA‑impersonation roles and bindings**
  * Extend scanning to detect roles containing SA‑impersonation / token permissions and enumerate who can use them:
  ```python theme={null}
  from googleapiclient.discovery import build
  from google.oauth2 import service_account

  SA_KEY = "service-account.json"
  ORG_ID = "123456789012"
  SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]

  SA_IMPERSONATION_PERMS = {
      "iam.serviceAccounts.getAccessToken",
      "iam.serviceAccounts.signJwt",
      "iam.serviceAccounts.actAs",
  }

  def get_services():
      creds = service_account.Credentials.from_service_account_file(
          SA_KEY, scopes=SCOPES
      )
      return (
          build("iam", "v1", credentials=creds),
          build("cloudresourcemanager", "v3", credentials=creds),
      )

  def list_org_custom_roles(iam_svc, org_id):
      roles = []
      req = iam_svc.organizations().roles().list(
          parent=f"organizations/{org_id}", view="FULL"
      )
      while req is not None:
          resp = req.execute()
          roles.extend(resp.get("roles", []))
          req = iam_svc.organizations().roles().list_next(req, resp)
      return roles

  def list_projects(crm_svc, org_id):
      projects = []
      req = crm_svc.projects().list(parent=f"organizations/{org_id}")
      while req is not None:
          resp = req.execute()
          projects.extend(resp.get("projects", []))
          req = crm_svc.projects().list_next(req, resp)
      return projects

  def get_policy(crm_svc, resource_name):
      req = crm_svc.projects().getIamPolicy(
          resource=resource_name,
          body={"options": {"requestedPolicyVersion": 3}},
      )
      return req.execute()

  if __name__ == "__main__":
      iam_svc, crm_svc = get_services()
      roles = list_org_custom_roles(iam_svc, ORG_ID)
      sa_roles = {
          r["name"]: set(r.get("includedPermissions", [])) & SA_IMPERSONATION_PERMS
          for r in roles
          if set(r.get("includedPermissions", [])) & SA_IMPERSONATION_PERMS
      }

      print("Backdoor‑like custom roles:")
      for name, perms in sa_roles.items():
          print(f"{name}: {perms}")

      print("\nBindings for these roles (project level):")
      projects = list_projects(crm_svc, ORG_ID)
      for p in projects:
          pid = p["projectId"]
          policy = get_policy(crm_svc, pid)
          for b in policy.get("bindings", []):
              if b["role"] in sa_roles:
                  print(f"Project {pid} -> {b['role']} -> {b.get('members', [])}")
  ```
  * Use findings to: remove SA‑impersonation permissions from generic roles, rebind such roles only to tightly‑controlled break‑glass identities, and add org policies / SCP‑equivalent (e.g., Org Policy constraints + CI/CD policy checks) to prevent roles combining these permissions, satisfying PCI‑DSS 7.1 / HIPAA access‑control expectations.

* **Limit wide data‑read roles and enforce approvals**
  * Scan for any custom role with broad Storage / BigQuery read and track where they’re bound (supports GDPR Art.25/32 & SOC2 CC6.6 reviews):
  ```python theme={null}
  from googleapiclient.discovery import build
  from google.oauth2 import service_account

  SA_KEY = "service-account.json"
  ORG_ID = "123456789012"
  SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]

  DATA_READ_PERMS = {
      "storage.objects.list",
      "storage.objects.get",
      "storage.objects.getIamPolicy",
      "bigquery.tables.getData",
      "bigquery.tables.get",
      "bigquery.datasets.get",
  }

  def get_services():
      creds = service_account.Credentials.from_service_account_file(
          SA_KEY, scopes=SCOPES
      )
      return (
          build("iam", "v1", credentials=creds),
          build("cloudresourcemanager", "v3", credentials=creds),
      )

  def list_custom_roles(iam_svc, org_id):
      roles = []
      req = iam_svc.organizations().roles().list(
          parent=f"organizations/{org_id}", view="FULL"
      )
      while req is not None:
          resp = req.execute()
          roles.extend(resp.get("roles", []))
          req = iam_svc.organizations().roles().list_next(req, resp)
      return roles

  def list_projects(crm_svc, org_id):
      projects = []
      req = crm_svc.projects().list(parent=f"organizations/{org_id}")
      while req is not None:
          resp = req.execute()
          projects.extend(resp.get("projects", []))
          req = crm_svc.projects().list_next(req, resp)
      return projects

  def get_policy(crm_svc, project_id):
      req = crm_svc.projects().getIamPolicy(
          resource=project_id,
          body={"options": {"requestedPolicyVersion": 3}},
      )
      return req.execute()

  if __name__ == "__main__":
      iam_svc, crm_svc = get_services()
      roles = list_custom_roles(iam_svc, ORG_ID)

      data_roles = {
          r["name"]: set(r.get("includedPermissions", [])) & DATA_READ_PERMS
          for r in roles
          if set(r.get("includedPermissions", [])) & DATA_READ_PERMS
      }

      print("Custom roles with wide data‑read permissions:")
      for name, perms in data_roles.items():
          print(f"{name}: {perms}")

      projects = list_projects(crm_svc, ORG_ID)
      print("\nBindings of these roles (project level):")
      for p in projects:
          pid = p["projectId"]
          policy = get_policy(crm_svc, pid)
          for b in policy.get("bindings", []):
              if b["role"] in data_roles:
                  print(f"Project {pid} -> {b['role']} -> {b.get('members', [])}")
  ```
  * Feed these results into: (1) a pre‑approved catalog of analytics / read‑only roles, (2) mandatory data‑owner approval workflow for any role including `*getData` / object read on regulated datasets, and (3) periodic review/attestation of bindings on sensitive projects/folders to remove generic “analytics” groups and enforce least‑privilege.
