Project enforces separation of duties for Service Account Admin and Service Account User roles
Google Cloud IAM policies are evaluated to find principals granted both roles/iam.serviceAccountAdmin and roles/iam.serviceAccountUser within a project. Service-account related roles are expected to be segregated so that service account lifecycle management is distinct from their use or impersonation.
Risk
With both roles, a principal can create or modify service accounts and then use or attach them to workloads, enabling unchecked impersonation. This endangers confidentiality (expanded data access), integrity (policy/workload changes), and availability (persistence or sabotage via privileged automation).
Run this check with Prowler
Run in Prowler Cloudprowler gcp --checks iam_role_sa_enforce_separation_of_dutiesRecommendation
Enforce separation of duties: assign roles/iam.serviceAccountAdmin for lifecycle tasks and roles/iam.serviceAccountUser for attach/impersonate, never both to one principal.
- Apply least privilege with narrow scope and conditions
- Use temporary elevation/approvals
- Regularly audit IAM bindings and logs
Remediation
Terraform
# Remove all project-level principals from Service Account User
resource "google_project_iam_binding" "sa_user_none" {
project = "<example_resource_id>"
role = "roles/iam.serviceAccountUser" # critical: target role to clear at project level
members = [] # critical: empty list removes the binding (no members)
}
# Remove all project-level principals from Service Account Admin
resource "google_project_iam_binding" "sa_admin_none" {
project = "<example_resource_id>"
role = "roles/iam.serviceAccountAdmin" # critical: target role to clear at project level
members = [] # critical: empty list removes the binding (no members)
}Other
Check Code
from prowler.lib.check.models import Check, Check_Report_GCP
from prowler.providers.gcp.services.cloudresourcemanager.cloudresourcemanager_client import (
cloudresourcemanager_client,
)
class iam_role_sa_enforce_separation_of_duties(Check):
def execute(self) -> Check_Report_GCP:
findings = []
for project in cloudresourcemanager_client.project_ids:
non_compliant_members = []
report = Check_Report_GCP(
metadata=self.metadata(),
resource=cloudresourcemanager_client.projects[project],
location=cloudresourcemanager_client.region,
project_id=project,
)
report.status = "PASS"
report.status_extended = f"Principle of separation of duties was enforced for Service-Account Related Roles in project {project}."
for binding in cloudresourcemanager_client.bindings:
if binding.project_id == project and (
"roles/iam.serviceAccountUser" in binding.role
or "roles/iam.serviceAccountAdmin" in binding.role
):
non_compliant_members.extend(binding.members)
if non_compliant_members:
report.status = "FAIL"
report.status_extended = f"Principle of separation of duties was not enforced for Service-Account Related Roles in project {project} in members {','.join(non_compliant_members)}."
findings.append(report)
return findings