Join our Newsletter — 33% off our NHI Course
Home› Guides› How to inventory Google Cloud service account keys…
Guide Non-Human Identity (NHI)

How to inventory Google Cloud service account keys and replace them with federation: From key files to keyless access

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 28 October 2026 10 min read
On this page

A Google Cloud service account key is a private key file that never expires by default and works from anywhere, so every key in a laptop, CI variable or repository is a standing credential for that service account. Google provides the pieces to remove them: key listings, Activity Analyzer for last authentication, workload identity federation for external workloads, and organisation policy constraints that stop new keys being created. This runbook takes you from a list of keys to keyless access: inventory, find which keys are used and by whom, move each consumer to federation, disable, then delete, and lock the door with organisation policy. It is for cloud platform and security engineers responsible for Google Cloud projects or organisations. For the background on key risk, see the Google Cloud Service Account Keys Security Guide.

Key takeaways

  • Only user-managed keys are your problem. Listings can include Google-created keys used by the Service Account Credentials API; filter on key type USER_MANAGED.
  • Activity Analyzer tells you which keys are used. gcloud policy-intelligence query-activity --activity-type=serviceAccountKeyLastAuthentication returns lastAuthenticatedTime per key, omitted for keys never used.
  • Federation replaces the key for external workloads. A workload identity pool and provider let GitHub Actions, GitLab, Azure DevOps, HCP Terraform and other clouds exchange their own tokens for Google credentials.
  • Disable, then delete. gcloud iam service-accounts keys disable is reversible with enable; it does not revoke short-lived credentials already minted from the key.
  • Enforce with organisation policy. iam.disableServiceAccountKeyCreation and iam.disableServiceAccountKeyUpload are enforced by default only for organisations created on or after 3 May 2024.

When you need this

  • Older organisations: organisations created before 3 May 2024 do not have key creation blocked by default and often hold years of user-managed keys.
  • CI/CD migration: pipelines that authenticate with a JSON key stored as a CI secret are the most common and easiest keys to remove with federation.
  • Exposure or offboarding: a key found in a repository, a departing engineer who created keys, or a supplier that held one. If a key is known to be leaked, disable it at once and follow the Cloud Access Key Leak Playbook; this runbook is for planned replacement.
  • Audit requests: evidence that long-lived service credentials are inventoried and minimised (NIST SP 800-53 IA-5, CM-8).

Before you start

TaskRole (least privilege named by Google)Notes
List keysroles/iam.serviceAccountViewerIAM API must be enabled
Query last authenticationroles/policyanalyzer.activityAnalysisViewerPolicy Analyzer API must be enabled (needs serviceusage.services.enable)
Disable, enable, delete keysroles/iam.serviceAccountKeyAdminOn the project or the service account
Create pools and providersWorkload Identity Pool Admin or equivalentUse the project number, not the project ID, in principal identifiers
Set organisation policyOrganization Policy AdministratorAt organisation, folder or project level

Google advises against basic roles (Owner, Editor, Viewer) in production; run discovery with a dedicated viewer identity. Decide the target for each consumer type in advance using the table in step 3.

Step-by-step

