In Google Cloud, most serious access problems come down to three things: a role that grants far more than the task needs, a service account key that never expires, and a principal who can quietly act as a more powerful service account. This guide goes below the platform overview into how GCP IAM decides access: allow policies and inheritance, basic, predefined and custom roles, IAM conditions, deny and Principal Access Boundary policies, service account types, the roles that let one identity become another, and the constraints that control keys. It also covers the IAM recommender, Policy Analyzer and the audit log fields that show who impersonated whom. It is for cloud security engineers, platform teams and IAM owners. For the wider picture see the Google Cloud Security Guide.
Key takeaways
- Access is the union of inherited allow policies, minus deny and boundary policies. A binding on the organisation or a folder reaches every project below it, so review the highest grants first.
- Basic roles are a production anti-pattern. Owner, Editor and Viewer (the old primitive roles) carry thousands of permissions and cannot take IAM conditions.
- Two roles decide who can become a service account. Service Account User (
roles/iam.serviceAccountUser) attaches it to a resource; Service Account Token Creator (roles/iam.serviceAccountTokenCreator) mints tokens as it. Grant both on individual accounts, never on a project. - Keys are the exception, not the method. Enforce
iam.managed.disableServiceAccountKeyCreation(or legacyiam.disableServiceAccountKeyCreation), set expiry and exposure response, and retire the keys that already exist. - Measure least privilege with Google's own tools. The IAM recommender compares grants with 90 days of use, Policy Analyzer answers "who can access this", and Data Access logs record every impersonation.
How a GCP IAM policy decides access
Every decision starts with an allow policy. "You can attach only one allow policy to each resource", and each policy is a list of role bindings, each tying one role to one or more principals, optionally with a condition.
- Principals: written with a prefix such as
user:,group:,serviceAccount:ordomain:, plus principal sets for federated and organisation-wide identities. Deleted principals stay in policies asdeleted:entries, a useful clean-up signal. - Policy version: version 1 "Does not support conditional role bindings"; version 3 adds the
conditionfield. Tooling that rewrites policies must request version 3 or it can strip conditions. - Concurrency: the
etagis "used for concurrency control"; a stale write fails with HTTP 409. Always read, modify and write with the etag. - Limits: up to 1,500 principals per allow policy, of which up to 250 can be Google groups. Hitting the limit means grants should move to groups.
GCP IAM inheritance
The effective policy is "the union of" the policy on the resource and those on all its ancestors (organisation, folders, project), and access "is granted if any of the higher-level role bindings grant access to the request." A project cannot opt out of an Editor binding set on its folder. Access is only taken away by removing the binding where it was granted, or by a deny or Principal Access Boundary policy.
Basic, predefined and custom roles
| Role type | Examples and name format | Key constraints | When to use |
|---|---|---|---|
| Basic, legacy (formerly primitive roles) | roles/owner, roles/editor, roles/viewer | "Basic roles include thousands of permissions across all Google Cloud services"; no conditions on their bindings | Only the Owner each project needs, and sandboxes |
| Basic, new (Preview) | roles/admin, roles/writer, roles/reader | Cannot be granted in the console; equally broad | Same narrow cases |
| Predefined | roles/SERVICE.IDENTIFIER, e.g. roles/storage.objectViewer | Maintained by Google; some broader than one task needs | The default for people and workloads |
| Custom | projects/PROJECT_ID/roles/ID, organizations/ORG_ID/roles/ID | Not at folder level; 300 per organisation and 300 per project; you maintain them | Where a predefined role is provably too broad |
Google is unambiguous: "In production environments, do not grant basic roles unless there is no alternative." Editor is the one that matters in practice, because default service accounts and old projects still carry it.
Building a GCP custom role safely
- Scope: created in an organisation or project, and grantable only there. Project-level roles cannot hold folder- or organisation-only permissions.
- Permission support: use only
SUPPORTEDpermissions;TESTINGones are "Not recommended for production use" andNOT_SUPPORTEDones cannot be used. - Launch stage as rollback: a role set to DISABLED still appears in policies but has no effect, which makes disabling a fast rollback. Deleted role IDs cannot be reused until a 44-day deletion process completes.
- Change control: "You are responsible for maintaining custom roles", and Google advises letting only "a small number of highly trusted principals" edit them, since adding a permission to a widely granted role grants it to everyone holding it.
GCP IAM conditions
Conditions add attribute checks to an allow binding, written in a subset of Common Expression Language (CEL).
| Attribute | Example use |
|---|---|
request.time | Elevated access that expires on a set date, a simple form of just-in-time access |
resource.name, resource.type, resource.service | A role only on buckets with a name prefix, or only on compute.googleapis.com/Instance |
| Resource tags | Grant only where a resource carries env=dev; tags are also the only condition deny rules recognise |
| Destination IP and port, access levels | Restrict Identity-Aware Proxy TCP tunnels and web apps |
Limits matter. "You cannot use conditions when you grant legacy basic roles, including Owner", nor on grants to all users; "Only some resource types accept conditions"; and Cloud Storage needs uniform bucket-level access. Conditions never subtract: "Conditional role bindings do not override role bindings with no conditions", so a time-bound grant is pointless if the principal also holds the role unconditionally through a group. Google advises no more than 100 conditional bindings per allow policy.
GCP IAM deny policies and Principal Access Boundary
Deny policies: "IAM always checks relevant deny policies before checking relevant allow policies." They attach to an organisation, folder or project and bind all descendants "even if the folders and projects within that organization have more permissive deny policies." A rule lists deniedPrincipals, optional exceptionPrincipals, deniedPermissions in the form SERVICE_FQDN/RESOURCE.ACTION (for example iam.googleapis.com/serviceAccountKeys.create), optional exceptionPermissions and a denialCondition that can test only resource tags. Each resource can hold up to 500 deny policies with 500 rules in total. "Only some permissions can be denied", and a rule covering service agents must list them as exceptions or Google services break.
Principal Access Boundary (PAB) policies "let you define the resources that principals can access". They bind to principal sets (workforce or workload identity pools, a Workspace domain, or all principals in a project, folder or organisation) and list the projects, folders and organisations those principals are eligible to touch. They "can only block or not block access" and "fail closed": once a principal is under any PAB policy, anything none of its policies make eligible is blocked. Limits are 500 rules and 500 resources per policy, 1,000 policies per organisation and 10 per principal set. Pin the enforcement version: Google warns that latest "might cause principals to lose access to resources unexpectedly".
| Question | Allow policy | Deny policy | Principal Access Boundary |
|---|---|---|---|
| Attached to | The resource | Organisation, folder or project | Principal sets, through bindings |
| Effect | Grants roles | Blocks named permissions for named principals | Limits which resources principals can reach |
| Conditions | Time, resource, tags | Tags only | principal.type, principal.subject |
| Typical use | Least-privilege grants | Only break-glass may create keys, delete log sinks or change organisation IAM | Your service accounts never act on projects outside your organisation |
For the cross-cloud view of allow, deny and boundary controls, see the Cloud PAM and CIEM Guide.
GCP service accounts and the default compute service account
A GCP service account is both an identity (it holds roles) and a resource (others hold roles on it). Google separates user-managed accounts, which you create, from service agents, which Google manages.
| Type | Email format | Security note |
|---|---|---|
| User-managed | NAME@PROJECT_ID.iam.gserviceaccount.com | One per workload, named for its purpose |
| Default compute service account | [email protected] | "might automatically be granted the Editor role on your project" |
| App Engine default | [email protected] | Same Editor risk |
| Service agents | e.g. [email protected] | "Unless a role recommendation suggests it, don't revoke the roles" |
The default compute service account is the most common over-privileged identity in Google Cloud: VMs run as it unless told otherwise, so code on any of them may hold Editor. iam.automaticIamGrantsForDefaultServiceAccounts (enforced by default for organisations created after 3 May 2024) stops the automatic grant but "doesn't prevent default service accounts from being granted the Editor or Owner roles later"; the managed iam.managed.preventPrivilegedBasicRolesForDefaultServiceAccounts prevents it "ever". Then follow Google's advice: "Create dedicated service accounts for each application, and avoid using default service accounts." Enforce iam.disableCrossProjectServiceAccountUsage unless cross-project attachment is a deliberate design.
Service account user role, token creator role and impersonation
| Role | Key permissions | What the holder can do |
|---|---|---|
Service Account User roles/iam.serviceAccountUser | iam.serviceAccounts.actAs | Attach the account to a VM, function or job and run code as it; it "does not allow principals to create short-lived credentials" |
Service Account Token Creator roles/iam.serviceAccountTokenCreator | getAccessToken, getOpenIdToken, implicitDelegation, signBlob, signJwt | Impersonate the account directly and sign as it |
OpenID Connect Identity Token Creator roles/iam.serviceAccountOpenIdTokenCreator | getOpenIdToken | Mint ID tokens only, for example to call Cloud Run |
Workload Identity User roles/iam.workloadIdentityUser | getAccessToken, getOpenIdToken | Lets GKE and federated workloads impersonate the account |
Scope is everything. Grant Token Creator in a project and "the user can impersonate any service account in the Google Cloud project"; Google says "don't manage access to service accounts at the Google Cloud project or folder level." Service Account User at project level likewise lets a developer launch a VM as the most privileged account in the project. Grant both on the individual account, and "Avoid letting users impersonate service accounts that are more privileged than the users themselves."
Used well, GCP service account impersonation replaces downloaded keys: a person or pipeline holds Token Creator on one narrowly scoped account and works with its permissions without a key file existing. The cross-cloud pattern is covered in the Cloud Workload Identity Guide.
Short-lived service account credentials
The IAM Service Account Credentials API (iamcredentials.googleapis.com) issues OAuth 2.0 access tokens, OpenID Connect ID tokens, self-signed JWTs and self-signed blobs through generateAccessToken, generateIdToken, signJwt and signBlob. Access tokens come without a refresh token.
- Lifetime: "By default, the maximum token lifetime is 1 hour (3,600 seconds)." It extends to 12 hours only for accounts listed in
constraints/iam.allowServiceAccountCredentialLifetimeExtension; treat each as an owned exception. ID tokens last one hour. - Delegation chains:
implicitDelegationlets a caller go through A to reach B. Google says the direct method is sufficient "In most cases"; chains blur audit, so allow them only by design. - Hosted and external workloads: attached service accounts get short-lived tokens from the metadata server automatically. Workloads on other clouds, CI/CD or on-premises should use GCP workload identity federation, covered in the Workload Identity Federation Guide.
GCP service account keys and the constraints that control them
A user-managed service account key authenticates as the account from anywhere, and "By default, service account keys that you create and download from IAM don't have an expiry time." Google warns that "authenticating by using a leaked service account key is likely to succeed" and that keys should be "an exception rather than the norm." Service account key authentication belongs only where an attached account, impersonation or federation cannot work.
| Constraint | Effect and notes |
|---|---|
iam.managed.disableServiceAccountKeyCreation | Disables new external keys and Cloud Storage HMAC keys; the managed form Google recommends |
iam.disableServiceAccountKeyCreation | Legacy form; enforced by default for organisations created on or after 3 May 2024 |
iam.managed.disableServiceAccountKeyUpload | Blocks uploading external public keys, the route around the creation block (legacy iam.disableServiceAccountKeyUpload) |
constraints/iam.serviceAccountKeyExpiryHours | Validity of new keys: 1h, 8h, 24h, 168h, 336h, 720h, 1440h or 2160h |
iam.serviceAccountKeyExposureResponse | DISABLE_KEY disables keys Google detects as exposed; set it explicitly |
None of these is retroactive: they "do not affect previously created and configured service accounts", so existing keys keep working until you retire them. Where a key is unavoidable, grant a tagged exception on one project, give it the shortest workable expiry, keep it in a secrets manager rather than on a file system, and rotate it. See the Service Account Security Guide.
Proving least privilege: IAM recommender and Policy Analyzer
GCP IAM recommender: it compares a principal's permissions "with the permissions that the principal used in the last 90 days", adds ML-predicted needs (inferredPermissions), and suggests REMOVE_ROLE, REPLACE_ROLE or REPLACE_ROLE_CUSTOMIZABLE. It "does not apply recommendations automatically" and never increases access except to restore a service agent's default role. Blind spots: conditional bindings, anything outside IAM (ACLs, Kubernetes RBAC), grants younger than about 15 days and inactive projects. Basic-role recommendations are free; policy insights, lateral movement insights (roles that let a service account impersonate one in another project) and other role recommendations need Security Command Center Premium or Enterprise.
GCP Policy Analyzer: it finds "which principals ... have what access to which Google Cloud resources based on your IAM allow policies", for example "Who can access this IAM service account?" It runs at organisation, folder or project scope, expands groups up to 1,000 members, can work out who can impersonate the service accounts in a result, and exports to BigQuery or Cloud Storage. Each organisation gets 20 free queries a day. It does not account for deny or PAB policies, so treat its answer as the worst case.
Detection and measurement
Admin Activity logs always record policy changes and google.iam.admin.v1.CreateServiceAccountKey. Impersonation is different: GenerateAccessToken on iamcredentials.googleapis.com goes to the Data Access log, and "you must enable IAM audit logs for Data Access activity" to get it. Calls made with an impersonated token then carry authenticationInfo.serviceAccountDelegationInfo, whose firstPartyPrincipal names who is behind the token. Key-authenticated calls carry protoPayload.authenticationInfo.serviceAccountKeyName, though App Engine and Compute Engine do not log it.
Alert on organisation- or folder-level SetIamPolicy, project-level grants of Token Creator or Service Account User, new keys, changes to the key constraints and key use on accounts meant to be keyless. These map to ATT&CK T1078.004 Valid Accounts: Cloud Accounts, T1098.001 Additional Cloud Credentials and T1098.003 Additional Cloud Roles.
| Metric | Target | Source |
|---|---|---|
| User-managed keys in production | Zero, or documented exceptions with expiry | IAM API, Cloud Asset Inventory |
| Basic role bindings on production projects and above | Only the required Owner | Policy export, recommender |
| Project-level Service Account User or Token Creator grants | Zero | Policy Analyzer |
| Production workloads on a default service account | Zero | Asset inventory |
| Recommendations open more than 30 days | Falling monthly | Recommender API |
Implementation roadmap
- Inventory: export allow policies at every level; list basic role bindings, project-level Service Account User and Token Creator grants, user-managed keys and workloads on default accounts.
- Stop new damage: apply the managed key and default service account constraints to a test folder first (dry run where supported), fix what breaks, add tagged, time-limited exceptions, then enforce at the organisation. Roll back on the affected folder, not organisation-wide.
- Turn on visibility: enable Data Access logs for IAM and the Service Account Credentials API, route them centrally and build the alerts above.
- Move grants to the account: replace project-level Service Account User and Token Creator with grants on individual accounts, team by team.
- Retire keys: per workload, switch to an attached account, impersonation or federation; when key authentication events reach zero, disable the key, wait a business cycle, then delete. Re-enable if something breaks.
- Prune roles: work the recommender queue, non-production first, with owner sign-off in production.
- Add guardrails: deny policies for key creation, sink deletion and organisation IAM changes with a break-glass exception; then a version-pinned PAB policy tested on one principal set.
Decision guidance
- Predefined or custom role: start predefined; build custom only when the recommender repeatedly shows most of a role unused and the needed permissions are SUPPORTED. A platform team can sustain a small custom library; a small team usually cannot.
- Condition, deny or PAB: a condition narrows one grant; a deny policy blocks a permission for nearly everyone; a PAB policy stops your principals reaching resources outside your estate.
- Which credential: attached service account on Google Cloud; impersonation for people and internal tooling; federation for other clouds, CI/CD and on-premises; a key, with expiry, only when nothing else works.
- Small versus large estates: small estates get most value from the key and default account constraints, predefined roles and free basic-role recommendations. Large or regulated estates add deny and PAB policies, SCC Premium for full recommender coverage, and Policy Analyzer exports in access reviews.
Common failure patterns
- Token Creator at project level "so the team can impersonate the deploy account", which also lets them impersonate every other account in the project.
- Constraint enforced, old keys forgotten: keys created years ago keep working. Leaked cloud credentials are the raw material of abuse such as LLMjacking, where stolen keys ran AI services at victims' expense.
- Default compute service account still holding Editor on projects older than the secure-by-default baseline, so one compromised VM can modify most of the project.
- Time-bound conditions undermined by standing grants held through a group.
- Policy Analyzer read as final: it ignores deny and PAB policies, so exposure is misread.
- Impersonation logs never enabled: after an incident there is no delegation trail showing who acted as the compromised account.
Framework mapping
| Control area | NIST CSF 2.0 | NIST SP 800-53 Rev. 5 | CIS Controls v8.1 | ISO/IEC 27001:2022 |
|---|---|---|---|---|
| Roles, conditions, deny and PAB | PR.AA-05 | AC-3, AC-6 | 6.8 | A.5.15, A.8.2 |
| Service account inventory and ownership | PR.AA-01 | AC-2 | 5.5 | A.5.16 |
| Keys and short-lived credentials | PR.AA-01 | IA-5 | 5, 6 | A.5.17 |
| Recommender and access review | PR.AA-05 | AC-2, AC-6 | 5.3, 6.2 | A.5.18 |
| Impersonation and key-use logging | DE.CM-03 | AU-2, AU-12 | 8.2 | A.8.15 |
Practitioner checklist
- Review allow policies top down, starting with basic role bindings on the organisation and folders.
- Remove basic roles from production except the required Owner; build custom roles only from SUPPORTED permissions.
- Grant Service Account User and Token Creator only on individual service accounts.
- Enforce the managed key creation and upload constraints, key expiry and
DISABLE_KEY, then retire existing keys. - Stop default service accounts holding Editor and give every workload its own account.
- Add deny policies for key creation and logging changes, and pin any PAB policy to an enforcement version.
- Enable Data Access logs for IAM and the Service Account Credentials API and alert on new keys and impersonation grants.
- Work the IAM recommender queue monthly and feed Policy Analyzer exports into access reviews.
Standards and references
- Google Cloud: Roles and permissions
- Google Cloud: Understanding IAM custom roles
- Google Cloud: Overview of IAM Conditions
- Google Cloud: Deny policies
- Google Cloud: Principal Access Boundary policies
- Google Cloud: Roles for service account authentication
- Google Cloud: Restricting service account usage
- Google Cloud: Role recommendations
Related NHI Mgmt Group resources: Google Cloud Security Guide · Cloud Workload Identity Guide · Service Account Security Guide · Azure RBAC and Service Principals Guide · Cloud Logging and Audit Trail Guide