Back to Checks
gcp

Service account was used within the configured maximum unused period

iam_service_account_unused
Service: iamSeverity: medium
OfficialBuilt-inVerified
Categories:identity-access
Resource Type:iam.googleapis.com/ServiceAccount

Google Cloud service accounts are evaluated for recent usage within a configurable window (default 180 days) using usage telemetry.

It highlights which accounts show activity versus those with no observed use in that period.

Risk

Dormant but permissioned service accounts threaten confidentiality and integrity via:

  • Impersonation/privilege escalation through stale roles or leaked keys
  • Lateral movement and persistent access They also weaken accountability, obscuring audit trails when reactivated unnoticed.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler gcp --checks iam_service_account_unused

Recommendation

Apply least privilege and reduce attack surface:

  • Verify inactivity, then disable and later delete unused accounts
  • Revoke role bindings and keys; favor short-lived impersonation over keys
  • Avoid powerful defaults; enforce separation of duties
  • Continuously monitor usage and alert on dormancy

Remediation

CLI

gcloud auth print-access-token --impersonate-service-account=<SERVICE_ACCOUNT_EMAIL>

Other

1In the Google Cloud console, open the IAM Service Account Credentials API reference for "GenerateAccessToken" and click "Try this method" (APIs Explorer)
2Set name to: projects/-/serviceAccounts/<SERVICE_ACCOUNT_EMAIL>
3Add scope: https://www.googleapis.com/auth/cloud-platform
4Click Execute (use an identity with roles/iam.serviceAccountTokenCreator on the service account)
5The generated token records recent usage for the service account, changing the finding to PASS

Check Code

from prowler.lib.check.models import Check, Check_Report_GCP
from prowler.providers.gcp.services.iam.iam_client import iam_client
from prowler.providers.gcp.services.monitoring.monitoring_client import (
    monitoring_client,
)


class iam_service_account_unused(Check):
    def execute(self) -> Check_Report_GCP:
        findings = []
        max_unused_days = monitoring_client.audit_config.get(
            "max_unused_account_days", 180
        )
        sa_ids_used = monitoring_client.sa_api_metrics
        for account in iam_client.service_accounts:
            report = Check_Report_GCP(
                metadata=self.metadata(),
                resource=account,
                resource_id=account.email,
                location=iam_client.region,
            )
            if account.disabled:
                report.status = "PASS"
                report.status_extended = (
                    f"Service Account {account.email} is disabled and cannot be used."
                )
            elif account.uniqueId in sa_ids_used:
                report.status = "PASS"
                report.status_extended = f"Service Account {account.email} was used over the last {max_unused_days} days."
            else:
                report.status = "FAIL"
                report.status_extended = f"Service Account {account.email} was not used over the last {max_unused_days} days."
            findings.append(report)

        return findings