Steps 1 and 2 are read-only. Steps 3 and 4 add federation alongside the key. Step 5 is reversible. Step 6 is destructive. Step 7 changes policy for the whole scope.

  1. Inventory user-managed keys. For each service account, list keys with their creation, expiry and disabled state:
    gcloud iam service-accounts list --project=PROJECT_ID --format="value(email)"
    gcloud iam service-accounts keys list \
      --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com
    The output shows KEY_ID, CREATED_AT, EXPIRES_AT, DISABLED, DISABLE_REASON and EXTENDED_STATUS. A disable reason of EXPOSED means Google itself disabled a key it detected as exposed. The REST API's keyTypes parameter filters on USER_MANAGED or SYSTEM_MANAGED; ignore system-managed keys. Loop over every project in scope and keep the result as a dated CSV.
  2. Find which keys are used. Query Activity Analyzer for every key in the project, or up to 10 specific keys joined with OR:
    gcloud policy-intelligence query-activity \
      --activity-type=serviceAccountKeyLastAuthentication \
      --project=PROJECT_ID --limit=1000
    In the console the same query is under IAM & Admin > Policy Analyzer > Analyze recent activity. Keys with no lastAuthenticatedTime have never been used within the observationPeriod; results may miss very recent events and timestamps are daily. Disabled keys are excluded from results. Use Cloud Audit Logs on the services the account calls to find the source of each used key (caller IP, user agent, the system that holds the file), then confirm with the owner.
  3. Choose the replacement for each consumer.
    ConsumerReplacementNote
    Workload on Google Cloud (Compute Engine, Cloud Run, GKE)Attached service account; Workload Identity Federation for GKENo key or federation pool needed
    GitHub Actions, GitLab SaaS, HCP Terraform, Azure DevOpsWorkload identity federation with an OIDC providerAttribute condition limiting your organisation or group is mandatory
    AWS or Azure workloadsWorkload identity federation with other cloudsUses the other cloud's native identity
    On-premises or third-party SaaS with no federation supportKeep a key, with short expiry and an ownerTrack as a documented exception
  4. Set up federation alongside the key. For a deployment pipeline, create a pool and an OIDC provider, then grant access:
    gcloud iam workload-identity-pools create POOL_ID --location="global" \
      --display-name="CI pipelines"
    gcloud iam workload-identity-pools providers create-oidc PROVIDER_ID \
      --location="global" --workload-identity-pool="POOL_ID" \
      --issuer-uri="https://token.actions.githubusercontent.com/" \
      --attribute-mapping="google.subject=assertion.sub,attribute.repository_owner_id=assertion.repository_owner_id,attribute.repository_id=assertion.repository_id" \
      --attribute-condition="assertion.repository_owner_id=='OWNER_ID'"
    GitHub, GitLab SaaS and HCP Terraform share one issuer across all customers, so without the attribute condition any of their customers could present a valid token. Google recommends the numeric repository_id and repository_owner_id claims over names, which can be re-registered after deletion. Then grant access either directly to the resource with a principal:// or principalSet:// member, or by letting the federated identity impersonate the existing service account:
    gcloud iam service-accounts add-iam-policy-binding SA_EMAIL \
      --role=roles/iam.workloadIdentityUser \
      --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/attribute.repository_id/REPO_ID"
    Impersonation is the lowest-risk first move because the service account's existing roles stay unchanged. In GitHub Actions, use google-github-actions/auth with workload_identity_provider and permissions: id-token: write, then remove the JSON key secret from the pipeline. See the GitHub Actions OIDC Federation Hardening Guide.
  5. Disable the old key (reversible). Once the consumer runs on federation:
    gcloud iam service-accounts keys disable KEY_ID \
      --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com --project=PROJECT_ID
    Google notes that disabling does not revoke short-lived credentials already issued from the key; revoking those requires disabling or deleting the service account, which affects every workload using it. Leave the key disabled through a full business cycle.
  6. Delete the key (destructive). Google recommends disabling before deleting.
    gcloud iam service-accounts keys delete KEY_ID \
      --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com
    Treat deletion as permanent and keep the inventory record of the key ID, owner and consumer.
  7. Block new keys with organisation policy.
    gcloud resource-manager org-policies enable-enforce \
      --organization ORGANIZATION_ID iam.disableServiceAccountKeyCreation
    gcloud resource-manager org-policies enable-enforce \
      --organization ORGANIZATION_ID iam.disableServiceAccountKeyUpload
    Managed equivalents exist (iam.managed.disableServiceAccountKeyCreation, which also blocks Cloud Storage HMAC keys, and iam.managed.disableServiceAccountKeyUpload). For documented exceptions, set constraints/iam.serviceAccountKeyExpiryHours (allowed values from 1h to 2160h) on the excepted project so new keys expire, and override the creation constraint only at that project.

