Google.IAM.Admin.V1.Enableserviceaccount Remediation
Event Information
-
Event Purpose & Semantics
google.iam.admin.v1.EnableServiceAccountindicates that a previously disabled service account has been re-enabled via the IAM API (e.g.,projects.serviceAccounts.enable).- After this event, the service account can again obtain tokens, sign JWTs, and be used by workloads (Compute Engine, GKE, Cloud Run, CI/CD, etc.).
-
Security & Compliance Implications (e.g., SOC 2, ISO 27001, PCI DSS)
- Re-enabling a service account can restore access to data and services; it is a privileged action that should be monitored and reviewed.
- You should verify: who initiated the event, from where (IP, network), and whether the re-enablement aligns with change tickets or access requests; log and retain this as part of access-control and change-management evidence.
-
Practical Monitoring & Controls
- Create Cloud Logging queries / Log-based metrics on
protoPayload.methodName="google.iam.admin.v1.EnableServiceAccount"and alert in Cloud Monitoring when triggered, especially for high-privilege or production service accounts. - Use IAM Conditions and least-privilege roles so only tightly controlled admin identities (e.g., a break-glass group or CI pipeline with change records) can call
iam.serviceAccounts.enable.
- Create Cloud Logging queries / Log-based metrics on
Examples
-
Re-enabling a previously disabled high-privilege service account
- Example: A “ci-prod-deployer@project.iam.gserviceaccount.com” with
roles/ownerwas disabled after an incident, but an attacker (or misconfigured pipeline) callsgoogle.iam.admin.v1.EnableServiceAccountto bring it back and reuse its OAuth2 tokens or keys. - Impact: Violates least-privilege principles (ISO 27001 A.9, CIS GCP 1.0 Controls) and can re-open a closed incident path if no additional monitoring is in place.
- Example: A “ci-prod-deployer@project.iam.gserviceaccount.com” with
-
Re-activating a service account used by exfiltration tooling
- Example: A service account with
roles/storage.adminwas disabled during a data-leak investigation; enabling it again allows access to sensitive buckets containing regulated data (e.g., PCI, HIPAA/PHI, GDPR personal data). - Impact: Potential non-compliance with data-protection requirements (PCI DSS 3/7, HIPAA 164.308/310, GDPR Art. 32), as a known risky identity regains read/write permissions to sensitive datasets.
- Example: A service account with
-
Bypassing change-control and SoD (Segregation of Duties)
- Example: A DevOps user with broad IAM permissions enables a service account tied to production billing or key management (
roles/cloudkms.admin,roles/billing.admin) outside of an approved change window. - Impact: Breaches change-management and SoD requirements (SOC 2 CC6/CC7, ISO 27001 A.12/A.15); can lead to unauthorized key operations, cost manipulation, or escalation of privileges via restored access to other GCP services.
- Example: A DevOps user with broad IAM permissions enables a service account tied to production billing or key management (
Remediation
Using Console
-
Immediately contain and verify (Console + Org Policy)
- In Cloud Console, go to IAM & Admin → Service Accounts, filter by name/email (e.g.,
ci-prod-deployer@...), and check Status, Last modified, and Keys; if the account must remain disabled, click the three‑dot menu → Disable (or Delete if formally decommissioned). - In IAM & Admin → Audit Logs, filter
google.iam.admin.v1.EnableServiceAccountand identify who/what re-enabled it; revoke or tighten that principal’s IAM (e.g., removeroles/iam.serviceAccountAdmin/roles/owner) under IAM. - Under IAM & Admin → Organization Policies, enforce org policies like
constraints/iam.disableServiceAccountKeyCreation,constraints/iam.allowedPolicyMemberDomains, and ensure high‑risk SAs are covered by a policy/process that prohibits ad‑hoc enabling without CAB/SoD approval.
- In Cloud Console, go to IAM & Admin → Service Accounts, filter by name/email (e.g.,
-
Lock down risky service accounts and access paths
- For incident‑related or high‑privilege SAs (exfil tooling,
roles/owner,roles/storage.admin,roles/cloudkms.admin,roles/billing.admin):- In Service Accounts → [SA] → Keys, delete all keys; in Details, remove unneeded roles and convert to least‑privilege or set to disabled with clear description/tags (e.g., “QUARANTINED – do not enable”).
- In IAM, remove broad roles from CI/CD service accounts and users; prefer custom roles limited to specific resources and actions required by pipelines.
- In Security → Access Approval / Access Control (where applicable) and your ticketing system, require a documented, approved change (linked ticket) to re-enable any SA associated with PCI/PHI/GDPR data or production financial/key‑management functions.
- For incident‑related or high‑privilege SAs (exfil tooling,
-
Implement preventive and detective controls (monitoring + workflow)
- In Cloud Monitoring → Logs Explorer, build a log‑based metric on
EnableServiceAccountand alerts to security/on‑call channels for:- Any enable of SAs with specific labels (e.g.,
env=prod,critical=true) or high‑risk roles; configure alerting policies under Monitoring → Alerting.
- Any enable of SAs with specific labels (e.g.,
- Use Security Command Center (SCC) and/or Cloud Logging sinks to export IAM audit logs to a SIEM; create detections for:
- Re‑enablement of disabled SAs, key creation, or assignment of powerful roles outside approved change windows.
- Document a standard change workflow (aligned with ISO 27001, SOC 2, PCI, HIPAA): re‑enablement only via a tracked ticket, dual approval (SoD), post‑change review, and periodic recertification of all high‑privilege service accounts via IAM access reviews.
- In Cloud Monitoring → Logs Explorer, build a log‑based metric on
Using CLI
-
Immediate containment & forensic validation (who/what re-enabled it)
- List and immediately disable the suspect service account; revoke keys and tokens:
-
gcloud iam service-accounts list \--project=PROJECT_IDgcloud iam service-accounts disable \SA_NAME@PROJECT_ID.iam.gserviceaccount.com \--project=PROJECT_IDgcloud iam service-accounts keys list \--iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.comgcloud iam service-accounts keys delete KEY_ID \--iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com
-
- Identify who/what re-enabled it using Cloud Audit Logs (map to change-control & SoD requirements):
-
gcloud logging read \'protoPayload.methodName="google.iam.admin.v1.EnableServiceAccount" \AND resource.type="service_account"' \--project=PROJECT_ID \--format="table(timestamp,protoPayload.authenticationInfo.principalEmail,protoPayload.resourceName)"
-
- For exfil-related accounts with storage or data roles, immediately remove high-risk bindings to protected resources:
-
gcloud projects get-iam-policy PROJECT_ID \--format=json > /tmp/policy.json# Edit policy.json to remove SA_NAME bindings, then:gcloud projects set-iam-policy PROJECT_ID /tmp/policy.json
-
- List and immediately disable the suspect service account; revoke keys and tokens:
-
Prevent re-enablement without formal change control & least privilege
- Lock down who can enable/disable service accounts via IAM Conditions, aligning to ISO 27001 / SOC 2 / CIS GCP:
-
gcloud projects add-iam-policy-binding PROJECT_ID \--member="group:cloud-security-admins@org.com" \--role="roles/iam.serviceAccountAdmin"gcloud projects remove-iam-policy-binding PROJECT_ID \--member="user:overprivileged-devops@org.com" \--role="roles/owner"
-
- For permanently retired or incident-related accounts, disable and deny use with an organization policy and labels:
-
gcloud iam service-accounts update \SA_NAME@PROJECT_ID.iam.gserviceaccount.com \--description="DISABLED DUE TO INCIDENT YYYY-MM-DD - DO NOT ENABLE" \--project=PROJECT_IDgcloud iam service-accounts disable \SA_NAME@PROJECT_ID.iam.gserviceaccount.com \--project=PROJECT_ID
- Optionally add an org policy to disallow
iam.serviceAccounts.enablefor non-security groups via custom org policy + Conditions (enforced at org/folder).
-
- Lock down who can enable/disable service accounts via IAM Conditions, aligning to ISO 27001 / SOC 2 / CIS GCP:
-
Monitoring, guardrails, and safer alternatives
- Create an alert on any
EnableServiceAccountfor high-privilege roles (PCI/ HIPAA/GDPR evidence):-
gcloud logging metrics create sa_enable_highpriv_metric \--description="SA enable for high-priv SAs" \--log-filter='protoPayload.methodName="google.iam.admin.v1.EnableServiceAccount"AND protoPayload.serviceData.policyDelta.bindingDeltas:(member:serviceAccount: AND(role:"roles/owner" OR role:"roles/storage.admin" OR role:"roles/cloudkms.admin" OR role:"roles/billing.admin"))'# Then create a Log-based alert in Cloud Monitoring on this metric.
-
- Replace broad roles with least-privilege custom roles and use short-lived Workload Identity Federation for CI/CD instead of long-lived keys:
-
gcloud iam roles create ci_deployer_minimal \--project=PROJECT_ID \--title="CI Deployer Minimal" \--permissions=compute.instances.update,run.services.update \--stage=GAgcloud projects add-iam-policy-binding PROJECT_ID \--member="serviceAccount:ci-prod-deployer@PROJECT_ID.iam.gserviceaccount.com" \--role="projects/PROJECT_ID/roles/ci_deployer_minimal"
-
- Create an alert on any
Using Python
-
Immediate technical containment & enforcement (org policy, deny, and guardrails)
- Create an org-level Deny Policy that blocks re-enabling high‑risk service accounts except through a controlled group (GCP IAM Deny, preview/GA dependent):
gcloud iam deny-policies create \--display-name="Deny enable of protected SAs" \--parent="organizations/ORG_ID" \--policy-file=deny-enable-protected-sas.yaml
deny-enable-protected-sas.yaml(example):displayName: "Deny enable for protected SAs"rules:- denyRule:deniedPermissions:- iam.serviceAccounts.enableexceptionPrincipals: # only break-glass group can enable- group:breakglass-iam-admins@ORG_DOMAINcondition:expression: "starts_with(resource.name, 'projects/PROJECT_ID/serviceAccounts/ci-prod-deployer@project.iam.gserviceaccount.com')" - Where org‑level deny isn’t available, move critical SAs into a locked “admin only” project/folder and strip
roles/iam.serviceAccountAdmin/roles/ownerfrom CI users. Use pre‑defined custom roles with noiam.serviceAccounts.enableoriam.serviceAccounts.undeletepermissions. - For exfiltration‑related SAs, also add: VPC‑SC around sensitive projects, CMEK protections, and bucket‑level conditional IAM (e.g.,
request.time < decommission_time) to ensure PCI/HIPAA/GDPR stores cannot be reached even if the SA is re‑enabled.
- Create an org-level Deny Policy that blocks re-enabling high‑risk service accounts except through a controlled group (GCP IAM Deny, preview/GA dependent):
-
Detection, monitoring, and change-control guardrails (Cloud Audit Logs + SCC + pipeline checks)
- Enable Data Access logs for
iam.googleapis.comand build log‑based alerts forgoogle.iam.admin.v1.EnableServiceAccounton tagged “protected” SAs (e.g., labelrisk_level=high), integrating with SIEM and ticketing to satisfy SOC 2 / ISO 27001 change‑management evidence. - Example Python script (Cloud Functions / Cloud Run) to auto‑re-disable protected SAs when an enable event is detected (triggered from Pub/Sub -> Log Sink):
import base64import jsonfrom googleapiclient.discovery import buildfrom google.oauth2 import service_account# ConfigurePROTECTED_SAS = {"ci-prod-deployer@project.iam.gserviceaccount.com","exfil-sa@project.iam.gserviceaccount.com",}SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]# If running as Cloud Function with appropriate IAM, you can use default credentials insteadCREDS = service_account.Credentials.from_service_account_file("svc-iam-automation.json", scopes=SCOPES)def _extract_principal_and_sa(proto_payload):method = proto_payload.get("methodName", "")sa_email = Noneresource_name = proto_payload.get("resourceName", "")# resourceName like: projects/PROJECT_ID/serviceAccounts/EMAILif "serviceAccounts/" in resource_name:sa_email = resource_name.split("serviceAccounts/")[-1]principal = proto_payload.get("authenticationInfo", {}).get("principalEmail")return method, sa_email, principaldef remediate_enable_sa(event, context):"""Cloud Function entrypoint for log-based trigger."""data = base64.b64decode(event["data"]).decode("utf-8")log_entry = json.loads(data)proto = log_entry.get("protoPayload", {})method, sa_email, principal = _extract_principal_and_sa(proto)if method != "google.iam.admin.v1.EnableServiceAccount":returnif sa_email not in PROTECTED_SAS:return# Re-disable the service accountproject = sa_email.split("@")[1].split(".")[0] # crude extraction; prefer labels/configname = f"projects/{project}/serviceAccounts/{sa_email}"iam = build("iam", "v1", credentials=CREDS, cache_discovery=False)iam.projects().serviceAccounts().disable(name=name, body={}).execute()# Optionally: write a security log / notify SIEM with principal and SA infoprint(json.dumps({"action": "auto_disable_service_account","service_account": sa_email,"triggered_by": principal,"log_insert_id": log_entry.get("insertId")}))
- Add a pipeline‑side control: Python pre‑deployment check to fail CI/CD jobs if they attempt to enable blocked SAs (meets SoD / change‑control). Example snippet in a job step:
import sysimport jsonPROTECTED_SAS = {"ci-prod-deployer@project.iam.gserviceaccount.com","exfil-sa@project.iam.gserviceaccount.com",}def main(plan_file_path: str):with open(plan_file_path) as f:plan = json.load(f)violations = []for change in plan.get("resource_changes", []):if change.get("type") == "google_service_account":after = change.get("change", {}).get("after", {})email = after.get("email")disabled = after.get("disabled")if email in PROTECTED_SAS and disabled is False:violations.append(email)if violations:print("ERROR: Attempt to enable protected service accounts:", violations)sys.exit(1)if __name__ == "__main__":main(sys.argv[1])
- Enable Data Access logs for
-
Lifecycle cleanup, least-privilege redesign, and compliance alignment
- For formerly compromised / exfiltration SAs: permanently delete instead of disable where possible, rotate all keys/tokens, and re‑implement workloads using narrowly scoped SAs (workload‑identity with per‑bucket / per‑KMS‑key roles). Document in incident records for PCI DSS / HIPAA / GDPR and ensure DLP / access reviews reflect the removal.
- Implement periodic Python-driven review of high‑privilege SAs (mapping to ISO 27001 A.9 & CIS GCP 1.0):
from googleapiclient.discovery import buildfrom google.oauth2 import service_accountSCOPES = ["https://www.googleapis.com/auth/cloud-platform"]CREDS = service_account.Credentials.from_service_account_file("svc-iam-audit.json", scopes=SCOPES)def list_high_priv_sas(project_id: str):iam = build("iam", "v1", credentials=CREDS, cache_discovery=False)res = iam.projects().serviceAccounts().list(name=f"projects/{project_id}").execute()high_risk_roles = {"roles/owner","roles/storage.admin","roles/cloudkms.admin","roles/billing.admin",}high_risk = []for sa in res.get("accounts", []):email = sa["email"]# In practice, also query project IAM bindings to see roles for this SA.# Placeholder: tag all for manual reviewhigh_risk.append(email)return high_risk
- Embed approvals (CAB / dual control) before any change that enables or modifies high‑privilege SAs: enforce via separate “requester” vs “approver” groups (SoD), and store approvals in a system of record linked to the underlying API calls/logs.