A service account holding Owner or Editor can create, change or delete most resources in its project, and anyone who can impersonate or attach that account inherits the same reach. These grants usually arrive quietly: a default service account that received Editor at project creation, a pipeline account given Owner to get a deployment working, a grant made at folder level that every child project inherits. This guide is one task done properly: find every service account with a basic role across a Google Cloud organisation, work out who can act as it, replace the grant with narrower roles and remove it without breaking workloads. It is for cloud security and platform engineers. For the wider context see the Top 10 GCP IAM Mistakes and the Google Cloud IAM and Service Accounts Guide.
This is an independent guide from NHI Mgmt Group, based on Google Cloud's public documentation as of November 2026. It is not sponsored or reviewed by Google. Product features change, so confirm settings against Google Cloud's current documentation.
Key takeaways
- Search the organisation, not one project.
gcloud asset search-all-iam-policiesat organisation scope returns grants made on the organisation, folders, projects and individual resources in one pass. - Look for six roles, not two. Google Cloud documents legacy basic roles (
roles/owner,roles/editor,roles/viewer) and newer basic roles (roles/admin,roles/writer,roles/reader). Include all of them in the search. - The account is only half the risk. Principals with
iam.serviceAccounts.actAsoriam.serviceAccounts.getAccessTokenon that account can use its access, so map them before you decide what to fix first. - Use role recommendations to choose the replacement. The IAM recommender compares granted permissions with the last 90 days of use, and recommendations for basic roles are available at no charge.
- Grant the new role first, remove the basic role second. Then stop the problem returning with the
iam.automaticIamGrantsForDefaultServiceAccountsconstraint and an alert on new basic role grants.
When you need this
- Audit preparation: evidence of least privilege for workload identities, typically mapped to NIST SP 800-53 AC-6 and AC-6(7) Review of User Privileges.
- Older projects: Google Cloud says that, depending on organisation policy, default service accounts may receive Editor on the project. The constraint that stops this is enforced by default only for organisations created after 3 May 2024, and Google advises replacing Editor with less permissive roles where a default service account already has it.
- After a credential leak: a leaked key for an Owner service account is a project compromise. Run this search during the response to size the blast radius. See the Google Cloud Project Compromise Playbook.
- Before moving to federation: replacing keys with workload identity federation is easier when the account's roles are already right sized.
Before you start
| Tool | What it answers | Access needed | Limit to know |
|---|---|---|---|
gcloud asset search-all-iam-policies | Which allow policies in a scope contain a role or principal type | Cloud Asset Inventory API enabled in the calling project; a role that allows policy search, for example Cloud Asset Viewer (roles/cloudasset.viewer); confirm against Google's Cloud Asset Inventory roles list | Returns the bindings where they are made; inheritance has to be reasoned about from the resource path |
gcloud asset analyze-iam-policy (Policy Analyzer) | Who effectively has a permission, including group expansion and impersonation analysis | roles/cloudasset.viewer; roles/iam.roleViewer for custom roles | Allow policies only, deny policies are not accounted for; more than 20 queries per organisation per day needs Security Command Center Premium or Enterprise at organisation level |
IAM recommender (google.iam.policy.Recommender) | Which permissions a principal actually used, and a suggested replacement role | Recommender viewer access on the project, folder or organisation | Up to 90 days of observation; basic role recommendations are free, advanced features need Security Command Center Premium or Enterprise |
| Cloud Audit Logs | When a basic role was granted and by whom (SetIamPolicy entries) | Logs Viewer on the log bucket | Admin Activity logs are always on; Data Access logs need enabling for usage evidence |
Run discovery from a dedicated read-only identity, and keep the change steps under a separate, approved identity. Export the current allow policy of every project you will touch (gcloud projects get-iam-policy PROJECT_ID --format=json > PROJECT_ID-policy.json); that file is your rollback. Agree an owner contact route for each project before you start, because the slow part of this task is confirming what a service account is used for.
Step-by-step
Steps 1 to 4 are read only. Steps 5 to 7 change access and are reversible from the exported policy.
- Find service accounts with basic roles across the organisation. Google Cloud's own example for Owner, adapted to a CSV export:
Repeat withgcloud asset search-all-iam-policies \ --scope=organizations/ORG_ID \ --query="policy:(roles/owner serviceAccount)" \ --flatten="policy.bindings[].members[]" \ --format="csv(resource, policy.bindings.role, policy.bindings.members)" \ | grep serviceAccount > sa-owner.csvroles/editor,roles/adminandroles/writer(and the reader roles if your standard treats them as excessive for workloads). The grep matters: a binding that contains both a user and a service account is returned whole, so filter on the member, not the binding. Also check bindings on the organisation and folders in the output: a basic role granted on a folder applies to every project beneath it. - Classify each account. The email tells you the type.
[email protected]is the Compute Engine default service account and[email protected]is the App Engine default. Accounts endingiam.gserviceaccount.comare user-managed. Google-managed service agents (their emails sit in Google-owned domains) need care: Google advises not revoking roles granted to service agents unless a role recommendation suggests it. Accounts from another project are cross-project grants and go straight to the top of the list. - Find who can act as each account. For each flagged account, ask Policy Analyzer which principals hold the impersonation permissions on it:
Google documents that Service Account User carries actAs, and that Service Account Token Creator and Workload Identity User carry getAccessToken; any other role that includes these permissions, including custom roles, counts too. Look in particular for a service account in one project that can impersonate a service account in another; record these chains, because a low-privileged account in a development project that can impersonate an Owner account in production is effectively an Owner. Policy Analyzer also hasgcloud asset analyze-iam-policy --organization=ORG_ID \ --full-resource-name="//iam.googleapis.com/projects/PROJECT_ID/serviceAccounts/SA_EMAIL" \ --permissions="iam.serviceAccounts.actAs,iam.serviceAccounts.getAccessToken"--analyze-service-account-impersonation, which Google calls a very expensive operation, so use it on targeted queries only. - Collect evidence of use. List recommendations for each project:
A recommendation to remove the role means no permission from it was used in the observation period. A recommendation to replace it names predefined roles or a project-level custom role that covers what was used. Also check for user-managed keys on the account (gcloud recommender recommendations list \ --location=global \ --recommender=google.iam.policy.Recommender \ --project=PROJECT_ID --format=jsongcloud iam service-accounts keys list --iam-account=SA_EMAIL --managed-by=user): an Owner account with a key outside Google Cloud is your first fix. - Grant the replacement roles first. Apply the recommended or agreed narrower roles at the narrowest scope that works, for example a single bucket or dataset rather than the project:
Where you apply a recommendation, mark it claimed first withgcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:SA_EMAIL" --role="roles/storage.objectViewer"gcloud recommender recommendations mark-claimedso it does not change while you work, thenmark-succeededafterwards. - Remove the basic role.
Use the folder or organisation equivalent where the grant was made higher up. For default service accounts that nothing uses, Google's guidance is to avoid them and create dedicated service accounts per application; disable rather than delete first, because a re-enabled account keeps its bindings while a deleted and recreated one is a new identity.gcloud projects remove-iam-policy-binding PROJECT_ID \ --member="serviceAccount:SA_EMAIL" --role="roles/editor" - Remove the excess impersonation grants. Take the principals found in step 3 that do not need to act as the account and remove Service Account User or Token Creator from them, ideally at the service account resource rather than the project.
Prioritise the work
| Finding | Priority (NHI Mgmt Group view) | Why |
|---|---|---|
| Owner or Editor service account with a user-managed key | 1 | The credential can be used from anywhere without any Google Cloud access |
| Basic role granted at organisation or folder level | 1 | Applies to every child project, including future ones |
| Account impersonable from another project or by many users | 2 | Lateral movement chain; effective privilege spreads |
| Default Compute Engine or App Engine account with Editor, attached to running resources | 2 | Any code on those resources holds Editor through the metadata server |
| Recommender says the role is entirely unused | 3 | Lowest effort: remove after owner sign off |
| Viewer or Reader on a workload account | 4 | Broad read access; replace during normal hygiene |
Verify it worked
- Search again: rerun step 1. The account should no longer appear. Asset search data is refreshed on a best-effort basis, so recheck the next day if a change is missing.
- Effective access: run Policy Analyzer with
--identity="serviceAccount:SA_EMAIL"to confirm what the account can now reach, including inherited grants. - No breakage: watch Cloud Logging for
PERMISSION_DENIEDerrors from the account for a full business cycle, including month-end and batch jobs. - Recommendation state: applied recommendations show as succeeded, and new recommendations for the same principal stop appearing.
Rollback and common errors
| Situation | Fix |
|---|---|
| A workload fails after removing Editor | Read the denied permission from the error log and grant the predefined role that contains it; re-add Editor temporarily only with an expiry ticket |
| Large change needs full restore | gcloud projects set-iam-policy PROJECT_ID PROJECT_ID-policy.json, after checking that nobody else changed the policy since the export (the etag will differ) |
| Default service account was deleted and a service now fails | Google documents undelete for a limited period after deletion; check the current window in the docs and act quickly, otherwise recreate a dedicated account and rebind |
| Service agent stops working after a role change | Restore its role from the export; only change service agent roles through a role recommendation |
| Account keeps reappearing in the search | A grant at folder or organisation level, or infrastructure as code re-applying the binding; fix it at the source |
| Recommendation looks wrong | Infrequent jobs fall outside 90 days; dismiss it with a reason and record the job schedule |
Hardening afterwards
- Stop automatic grants: enforce
iam.automaticIamGrantsForDefaultServiceAccountsat the organisation node. See the Google Cloud Organization Policy Guide. - Alert on new basic role grants: create a log-based alert on Admin Activity
SetIamPolicyentries whose binding delta adds a basic role to aserviceAccount:member. Treat any grant at folder or organisation level as high severity. - Remove the keys: move remaining keyed accounts to attached service accounts or federation. See How to inventory Google Cloud service account keys and replace them with federation.
- Policy as code: block
roles/owner,roles/editor,roles/adminandroles/writerfor service account members in your Terraform or deployment pipeline checks, with an exception process. - Record ownership: every remaining service account needs an owner and purpose. The Service Account Inventory Template gives the fields.
Small estate or large organisation
A handful of projects can be cleared with the four commands above and the role recommendations shown in the console. An organisation with hundreds of projects should export search results to BigQuery on a schedule, work the list centrally by priority, and budget for Policy Analyzer query limits: the free allowance of 20 queries per organisation per day runs out quickly when every account needs an impersonation check, so batch those queries or use long-running analysis written to BigQuery or Cloud Storage.
Metrics
| Metric | Target direction |
|---|---|
| Service accounts holding any basic role | Towards zero in production |
| Basic role grants at folder or organisation level to service accounts | Zero |
| Default service accounts holding Editor | Zero |
| Open IAM role recommendations older than 30 days | Falling |
| Principals able to impersonate a privileged service account | Falling, each one justified |
| Time from new basic role grant to alert triage | Under one business day |
Common failure patterns
- Searching per project only: folder and organisation grants never show up, so the same problem reappears in every new project.
- Removing before granting: the basic role goes first, a nightly job fails, and Editor is restored permanently "to be safe".
- Ignoring impersonation: the Owner account is fixed but a developer group keeps Token Creator on a second privileged account.
- Touching service agents by hand: revoking a Google-managed agent's role breaks a managed service in ways that are slow to diagnose.
- Trusting a short observation window: quarterly and annual jobs look unused in 30 days; confirm with the owner before removal.
Framework mapping
| Framework | Reference | How this task supports it |
|---|---|---|
| NIST SP 800-53 Rev 5 | AC-6 Least Privilege; AC-6(5) Privileged Accounts; AC-6(7) Review of User Privileges | Basic roles removed from workloads; privileged service accounts reviewed |
| NIST CSF 2.0 | PR.AA-05 | Access permissions managed and reviewed with least privilege |
| MITRE ATT&CK | T1078.004 Cloud Accounts; T1098.003 Additional Cloud Roles | Less value in a stolen workload credential; alerting on new role grants |
Practitioner checklist
- Search the organisation scope for service accounts with all six basic roles and export the results.
- Separate default service accounts, user-managed accounts, cross-project accounts and service agents.
- Map who holds
actAsandgetAccessTokenon each flagged account. - Pull IAM role recommendations and key lists for every flagged account.
- Export each project's allow policy before any change.
- Grant narrower roles first, then remove the basic role, then trim impersonation grants.
- Enforce
iam.automaticIamGrantsForDefaultServiceAccountsand alert on new basic role grants. - Rerun the search monthly and report the metrics.
Standards and references
- Google Cloud: Search allow policies
- Google Cloud: Analyze allow policies
- Google Cloud: Role recommendations overview
- Google Cloud: Review and apply role recommendations
- Google Cloud: Best practices for using service accounts securely
- Google Cloud: Service account types
- Google Cloud: Roles and permissions
- Google Cloud: Roles for service account authentication
- NIST: SP 800-53 Rev. 5
Related NHI Mgmt Group resources: Top 10 GCP IAM Mistakes · Google Cloud IAM and Service Accounts Guide · Google Cloud Service Account Keys Security Guide · Service Account Security Guide