Back to Checks
gcp

Service account user-managed key has been rotated within the last 90 days

iam_sa_user_managed_key_rotate_90_days
Service: iamSeverity: low
OfficialBuilt-inVerified
Categories:secrets
Resource Type:iam.googleapis.com/ServiceAccountKey

GCP IAM service account user-managed keys are evaluated by last rotation. Keys of type USER_MANAGED older than 90 days are identified; those rotated within 90 days align with the expected rotation cadence.

Risk

Stale service account keys extend exposure of long-lived credentials. A leaked or retained key can grant persistent API access, enabling data exfiltration, tampering, and lateral movement. Weak rotation reduces revocation effectiveness and erodes confidentiality and integrity.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler gcp --checks iam_sa_user_managed_key_rotate_90_days

Recommendation

Rotate user-managed keys at least every 90 days and prefer Workload Identity Federation or other short-lived credentials over static keys.

  • Minimize key count; remove unused keys
  • Enforce least privilege on service accounts
  • Automate rotation and alert on aged keys
  • Set key expiry as defense in depth

Remediation

CLI

gcloud iam service-accounts keys delete <KEY_ID> --iam-account=<SA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com

Other

1In Google Cloud Console, go to IAM & Admin > Service Accounts
2Open the service account, then go to the Keys tab
3If the workload still needs a key: click Add key > Create new key (JSON), download it, and update the workload to use it
4Delete the user-managed key(s) older than 90 days by clicking Delete next to each
5Re-run the check to confirm only recent keys remain

Check Code

from datetime import datetime

from prowler.lib.check.models import Check, Check_Report_GCP
from prowler.providers.gcp.services.iam.iam_client import iam_client


class iam_sa_user_managed_key_rotate_90_days(Check):
    def execute(self) -> Check_Report_GCP:
        findings = []
        for account in iam_client.service_accounts:
            for key in account.keys:
                if key.type == "USER_MANAGED":
                    last_rotated = (datetime.now() - key.valid_after).days
                    report = Check_Report_GCP(
                        metadata=self.metadata(),
                        resource=account,
                        resource_id=key.name,
                        resource_name=account.email,
                        location=iam_client.region,
                    )
                    report.status = "PASS"
                    report.status_extended = f"User-managed key {key.name} for account {account.email} was rotated over the last 90 days ({last_rotated} days ago)."
                    if last_rotated > 90:
                        report.status = "FAIL"
                        report.status_extended = f"User-managed key {key.name} for account {account.email} was not rotated over the last 90 days ({last_rotated} days ago)."
                    findings.append(report)

        return findings