Verify it worked

  • Consumer on federation: Cloud Audit Logs for the pipeline's calls show the federated principal or the impersonated service account, with no key ID in the authentication info.
  • Key quiet: re-run query-activity for the key before disabling; after disabling, the consumer keeps working.
  • Inventory shrinks: the keys list for the service account shows the key disabled, then gone.
  • No orphaned secrets: the JSON key secret is removed from the CI system, secret store and any developer machines named by the owner, so a later re-enable cannot be quietly reused. Ask owners to confirm deletion of local copies in the change ticket.
  • Policy holds: an attempt to create a key in a covered project fails with an organisation policy error.

Rollback and common errors

SituationFix
A consumer fails after key disablegcloud iam service-accounts keys enable KEY_ID ..., find the consumer, move it, disable again
Federated token rejectedCheck the attribute condition, the mapping for every attribute used in the condition, and the audience; see the Multi-Cloud Workload Identity Troubleshooting Guide
PERMISSION_DENIED on key deleteThe service account does not exist or the caller lacks Service Account Key Admin
Principal identifier rejectedUse the project number, not the project ID, in principal:// and principalSet://
Pool or provider ID refusedThe gcp- prefix is reserved
Team needs a key after enforcementGrant a project-level exception with key expiry set, and record an owner and end date
Provider too broadA condition on repository owner alone admits every repository in the organisation; narrow to repository ID or branch claims

Hardening afterwards

  • One provider per trust boundary: separate pools or providers for production and non-production pipelines, so a test repository cannot reach production.
  • Least privilege on the service account: federation does not reduce what the service account can do. Review its roles; see the Google Cloud IAM and Service Accounts Guide.
  • Watch for new keys: alert on key creation events in Cloud Audit Logs for any project with an exception.
  • Remove dormant service accounts: query serviceAccountLastAuthentication for accounts with no authentication and disable them.
  • Keep the inventory current: feed the key and exception list into your NHI inventory. See NHI Inventory Reconciliation.

Metrics

  • User-managed keys in the organisation (target: falling to the documented exception list).
  • Keys never used or unused for 90 days (target: zero).
  • Pipelines using federation instead of a JSON key (target: all).
  • Projects with key creation allowed (target: only named exceptions, each with key expiry set).

Small estate or large organisation

A handful of projects can be cleaned in a sprint using the commands above. A large organisation should enforce the creation constraint first at a folder holding new projects, migrate CI/CD systems centrally (one pool per platform with per-repository conditions), and work the remaining keys by owner. Enforcing at the organisation root before the migration will block teams that legitimately rotate keys today, so roll out folder by folder with exceptions agreed.

Framework mapping

FrameworkReferenceRelevance
NIST SP 800-53 Rev 5IA-5 Authenticator Management; CM-8 System Component InventoryInventory and minimise long-lived authenticators
NIST CSF 2.0PR.AA-01Service credentials managed by the organisation
MITRE ATT&CKT1552.001 Credentials In Files; T1078.004 Cloud AccountsKey files are stolen from disks and repositories and used as cloud accounts

Practitioner checklist

  • List user-managed keys in every project and record owner and consumer.
  • Query Activity Analyzer for key last authentication.
  • Pick a replacement per consumer: attached service account, federation or documented exception.
  • Create providers with an attribute condition on numeric owner or repository IDs.
  • Run the consumer on federation before touching the key.
  • Disable, wait a business cycle, then delete.
  • Enforce the key creation and upload constraints, with expiry on exceptions.

Standards and references

Related NHI Mgmt Group resources: Google Cloud Service Account Keys Security Guide · Workload Identity Federation Guide · GitHub Actions OIDC Federation Hardening Guide · Google Cloud IAM and Service Accounts Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 28 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org