google.iam.admin.v1.CreateServiceAccount
Event Information
-
Event purpose & scope
google.iam.admin.v1.CreateServiceAccountindicates that a new IAM service account was created in a GCP project (via Console, gcloud, API, or Terraform) using the IAM Admin API.- The payload typically includes the service account name, email, display name, and the actor (user/service) that initiated the creation.
-
Security & compliance relevance (e.g., ISO 27001, SOC 2, PCI-DSS)
- Creation of service accounts introduces new identities that can hold roles and keys; this is a sensitive event for access control and least-privilege enforcement.
- It should be monitored and logged in Cloud Audit Logs and forwarded to SIEM for anomaly detection, owner validation, and periodic access reviews.
-
Practical controls & monitoring
- Set up log-based alerts (Cloud Logging) on
protoPayload.methodName="google.iam.admin.v1.CreateServiceAccount"to notify security/operations when new service accounts are created. - Enforce policies via Organization Policy / IAM conditions and deployment pipelines (e.g., Terraform with code review) to restrict who can create service accounts and ensure naming conventions, owner tagging, and rotation/usage of keys align with compliance requirements.
- Set up log-based alerts (Cloud Logging) on
Examples
-
Over-privileged / Mis-scoped service account for workloads
- A new service account is created and then granted broad roles like
roles/owner,roles/editor, or project-levelroles/iam.serviceAccountTokenCreator. This allows lateral movement, privilege escalation, and token abuse if the workload or service account key is compromised. - Compliance: Violates least-privilege requirements in ISO 27001, SOC 2, PCI DSS (e.g., PCI DSS 7 – access needs to be restricted to business need-to-know).
- A new service account is created and then granted broad roles like
-
Shadow / Backdoor service accounts created outside standard process
- An attacker or insider with IAM admin rights creates a hidden service account (e.g., not registered in CMDB/ITSM) and binds it to critical resources, then uses it for persistence or data exfiltration.
- Compliance: Breaks access governance and account lifecycle controls (e.g., ISO 27001 A.9, SOC 2 CC6, NIST 800-53 AC family).
-
Service accounts with user-like capabilities
- Service account is created with roles that allow impersonating users or other service accounts (
roles/iam.serviceAccountUser,roles/iam.serviceAccountTokenCreatoracross the project), enabling bypass of SSO/MFA, audit trails tied to the wrong principal, and impersonation of privileged identities. - Compliance: Undermines identity assurance and non-repudiation (e.g., PCI DSS 10, SOX logging/traceability, NIST 800-53 AU/IA families).
- Service account is created with roles that allow impersonating users or other service accounts (
Remediation
Using Console
-
Remove broad / mis-scoped roles and re-scope to least privilege
- In GCP Console: Go to IAM & Admin → IAM → locate the over-privileged service account → click Edit principal (pencil icon) → remove broad roles (
Owner,Editor, project-wideService Account Token Creator, etc.). - Create or use minimal custom roles: IAM & Admin → Roles → Create Role → add only the specific permissions needed for the workload → return to IAM and assign this custom role only at the narrowest scope (folder/project/specific resource) required.
- For compliance (ISO 27001, SOC 2, PCI DSS 7): document the new role design, keep an approval record (e.g., in your ITSM), and enforce change control for any future privilege increases.
- In GCP Console: Go to IAM & Admin → IAM → locate the over-privileged service account → click Edit principal (pencil icon) → remove broad roles (
-
Detect and remove shadow/backdoor service accounts; enforce lifecycle controls
- Discover rogue SAs: Go to IAM & Admin → Service Accounts → filter by project and compare against your CMDB/ITSM; look for SAs with no owner record, unusual names, or created by unexpected users (check Service account details → Audit logs).
- For any unapproved SA: in Service Accounts, click the SA → Delete (or first revoke its bindings via IAM & Admin → IAM by removing all roles that reference that SA). Also remove any keys: Service account → Keys → Delete for each key.
- Implement governance: restrict who can create SAs and bind roles by going to IAM and removing
roles/iam.serviceAccountAdmin,roles/iam.admin, androles/resourcemanager.projectIamAdminfrom general users; keep these only in tightly controlled admin groups, and review SA inventory periodically against compliance requirements (ISO 27001 A.9, SOC 2 CC6, NIST AC).
-
Remove user-like capabilities and impersonation; constrain service account usage
- In IAM & Admin → IAM, search for principals with
roles/iam.serviceAccountUserorroles/iam.serviceAccountTokenCreatorat project level; click Edit principal and remove project-wide bindings. Reassign these roles only on specific service accounts where absolutely necessary: open the SA under Service Accounts → Show Info Panel → Permissions → Grant Access, and grantService Account Useronly to the minimal group/workload. - Disable broad impersonation: avoid granting SAs roles that allow impersonating users (e.g., custom roles with
impersonatepermissions orroles/iam.serviceAccountTokenCreatoron many SAs). Where required, scope to a single SA and enforce usage through workload identity (e.g., GCE/GKE default SA) rather than keys. - For compliance (PCI DSS 10, SOX, NIST AU/IA): ensure auditability by confirming in IAM & Admin → Audit Logs that actions are performed by distinct, named principals; document which SAs are allowed to impersonate whom, require approvals, and periodically review these bindings.
- In IAM & Admin → IAM, search for principals with
Using CLI
-
Constrain over-privileged / mis-scoped service accounts
- Identify and review risky bindings (e.g.,
roles/owner,roles/editor, wideserviceAccountTokenCreator) and export for approval:gcloud projects get-iam-policy PROJECT_ID --format=json > iam-policy.jsongcloud iam service-accounts list --project=PROJECT_IDgcloud projects get-iam-policy PROJECT_ID \--flatten="bindings[].members" \--filter="bindings.members:serviceAccount:" \--format="table(bindings.role, bindings.members)" - Remove broad roles and replace with least-privilege, role-scoped bindings (prefer custom roles and per-service-project scope):
# Remove project-level owner/editor from SAgcloud projects remove-iam-policy-binding PROJECT_ID \--member="serviceAccount:SA_NAME@PROJECT_ID.iam.gserviceaccount.com" \--role="roles/editor"# Grant minimal custom role at narrow resource scopegcloud projects add-iam-policy-binding PROJECT_ID \--member="serviceAccount:SA_NAME@PROJECT_ID.iam.gserviceaccount.com" \--role="projects/PROJECT_ID/roles/CUSTOM_MIN_ROLE"
- Enforce prevention controls aligned with ISO 27001 / PCI DSS 7 (policy-as-code, org policy, CI checks):
# Example org policy to disable basic roles at project levelgcloud org-policies set-policy policy.yaml --organization=ORG_ID# policy.yaml: constraint=iam.disableServiceAccountKeyCreation / iam.allowedPolicyMemberDomains etc.
- Identify and review risky bindings (e.g.,
-
Detect and remove shadow / backdoor service accounts
- Discover unregistered / shadow SAs and bindings by comparing GCP to CMDB lists:
gcloud iam service-accounts list --project=PROJECT_ID \--format="table(email,disabled,oauth2ClientId)"gcloud projects get-iam-policy PROJECT_ID \--flatten="bindings[].members" \--filter="bindings.members:serviceAccount:" \--format="table(bindings.role, bindings.members)"
- Disable or delete backdoor SAs (after impact review) and strip their bindings to meet ISO 27001 A.9 / NIST AC lifecycle controls:
# Remove all bindings for backdoor SAgcloud projects get-iam-policy PROJECT_ID --format=json > policy.json# edit policy.json to remove that SA, then:gcloud projects set-iam-policy PROJECT_ID policy.json# Optionally disable before deletegcloud iam service-accounts disable BACKDOOR_SA@PROJECT_ID.iam.gserviceaccount.comgcloud iam service-accounts delete BACKDOOR_SA@PROJECT_ID.iam.gserviceaccount.com
- Hardening and governance (prevent future shadow SAs): require Terraform/Deployment Manager + approval for SA creation and monitor with log-based alerts:
gcloud logging sinks create sa-creation-sink STORAGE_BUCKET \--log-filter='protoPayload.methodName="google.iam.admin.v1.CreateServiceAccount"'
- Discover unregistered / shadow SAs and bindings by comparing GCP to CMDB lists:
-
Restrict service accounts with user-like capabilities (impersonation / token creation)
- Enumerate who can impersonate or mint tokens (violates non-repudiation in PCI DSS 10 / SOX if overly broad):
# List Service Account User / TokenCreator bindingsgcloud projects get-iam-policy PROJECT_ID \--flatten="bindings[].members" \--filter='bindings.role:roles/iam.serviceAccountUser OR bindings.role:roles/iam.serviceAccountTokenCreator' \--format="table(bindings.role, bindings.members)"
- Remove project-wide impersonation and re-scope to specific identities and SAs only where needed:
gcloud projects remove-iam-policy-binding PROJECT_ID \--member="user:USER@EXAMPLE.COM" \--role="roles/iam.serviceAccountUser"# Grant SA User only on specific SAgcloud iam service-accounts add-iam-policy-binding SA_NAME@PROJECT_ID.iam.gserviceaccount.com \--member="user:USER@EXAMPLE.COM" \--role="roles/iam.serviceAccountUser"
- Enforce controls that keep SA usage non-interactive and traceable: disallow SA keys, require Workload Identity Federation, and alert on high-risk methods:
# Org policy to disable service account key creationgcloud org-policies set-policy disable-sa-keys.yaml --organization=ORG_ID# disable-sa-keys.yaml uses constraint: iam.disableServiceAccountKeyCreation# Alert on token creation / signBlob/signJwtgcloud logging sinks create sa-token-abuse-sink PUBSUB_TOPIC \--log-filter='protoPayload.methodName=("google.iam.credentials.v1.IAMCredentials.GenerateAccessToken" OR "google.iam.credentials.v1.IAMCredentials.SignJwt" OR "google.iam.credentials.v1.IAMCredentials.SignBlob")'
- Enumerate who can impersonate or mint tokens (violates non-repudiation in PCI DSS 10 / SOX if overly broad):
Using Python
-
Detect and right-size over-privileged / mis-scoped service accounts
- Enumerate service accounts and flag broad roles (
roles/owner,roles/editor, project-levelroles/iam.serviceAccount*); export for review and replace with least-privilege custom or predefined roles.from google.cloud import resourcemanager_v3, iam_v1project_id = "YOUR_PROJECT_ID"broad_roles = {"roles/owner","roles/editor","roles/iam.serviceAccountTokenCreator","roles/iam.serviceAccountUser",}def get_project_number(project_id):client = resourcemanager_v3.ProjectsClient()proj = client.get_project(name=f"projects/{project_id}")return proj.name.split("/")[-1]def list_bindings_with_broad_roles(project_id):project_number = get_project_number(project_id)resource = f"projects/{project_number}"client = iam_v1.IAMPolicyClient()policy = client.get_iam_policy(resource=resource)for b in policy.bindings:if b.role in broad_roles:print(f"ROLE: {b.role}")for m in b.members:if m.startswith("serviceAccount:"):print(f" SA: {m}")if __name__ == "__main__":list_bindings_with_broad_roles(project_id) - To remediate, adjust the binding: remove broad role from SA and add granular roles on specific resources (e.g., specific GCS buckets, Pub/Sub topics).
from google.cloud import iam_v1project_id = "YOUR_PROJECT_ID"sa_email = "sa-name@YOUR_PROJECT_ID.iam.gserviceaccount.com"role_to_remove = "roles/editor"roles_to_add = ["roles/storage.objectViewer"] # exampledef update_sa_bindings(project_id, sa_email, role_to_remove, roles_to_add):resource = f"projects/{project_id}"client = iam_v1.IAMPolicyClient()policy = client.get_iam_policy(resource=resource)member = f"serviceAccount:{sa_email}"new_bindings = []for b in policy.bindings:if b.role == role_to_remove:b.members[:] = [m for m in b.members if m != member]if not b.members:continuenew_bindings.append(b)policy.bindings[:] = new_bindingsfor r in roles_to_add:found = Falsefor b in policy.bindings:if b.role == r:if member not in b.members:b.members.append(member)found = Truebreakif not found:policy.bindings.append(iam_v1.Binding(role=r, members=[member]))client.set_iam_policy(resource=resource, policy=policy)if __name__ == "__main__":update_sa_bindings(project_id, sa_email, role_to_remove, roles_to_add)
- Governance: enforce least-privilege via CI/CD checks and Org Policies (
constraints/iam.allowedPolicyMemberDomains,constraints/iam.disableServiceAccountKeyCreation), and maintain approvals/records for ISO 27001/SOC 2/PCI DSS evidence.
- Enumerate service accounts and flag broad roles (
-
Detect and remove shadow/backdoor service accounts
- List all service accounts and compare against an “approved” inventory (from CMDB/ITSM) to detect unregistered SAs; immediately review and, if unauthorized, disable and remove bindings.
from google.cloud import iam_credentials_v1from google.cloud import iam_v1project_id = "YOUR_PROJECT_ID"approved_sas = {"approved1@YOUR_PROJECT_ID.iam.gserviceaccount.com","approved2@YOUR_PROJECT_ID.iam.gserviceaccount.com",}def list_service_accounts(project_id):client = iam_v1.IAMClient()parent = f"projects/{project_id}"return list(client.list_service_accounts(name=parent))if __name__ == "__main__":sas = list_service_accounts(project_id)for sa in sas:if sa.email not in approved_sas:print(f"UNAPPROVED SA: {sa.email}")
- For any unapproved SA, revoke IAM bindings and disable/delete account, keeping logs for audit:
from google.cloud import iam_v1project_id = "YOUR_PROJECT_ID"sa_email = "suspicious@YOUR_PROJECT_ID.iam.gserviceaccount.com"def remove_sa_from_project_bindings(project_id, sa_email):resource = f"projects/{project_id}"client = iam_v1.IAMPolicyClient()policy = client.get_iam_policy(resource=resource)member = f"serviceAccount:{sa_email}"new_bindings = []for b in policy.bindings:b.members[:] = [m for m in b.members if m != member]if b.members:new_bindings.append(b)policy.bindings[:] = new_bindingsclient.set_iam_policy(resource=resource, policy=policy)def disable_and_delete_sa(project_id, sa_email):client = iam_v1.IAMClient()name = f"projects/{project_id}/serviceAccounts/{sa_email}"client.disable_service_account(name=name)# wait / validate, then:client.delete_service_account(name=name)if __name__ == "__main__":remove_sa_from_project_bindings(project_id, sa_email)disable_and_delete_sa(project_id, sa_email)
- Governance: restrict who can create SAs (
roles/iam.serviceAccountAdmin) at org/folder level, require change tickets for any SA, and periodically reconcile against CMDB to satisfy ISO 27001 A.9, SOC 2 CC6, NIST AC controls.
- List all service accounts and compare against an “approved” inventory (from CMDB/ITSM) to detect unregistered SAs; immediately review and, if unauthorized, disable and remove bindings.
-
Limit service accounts with user-like/impersonation capabilities
- Enumerate bindings for
roles/iam.serviceAccountUserandroles/iam.serviceAccountTokenCreatorand verify they are only granted to tightly scoped technical identities on specific SAs, never broad “project-wide” or to generic groups.from google.cloud import iam_v1project_id = "YOUR_PROJECT_ID"sensitive_roles = {"roles/iam.serviceAccountUser","roles/iam.serviceAccountTokenCreator",}def list_impersonation_bindings(project_id):resource = f"projects/{project_id}"client = iam_v1.IAMPolicyClient()policy = client.get_iam_policy(resource=resource)for b in policy.bindings:if b.role in sensitive_roles:print(f"SENSITIVE ROLE: {b.role}")for m in b.members:print(f" MEMBER: {m}")if __name__ == "__main__":list_impersonation_bindings(project_id) - Where over-broad, remove project-level impersonation and re-grant at the individual service account resource path (e.g.,
projects/{p}/serviceAccounts/{sa}) only to approved identities, ensuring user access still flows through SSO/MFA and that logs clearly attribute actions.from google.cloud import iam_v1project_id = "YOUR_PROJECT_ID"sa_email = "target-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com"member_to_allow = "user:admin@example.com"role = "roles/iam.serviceAccountUser"def set_sa_level_impersonation(project_id, sa_email, member):resource = f"projects/{project_id}/serviceAccounts/{sa_email}"client = iam_v1.IAMPolicyClient()policy = client.get_iam_policy(resource=resource)for b in policy.bindings:if b.role == role and member in b.members:return # already presentpolicy.bindings.append(iam_v1.Binding(role=role, members=[member]))client.set_iam_policy(resource=resource, policy=policy)if __name__ == "__main__":set_sa_level_impersonation(project_id, sa_email, member_to_allow) - Governance: enable Cloud Audit Logs for all admin/data access, monitor SA-impersonation events (e.g.,
GenerateAccessToken,SignJwt), and document mappings of who may impersonate which SA to satisfy PCI DSS 10, SOX, and NIST AU/IA non‑repudiation requirements.
- Enumerate bindings for