Project has no IAM users assigned the Service Account User or Service Account Token Creator roles at project level
Google Cloud IAM policies are inspected for project-level grants of roles/iam.serviceAccountUser and roles/iam.serviceAccountTokenCreator to principals. The focus is on bindings that enable attaching or impersonating service accounts at the project scope rather than on individual service accounts.
Risk
Project-wide impersonation rights enable privilege escalation and lateral movement. Holders can act as any service account, access data across services, modify resources, and persist access. New service accounts inherit exposure, undermining confidentiality and integrity.
Run this check with Prowler
Run in Prowler Cloudprowler gcp --checks iam_no_service_roles_at_project_levelRecommendation
Assign roles/iam.serviceAccountUser and roles/iam.serviceAccountTokenCreator only on the specific service account, not at project scope. Enforce least privilege and separation of duties with per-SA grants, conditional bindings, and time-bound access. Prefer short-lived impersonation; review grants regularly.
Remediation
Terraform
# Grant required access at the service account level instead of the project level
resource "google_service_account_iam_member" "<example_resource_name>" {
service_account_id = "projects/<example_resource_id>/serviceAccounts/<example_resource_name>@<example_resource_id>.iam.gserviceaccount.com" # CRITICAL: scope grant to a specific service account, not the project
role = "roles/iam.serviceAccountUser" # CRITICAL: this role is granted only at the service account level
member = "user:<example_resource_name>@example.com"
}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_no_service_roles_at_project_level(Check):
def execute(self) -> Check_Report_GCP:
findings = []
failed_projects = set()
for binding in cloudresourcemanager_client.bindings:
report = Check_Report_GCP(
metadata=self.metadata(),
resource=binding,
resource_id=binding.role,
resource_name=binding.role if binding.role else "Service Role",
location=cloudresourcemanager_client.region,
)
if binding.role in [
"roles/iam.serviceAccountUser",
"roles/iam.serviceAccountTokenCreator",
]:
report.status = "FAIL"
report.status_extended = f"IAM Users assigned to service role '{binding.role}' at project level {binding.project_id}."
failed_projects.add(binding.project_id)
findings.append(report)
for project in cloudresourcemanager_client.project_ids:
if project not in failed_projects:
report = Check_Report_GCP(
metadata=self.metadata(),
resource=cloudresourcemanager_client.projects[project],
project_id=project,
location=cloudresourcemanager_client.region,
)
report.status = "PASS"
report.status_extended = f"No IAM Users assigned to service roles at project level {project}."
findings.append(report)
return findings