"category": "CI/CD federation" · job/hcp-terraform-dynamic-credentials
Scoping HCP Terraform dynamic credentials: trust for plan and apply
How HCP Terraform workload identity tokens are built, why plan and apply deserve different roles, and the trust configuration each cloud needs.
- The sub claim names the organization, project, workspace and run phase, all by name.
- HashiCorp says to check, at minimum, the audience and the organization name.
- A plan runs before anyone approves it, so give plan a read-only role and keep write access for apply.
- Microsoft Entra needs two credentials per workspace, one for each run phase.
HCP Terraform’s dynamic provider credentials let a run sign in to a cloud with a short-lived OIDC token instead of stored keys. Every HCP Terraform organization’s tokens come from the same issuer, https://app.terraform.io. AWS treats it as a shared provider and requires app.terraform.io:sub in trust policies. Its docs add that STS denies requests when a trust policy doesn’t evaluate that key.[AWS IAM User Guide][AWS IAM User Guide]
What is in the token?
The subject names where and when the run happens:[HashiCorp Developer]
organization:my-org:project:Default Project:workspace:my-workspace:run_phase:apply
The token also carries ID claims: terraform_organization_id (for example org-GRNbCjYNpBB6NEH9), terraform_project_id and terraform_workspace_id, along with the matching _name claims, terraform_full_workspace, terraform_run_id and terraform_run_phase. HashiCorp notes that names, such as project names, can be changed.[HashiCorp Developer] A trust rule that matches names stops matching after a rename and could match a different workspace that later takes the old name.
AWS: aud and sub, per phase
HashiCorp’s AWS guide checks two keys and says to always check, at minimum, the audience and the organization name:[HashiCorp Developer]
"Condition": {
"StringEquals": {
"app.terraform.io:aud": "aws.workload.identity",
"app.terraform.io:sub": "organization:my-org:project:Default Project:workspace:my-workspace:run_phase:apply"
}
}
The workspace tells HCP Terraform which role to use with environment variables: TFC_AWS_PROVIDER_AUTH=true and TFC_AWS_RUN_ROLE_ARN, or TFC_AWS_PLAN_ROLE_ARN and TFC_AWS_APPLY_ROLE_ARN for one role per phase.[HashiCorp Developer]
If both phases really need the same permissions, HashiCorp’s pattern is StringLike with run_phase:* at the end of the subject.[HashiCorp Developer] Avoid wildcards anywhere earlier in the string: organization:my-org:* lets every workspace in the organization in, including ones created later.
Microsoft Entra: two credentials per workspace
Entra matches the subject as a direct string, so HashiCorp’s Azure guide creates two federated identity credentials for each workspace, one ending run_phase:plan and one ending run_phase:apply.[HashiCorp Developer] The issuer is https://app.terraform.io with no trailing slash. The audience is api://AzureADTokenExchange unless you set TFC_AZURE_WORKLOAD_IDENTITY_AUDIENCE.[HashiCorp Developer]
[
{ "name": "my-workspace-plan", "issuer": "https://app.terraform.io",
"subject": "organization:my-org:project:Default Project:workspace:my-workspace:run_phase:plan",
"audiences": ["api://AzureADTokenExchange"] },
{ "name": "my-workspace-apply", "issuer": "https://app.terraform.io",
"subject": "organization:my-org:project:Default Project:workspace:my-workspace:run_phase:apply",
"audiences": ["api://AzureADTokenExchange"] }
]
To give plan less access in Azure, put the plan credential on a separate app registration or managed identity with fewer role assignments.
Google Cloud: the organization ID
Google requires an attribute condition for HCP Terraform providers on your organization ID, optionally narrowed to a workspace, and suggests mapping google.subject to the workspace ID:[Google Cloud documentation]
assertion.terraform_organization_id == 'org-GRNbCjYNpBB6NEH9' &&
assertion.terraform_workspace_id == 'ws-mbsd5E3Ktt5Rg2Xm'
Google notes that HCP Terraform doesn’t support direct resource access, so the run impersonates a service account.[Google Cloud documentation] Because this condition uses IDs, renaming the organization or workspace doesn’t break it.
Checklist
- The trust record checks the audience and names the organization exactly.
- Project and workspace are pinned; no
*beforerun_phase:. - Plan uses a read-only role (AWS) or identity (Azure); apply has write access.
- Trust is updated in the same change that renames or deletes a workspace.
The OIDC trust checker has an HCP Terraform preset for plan and apply on all three clouds.
Questions people ask
- What audience does HCP Terraform use?
- aws.workload.identity for AWS by default, set by TFC_AWS_WORKLOAD_IDENTITY_AUDIENCE if you change it. For Azure the default is api://AzureADTokenExchange.
- Can one role serve both plan and apply?
- Yes. HashiCorp shows StringLike with run_phase:* for that case. If the phases need different permissions, it says to create two roles and two trust policies.
- Do these rules apply to Terraform Enterprise?
- The token format is the same, but the issuer is your Terraform Enterprise address instead of https://app.terraform.io, so the provider and condition-key prefix change to match.
Sources
- Workload identity tokens — HashiCorp Developer.# official documentation (issuer) · checked November 10, 2026 (full page text)
- Use dynamic credentials with the AWS provider — HashiCorp Developer.# official documentation (issuer) · checked November 10, 2026 (full page text)
- Use dynamic credentials with the Azure provider — HashiCorp Developer.# official documentation (issuer) · checked November 10, 2026 (full page text)
- Identity-provider controls for shared OIDC providers — AWS IAM User Guide.# official documentation · checked November 10, 2026 (full page text)
- Create a role for OpenID Connect federation (console) — AWS IAM User Guide.# official documentation · checked November 10, 2026 (full page text)
- Configure Workload Identity Federation with deployment pipelines — Google Cloud documentation.# official documentation · checked November 10, 2026 (full page text)
Update history
- — First published.
Related guides
3 jobs- job/gitlab-ci-oidc-trust category CI/CD federation
Scoping GitLab CI OIDC trust in AWS, Azure and Google Cloud
How GitLab ID tokens are matched by each cloud, which claims stay stable when a project moves, and worked trust configurations for a protected branch on GitLab.com.
· 7 min - job/github-oidc-trust-aws category CI/CD federation
One workflow, three clouds: how AWS, Azure and Google Cloud match GitHub OIDC tokens
The same GitHub Actions token is checked three different ways. A side-by-side comparison of where the trust lives, how claims are matched, wildcard support, limits and how failures show up.
· 7 min - job/oidc-subject-recycling category Risks
Subject recycling: when a deleted GitHub name still unlocks your cloud role
Trust rules that match names can be satisfied by whoever registers that name next. What the 2026 research found, what GitHub, GitLab and the clouds changed, and how to audit your own roles.
· 7 min