Identity and Access
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. See the section bibliography for the reference material consulted while preparing these pages. |
Commands and behaviour below are current for the gcloud CLI 586.x line, the Google Cloud Client Libraries for Java
(libraries-bom), Spring Cloud GCP 8.x for Spring Boot 4 (7.4.x for Spring Boot 3) and the
google-github-actions/auth action v3 (checked 2026-09-30); re-verify against gcloud version and the linked
reference pages before relying on them in a pipeline.
Every Bookshelf resource (the catalog-api and orders-api Cloud Run services, the bookshelf-uploads-$PROJECT_ID
bucket, the Secret Manager secrets and so on — see Google Cloud) is reached by something that
first has to prove who it is and then be told what it may do. This page is that "who" and "what": Identity and
Access Management (IAM) as the authorization model, service accounts and Workload Identity Federation for
everything that is not a person, and Identity-Aware Proxy (IAP) for putting a sign-in in front of an application. The
project and organization structure that IAM policies inherit through is in
Resource Hierarchy and Projects; the account bootstrap is in
Getting Started with Google Cloud.
IAM concepts
IAM answers one question for every API request: may this principal perform this permission on this resource? The answer comes from policies attached to the resource or to one of its ancestors in the resource hierarchy. IAM is a global service; policies are stored on the resource they protect, not in a region.
- Principal
-
The "who". Principals are Google Accounts (people), Google groups, service accounts (workloads), Cloud Identity or Google Workspace domains, and federated identities from Workforce Identity Federation (people from an external identity provider) or Workload Identity Federation (workloads outside Google Cloud). The special values
allUsersandallAuthenticatedUsersmake a resource public and are best avoided. - Permission
-
A single allowed operation, written
service.resource.verb, for examplestorage.buckets.listorrun.services.invoke. Permissions are never granted directly. - Role
-
A named collection of permissions. A principal is granted a role, never an individual permission.
- Allow policy
-
A list of bindings attached to a resource, each binding tying one role to a list of principals (optionally with a condition). Older documents call this simply an "IAM policy".
- Resource
-
Anything with an IAM policy: an organization, a folder, a project, or a service resource such as a bucket, a Cloud Run service, a secret or a Pub/Sub topic.
The practical rule for Bookshelf: people are in Google groups that receive roles, workloads run as dedicated service accounts with narrow roles, CI authenticates through Workload Identity Federation, and no one downloads a service-account key.
Role types
| Role type | What it is | When to use it |
|---|---|---|
Basic ( |
Thousands of permissions across nearly every service. Historically the only roles; they predate the others. |
Throwaway projects only. Never grant them to a service account or to a production workload. |
Predefined ( |
Maintained by Google for one service and one job function, updated as the service gains permissions. |
The default choice. Pick the narrowest one that covers the task. |
Custom |
A role you define from a list of permissions, at the organization or project level. |
When no predefined role is narrow enough. You maintain it as permissions change. |
Each role also has a launch stage and a list of permissions that can be checked before granting it, and not every permission can be used in a custom role (the role reference marks the exceptions).
Policy inheritance
Policies are inherited downwards: a binding on an organization applies to every folder, project and resource below it, and a binding on a project applies to every resource inside the project. The effective permissions of a principal are the union of the allow bindings on the resource and all its ancestors. A child policy can add access but cannot remove what a parent granted; to take access away you use a deny policy (below), not an allow policy lower down.
binding: group admins] --> F[Folder bookshelf
binding: group developers] F --> P[Project my-project-id
binding: ci service account] P --> B[Bucket bookshelf-uploads
binding: catalog-api service account] P --> S[Cloud Run service catalog-api
binding: IAP service agent] B --> E[Effective access =
union of every binding above and on the resource]
Bind at the lowest level that makes sense: a role on one bucket or one secret beats the same role on the project.
Granting roles
In the Google Cloud console
-
Select the project (or folder, or organization) in the project picker, then open IAM & Admin → IAM.
-
Click Grant access, enter the principal (an email, a group or a service-account address) and choose a role. The role selector filters by product and shows the permission list of the selected role.
-
Optionally click Add IAM condition to restrict the binding (see below), then Save.
-
To grant on a single resource, open that resource (for example a bucket’s Permissions tab) instead.
-
To check what a principal can already do, use IAM & Admin → Policy Troubleshooter, or open IAM and switch to the View by roles tab.
-
Open the Recommendations column or the Role Recommender hints to see roles that can be reduced.
With the gcloud CLI
gcloud services enable iam.googleapis.com iamcredentials.googleapis.com \
sts.googleapis.com cloudresourcemanager.googleapis.com \
recommender.googleapis.com
PROJECT_ID=$(gcloud config get-value project)
BUCKET=bookshelf-uploads-$PROJECT_ID
# Organization, folder and project level
gcloud organizations add-iam-policy-binding ORGANIZATION_ID \
--member="group:platform-admins@example.com" \
--role="roles/resourcemanager.folderAdmin"
gcloud resource-manager folders add-iam-policy-binding FOLDER_ID \
--member="group:bookshelf-devs@example.com" \
--role="roles/run.developer"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="group:bookshelf-devs@example.com" \
--role="roles/logging.viewer"
# A single resource: the least-privilege option
gcloud storage buckets add-iam-policy-binding "gs://$BUCKET" \
--member="serviceAccount:catalog-api@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/storage.objectUser"
# Inspect and review
gcloud projects get-iam-policy "$PROJECT_ID" --format=yaml
gcloud iam roles describe roles/storage.objectUser
Derived from Grant, change and revoke access, gcloud projects add-iam-policy-binding and gcloud storage buckets add-iam-policy-binding.
Every add-iam-policy-binding reads the policy, edits it and writes it back with its etag, so two concurrent
callers do not silently overwrite each other. Prefer add-iam-policy-binding and remove-iam-policy-binding over
set-iam-policy with a hand-edited file, which replaces the whole policy.
Custom roles
A custom role is created from a list of permissions. The smallest useful example is a role that can only list the
buckets of a project (storage.buckets.list), for example for an audit tool:
gcloud iam roles create bucketLister \
--project="$PROJECT_ID" \
--title="Bucket Lister" \
--description="List buckets in the project, nothing else" \
--permissions=storage.buckets.list \
--stage=GA
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:auditor@$PROJECT_ID.iam.gserviceaccount.com" \
--role="projects/$PROJECT_ID/roles/bucketLister"
Derived from Create and manage custom roles and gcloud iam roles create.
In the console use IAM & Admin → Roles → Create role, or Create role from selection after ticking existing roles. Custom roles are a maintenance burden (new permissions in a service do not appear in them) and have per-project and per-organization quotas, so prefer a predefined role when one fits.
IAM Conditions
A binding can carry a Common Expression Language condition so that it applies only to some resources or at some
times. For example, allow the Bookshelf notifier service account to read objects only under a prefix:
gcloud storage buckets add-iam-policy-binding "gs://$BUCKET" \
--member="serviceAccount:notifier@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/storage.objectViewer" \
--condition='expression=resource.name.startsWith("projects/_/buckets/'"$BUCKET"'/objects/covers/"),title=covers-prefix-only'
Derived from IAM Conditions overview and Use IAM permissions (Cloud Storage).
Conditions can also test time (temporary access), resource tags, or the resource type, and conditional bindings require policy version 3.
Deny policies, boundaries and just-in-time access
Allow policies only add access. Three further features narrow it or make it temporary:
-
Deny policies are attached to an organization, folder or project and block named permissions for named principals regardless of any allow policy. They are evaluated before allow policies, so they are the tool for guardrails such as "nobody outside the platform group may delete projects".
-
Principal Access Boundary policies define the set of resources a group of principals is even eligible to access, independent of the roles they hold.
-
Privileged Access Manager grants time-bound, approved elevation, so people hold no standing privileged roles.
Each is created with its own gcloud iam command group (for example gcloud iam policies create for deny policies);
see the linked pages for the syntax. Together with the allow policy, the order of evaluation is: deny policies first,
then principal access boundaries, then allow policies.
The IAM recommender closes the loop: it compares granted permissions with the permissions actually used over the last 90 days and suggests narrower roles.
gcloud recommender recommendations list \
--project="$PROJECT_ID" --location=global \
--recommender=google.iam.policy.Recommender \
--format="table(name.basename(),description)"
Service accounts
A service account is a principal for a workload rather than a person. It has an email address of the form
NAME@PROJECT_ID.iam.gserviceaccount.com and is both an identity (granted roles on resources) and a resource (other
principals are granted roles on it, such as permission to use or impersonate it).
Three ways exist for code to act as a service account, in order of preference:
-
Attach the service account to the compute resource, so the platform supplies short-lived tokens.
-
Impersonate the service account from another identity, producing short-lived tokens on request.
-
Create and download a key — a long-lived secret. This is the last resort.
Default and user-managed service accounts
Some services create a default service account on first use (for example the Compute Engine default service account).
In older projects these were granted the broad roles/editor role automatically. Organizations created on or after
2024-05-03 are secure by default, and the constraint iam.automaticIamGrantsForDefaultServiceAccounts stops that
grant, so a workload on a default service account in such an organization has no permissions until you give it some.
Create one dedicated user-managed service account per workload instead (catalog-api, orders-api, notifier) and
grant it only the roles it needs.
Creating and attaching a service account
In the Google Cloud console
-
Open IAM & Admin → Service Accounts → Create service account.
-
Enter a name and ID (the ID is permanent and forms the email address) and click Create and continue.
-
Add the roles it needs on the project (preferably leave this empty and grant on individual resources later) and click Done.
-
When creating a Cloud Run service, a Compute Engine VM or a GKE node pool, choose this account in its Security (or Service account) setting. That is "attaching" it.
With the gcloud CLI
gcloud iam service-accounts create catalog-api \
--display-name="Bookshelf catalog-api runtime"
SA=catalog-api@$PROJECT_ID.iam.gserviceaccount.com
# Narrow roles on specific resources
gcloud storage buckets add-iam-policy-binding "gs://$BUCKET" \
--member="serviceAccount:$SA" --role="roles/storage.objectUser"
gcloud secrets add-iam-policy-binding catalog-db-password \
--member="serviceAccount:$SA" --role="roles/secretmanager.secretAccessor"
# Attach it to a Cloud Run service
gcloud run services update catalog-api --region="$REGION" \
--service-account="$SA"
Derived from Create service accounts, Cloud Run service identity and gcloud iam service-accounts create.
Whoever attaches a service account to a resource needs the permission to act as it (iam.serviceAccounts.actAs,
included in roles/iam.serviceAccountUser). That is deliberate: a developer who can deploy a service must also be
trusted with every permission of the account the service runs as. Grant roles/iam.serviceAccountUser on the single
service account, not on the project.
Impersonation
Impersonation lets an identity that holds roles/iam.serviceAccountTokenCreator on a service account request
short-lived tokens for it. It is the keyless way to run a command or a test with a workload’s exact permissions, and
the building block behind Workload Identity Federation for CI.
# Allow a developer group to impersonate the service account
gcloud iam service-accounts add-iam-policy-binding "$SA" \
--member="group:bookshelf-devs@example.com" \
--role="roles/iam.serviceAccountTokenCreator"
# One command as the service account
gcloud storage ls "gs://$BUCKET" --impersonate-service-account="$SA"
# Or for the whole gcloud configuration
gcloud config set auth/impersonate_service_account "$SA"
# Application Default Credentials for local code, impersonating the account
gcloud auth application-default login --impersonate-service-account="$SA"
Derived from Impersonate service accounts.
The token the impersonated call yields lasts one hour by default. Because every impersonation is written to the audit logs against both the caller and the service account, it is also the answer to "who really did this?".
Service-account keys
A service-account key is a JSON file containing a private key that anyone holding the file can use until it is
deleted. Older guides still show downloading one for local development or for a CI pipeline. Today it is the last
resort, and in organizations created on or after 2024-05-03 it fails by default: the managed constraints
iam.managed.disableServiceAccountKeyCreation and iam.disableServiceAccountKeyUpload are enforced, so
gcloud iam service-accounts keys create returns a policy error.
# See whether key creation is blocked for the project
gcloud org-policies describe iam.disableServiceAccountKeyCreation \
--project="$PROJECT_ID" --effective
Derived from Secure by default organization resources and Best practices for managing service account keys.
Do not lift that constraint to make a tutorial work. Use the replacements instead: an attached service account for
code running on Google Cloud, impersonation for a developer machine, and Workload Identity Federation for anything
running elsewhere. Where a key truly cannot be avoided (a third-party system with no federation support), scope the
exception to a single project, give the key a short expiry with iam.serviceAccountKeyExpiryHours, store it in
Secret Manager (see Secret Manager and Cloud KMS) and rotate it.
Workload Identity Federation
Workload Identity Federation lets a workload that runs outside Google Cloud (GitHub Actions, GitLab CI, AWS, Azure, an on-premises OIDC or SAML provider) exchange its own identity token for a short-lived Google Cloud access token, with no secret stored on the workload. A workload identity pool holds external identities, and a provider in the pool describes how to trust one issuer and how to map its token claims into attributes. GKE has its own variant, Workload Identity Federation for GKE, covered in GKE Clusters.
In the simplest form (direct Workload Identity Federation) the second token step is skipped: the roles are granted straight to the federated principal, and the federated token is used as is. Impersonating a service account adds a step but works with every product, including those that do not accept federated principals directly.
Setting up a pool for GitHub Actions
The attribute mapping decides what identity the pool exposes, and the attribute condition decides which tokens are accepted at all. Always set a condition: without one, any GitHub repository in the world could present a token to your pool.
In the Google Cloud console
-
Open IAM & Admin → Workload Identity Federation → Create pool.
-
Name the pool (for example
github), click Continue, choose the provider type OpenID Connect (OIDC), name the provider and set the Issuer (URL) tohttps://token.actions.githubusercontent.com. -
Under Configure provider attributes map
google.subjecttoassertion.sub, addattribute.repository=assertion.repositoryandattribute.repository_owner=assertion.repository_owner, and set an attribute condition that restricts the organization. -
Save, then use Grant access on the pool to bind a service account or a role to the principal that matches your repository.
With the gcloud CLI
PROJECT_NUMBER=$(gcloud projects describe "$PROJECT_ID" --format='value(projectNumber)')
gcloud iam workload-identity-pools create github \
--project="$PROJECT_ID" --location=global \
--display-name="GitHub Actions"
gcloud iam workload-identity-pools providers create-oidc bookshelf-repo \
--project="$PROJECT_ID" --location=global \
--workload-identity-pool=github \
--issuer-uri="https://token.actions.githubusercontent.com/" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
--attribute-condition="assertion.repository_owner=='my-org'"
# Let the bookshelf/catalog-api repository impersonate the deployer service account
gcloud iam service-accounts add-iam-policy-binding \
"deployer@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/github/attribute.repository/my-org/catalog-api"
Derived from Workload Identity Federation with deployment pipelines and gcloud iam workload-identity-pools providers create-oidc.
The documentation recommends matching on numeric identifiers (for example the repository owner ID) rather than names where the provider offers them, so a deleted and re-registered name cannot inherit trust.
A GitHub Actions workflow then needs the id-token: write permission and one authentication step. The action is
v3 at the time of writing; use the current major version listed in its repository:
name: deploy
on:
push:
branches: [main]
permissions:
contents: read
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- id: auth
uses: google-github-actions/auth@v3
with:
project_id: my-project-id
workload_identity_provider: projects/<project-number>/locations/global/workloadIdentityPools/github/providers/bookshelf-repo
service_account: deployer@my-project-id.iam.gserviceaccount.com
- uses: google-github-actions/setup-gcloud@v3
- run: gcloud run deploy catalog-api --source . --region us-central1
Derived from google-github-actions/auth.
The workflow holds no secret at all: the only values are resource names. The broader workflow structure (build, tests, image publishing, environments) is in Docker CI/CD with GitHub Actions; this page covers the Google Cloud side. Container images pushed from the workflow go to Artifact Registry, see Container Registry and Builds.
GitLab CI
GitLab issues an OIDC token to each job through the id_tokens keyword. The provider points at the GitLab issuer
(https://gitlab.com for gitlab.com, or your instance URL) and conventionally maps the project and namespace IDs:
gcloud iam workload-identity-pools create gitlab \
--project="$PROJECT_ID" --location=global
gcloud iam workload-identity-pools providers create-oidc bookshelf-group \
--project="$PROJECT_ID" --location=global \
--workload-identity-pool=gitlab \
--issuer-uri="https://gitlab.com" \
--attribute-mapping="google.subject=assertion.sub,attribute.project_id=assertion.project_id,attribute.namespace_id=assertion.namespace_id" \
--attribute-condition="assertion.namespace_id=='GITLAB_GROUP_ID'"
Derived from Workload Identity Federation with deployment pipelines.
The job declares an id_tokens entry whose audience is the provider’s full resource name, then exchanges that token
as in the diagram above. GitLab’s own
ID token authentication page documents the job side.
Other workloads outside Google Cloud
The same pool and provider structure serves AWS (an AWS provider type), Azure, and any OIDC or SAML issuer, and Workforce Identity Federation does the equivalent for people signing in with an external identity provider instead of a Google Account. For GKE pods, the equivalent is Workload Identity Federation for GKE, which can grant roles directly to a Kubernetes service account principal without a Google service account.
Application Default Credentials in code
Application Default Credentials (ADC) is the lookup strategy the Google client libraries use so the same code runs
locally and in production: they check the GOOGLE_APPLICATION_CREDENTIALS environment variable, then the credentials
written by gcloud auth application-default login, then the attached service account supplied by the metadata
server. In Cloud Run, GKE and Compute Engine this means the code needs no credential configuration at all.
package com.example.bookshelf;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
public final class StorageFactory {
private StorageFactory() {
}
// ADC: attached service account in production, the developer's own
// (or impersonated) credentials locally
public static Storage create() throws java.io.IOException {
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
return StorageOptions.newBuilder()
.setCredentials(credentials)
.build()
.getService();
}
}
Derived from How Application Default Credentials works and GoogleCredentials.
With Spring Cloud GCP the starters build their clients from the same ADC chain, so a Spring Boot service that uses the Cloud Storage or Secret Manager starter needs only a project ID, and no key file property. For the Spring side of storage and configuration see File Storage & Object Stores and Configuration and Profiles.
Where code must call an API as a different service account than the one it runs as (for example a signed URL signed
by a dedicated identity), use ImpersonatedCredentials on top of the ADC source credentials rather than a key.
Identity-Aware Proxy
Identity-Aware Proxy (IAP) puts Google sign-in and an IAM check in front of an application, so the application does
not implement login itself. A request only reaches the backend if the user is authenticated and holds the
IAP-secured Web App User role (roles/iap.httpsResourceAccessor) on the protected resource. IAP itself is offered
at no additional charge.
IAP can protect App Engine, backends behind an external or internal Application Load Balancer, GKE services
(configured on a GCPBackendPolicy with Gateway API; older guides show an Ingress BackendConfig), Compute Engine
and, since 2026-03-13 (generally available), Cloud Run services directly. Before that date IAP in front of Cloud Run
required a load balancer; direct integration protects the default run.app URL and every other ingress path with one
setting, and IAP cannot be configured on both the load balancer and the Cloud Run service. IAP also provides TCP
forwarding for SSH and RDP to VMs without public IPs, which is how Bookshelf administrators reach private
instances; see VPN and Hybrid Connectivity.
Enabling IAP on Cloud Run
In the Google Cloud console
-
Open Cloud Run, select the service and open the Security tab.
-
Under Authentication, choose Require authentication and tick Identity-Aware Proxy (IAP), then save. In a project that is not in an organization, first configure the OAuth consent screen and an OAuth client when prompted.
-
Open Security → Identity-Aware Proxy, select the Cloud Run service, click Add principal and grant IAP-secured Web App User to the people or groups who may sign in.
With the gcloud CLI
gcloud services enable iap.googleapis.com run.googleapis.com
gcloud run services update orders-api --region="$REGION" --iap
# IAP calls the service as its own service agent, so it needs the invoker role
gcloud run services add-iam-policy-binding orders-api --region="$REGION" \
--member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-iap.iam.gserviceaccount.com" \
--role="roles/run.invoker"
# Who may sign in
gcloud iap web add-iam-policy-binding \
--member="group:bookshelf-staff@example.com" \
--role="roles/iap.httpsResourceAccessor" \
--region="$REGION" --resource-type=cloud-run --service=orders-api
Derived from Configure IAP for Cloud Run.
For a new service pass --no-allow-unauthenticated --iap to gcloud run deploy. The service’s own invoker setting
and the other Cloud Run authentication options are covered in Cloud Run.
Trust the signed header, not the plain ones
IAP forwards the identity to the application in headers. X-Goog-Authenticated-User-Email and
X-Goog-Authenticated-User-Id are convenient, but they are plain headers: anything that can reach the backend
without going through IAP (another service in the VPC, a misconfigured firewall, a service with a stale ingress
setting) can send forged values. The header to trust is X-Goog-IAP-JWT-Assertion, a JWT signed by IAP with ES256,
whose issuer is https://cloud.google.com/iap and whose audience identifies the protected resource (for Cloud Run
/projects/PROJECT_NUMBER/locations/REGION/services/SERVICE_NAME). A Spring Boot servlet filter that verifies it:
package com.example.bookshelf.security;
import com.google.api.client.json.webtoken.JsonWebToken;
import com.google.auth.oauth2.TokenVerifier;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.web.filter.OncePerRequestFilter;
public class IapJwtFilter extends OncePerRequestFilter {
private static final String ISSUER = "https://cloud.google.com/iap";
private final TokenVerifier verifier;
public IapJwtFilter(String expectedAudience) {
this.verifier = TokenVerifier.newBuilder()
.setAudience(expectedAudience)
.setIssuer(ISSUER)
.build();
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String jwt = request.getHeader("X-Goog-IAP-JWT-Assertion");
if (jwt == null) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
try {
JsonWebToken verified = verifier.verify(jwt);
request.setAttribute("iap.email", verified.getPayload().get("email"));
chain.doFilter(request, response);
} catch (TokenVerifier.VerificationException e) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
Derived from Securing your app with signed headers.
The same filter can be turned into a Spring Security authentication filter. For general application-level security, see Spring Security and, for Quarkus, Security Overview and Architecture.
Identity Platform
IAP authenticates your staff with Google identities. For customers of an application, Identity Platform (the Google Cloud product that underlies Firebase Authentication) is a managed customer identity service: email and password, phone, social sign-in, SAML and OIDC providers, multi-factor authentication, and multi-tenancy. It issues standard ID tokens that a backend verifies with the Firebase Admin SDK or any OIDC library. Choose it when Bookshelf needs customer sign-up without running its own identity server; choose an identity server such as the one described in Authorization Server & Social Login when you need full control of the OAuth 2.0 and OpenID Connect behaviour.
Identity Platform is enabled per project with gcloud services enable identitytoolkit.googleapis.com and configured
in the console under Identity Platform → Providers; pricing is per monthly active user with a no-cost tier, so
check the pricing page before launch.
Derived from Identity Platform and Firebase Authentication.
For the protocol side, see OAuth Reference, OpenID Connect, Social Login and Federation and JWT and the JOSE Family. Verifying tokens in Quarkus is in JWT, OIDC and Keycloak.
Pricing, quotas and launch stages
-
Cost drivers: IAM, service accounts, Workload Identity Federation, deny policies and IAP are free. Identity Platform is billed per monthly active user beyond its free tier. Privileged Access Manager and some Security Command Center features sit in paid tiers. See the Identity Platform pricing page and re-check it before launch.
-
Quotas: service accounts per project, custom roles per project and organization, and members per policy are limited; the IAM quotas page lists current values and the console shows them under IAM & Admin → Quotas.
-
Launch stages: direct IAP on Cloud Run generally available since 2026-03-13; managed service-account key constraints enforced by default from 2024-05-03 for new organizations. Workload Identity Federation, deny policies, Principal Access Boundary and Privileged Access Manager are available features; confirm the launch stage on each product page before designing around it.
-
Policy propagation: IAM changes are eventually consistent and usually effective within about two minutes, but can take longer; retry a transient
PERMISSION_DENIEDafter a fresh grant before debugging further.
Troubleshooting
-
PERMISSION_DENIED: run the Policy Troubleshooter (console) orgcloud policy-intelligence troubleshoot-policy iamwith the principal, resource and permission to see which binding, deny policy or boundary decided the outcome. -
iam.serviceAccounts.actAsdenied on deploy: the deploying principal lacksroles/iam.serviceAccountUseron the runtime service account. -
Permission 'iam.serviceAccounts.getAccessToken' denied: the caller lacksroles/iam.serviceAccountTokenCreatoron the target, or in Workload Identity Federation the principal lacksroles/iam.workloadIdentityUser. -
The given credential is rejected by the attribute condition: the token claims do not satisfy the provider’s attribute condition; decode the token from a failing job and compare. -
Key creation is not allowed on this service account: the organization policy blocks keys, which is intended; switch to one of the keyless methods above. -
IAP returns 403 after sign-in: the user lacks
roles/iap.httpsResourceAccessoron that resource, or the IAP service agent lacksroles/run.invoker.
Clean up
Remove what this page created, in dependency order, in a throwaway project:
gcloud run services update orders-api --region="$REGION" --no-iap # or clear it in the Security tab
gcloud iam workload-identity-pools providers delete bookshelf-repo \
--project="$PROJECT_ID" --location=global --workload-identity-pool=github
gcloud iam workload-identity-pools delete github \
--project="$PROJECT_ID" --location=global
gcloud iam roles delete bucketLister --project="$PROJECT_ID"
gcloud iam service-accounts delete "$SA"
Derived from gcloud iam workload-identity-pools delete, gcloud iam roles delete and gcloud iam service-accounts delete.
Deleted pools, providers, custom roles and service accounts enter a soft-deleted state and can be undeleted for a
limited period (the retention differs per resource type; see the linked reference pages), and their IDs may not be
reusable immediately; use new IDs when repeating the exercise. Role bindings that
referenced a deleted service account stay in policies as deleted:serviceAccount:… entries until removed. Deleting
the whole throwaway project removes everything at once (see
Resource Hierarchy and Projects).
Related pages
-
Resource Hierarchy and Projects covers the organization, folders and projects that policies inherit through, and the org-policy constraints.
-
Secret Manager and Cloud KMS covers granting access to secrets and keys.
-
Governance, Security and Compliance covers audit logs, Cloud Asset Inventory and Security Command Center.
-
Docker CI/CD with GitHub Actions covers the workflow that uses the federation set up above.
-
Identity and Access and Identity and Access are the AWS and Azure counterparts.