The traditional paradigm of infrastructure as code (IaC) authentication has long been plagued by the "secret management paradox." To automate the deployment of secure infrastructure, engineers were forced to store highly privileged long-lived credentials—such as Azure Service Principal client secrets—within CI/CD environment variables or external vaults. This approach introduced significant security risks, including secret leakage, the operational overhead of rotation schedules, and the danger of over-privileged long-lived tokens. Terraform Workload Identity and Workload Identity Federation represent a fundamental shift in this architecture, moving the industry from static secrets to short-lived, verifiable cryptographic identities based on the OpenID Connect (OIDC) protocol.
Understanding Terraform Workload Identity
Terraform Workload Identity is the underlying mechanism that powers Dynamic Provider Credentials. At its core, it allows HCP Terraform to present a verifiable set of information about a specific Terraform workload to an external system (such as a cloud provider). This identity is not a password or a key, but rather a digital "identity card" in the form of a JSON Web Token (JWT).
When a Terraform run—whether it is a plan or an apply—is initiated, HCP Terraform generates a workload identity token. This token is signed using HCP Terraform’s private key, ensuring that any system possessing the corresponding public key can verify the token's authenticity and guarantee that it has not been tampered with. This process eliminates the need for the cloud platform to store a secret for HCP Terraform; instead, it relies on a trust relationship established via OIDC.
The power of this system lies in its ability to convey specific metadata, known as claims, about the workload. Instead of simply saying "I am Terraform," the token can specify exactly which organization, project, workspace, and run phase is requesting access. This allows cloud administrators to implement granular, least-privilege access control policies based on the actual context of the execution.
The Mechanics of OIDC and JWT Tokens
Workload identity is built upon the OpenID Connect (OIDC) protocol, an identity layer on top of the OAuth 2.0 protocol. The primary vehicle for this identity is the JSON Web Token (JWT), which consists of a header and a payload.
Token Structure and Verification
The header contains metadata about the token itself, such as the algorithm used for signing (RS256) and the key identifier (kid). The payload contains the claims—the actual identity data. When a cloud provider receives a token, it performs the following steps:
1. It retrieves the public key from HCP Terraform's OIDC provider endpoint.
2. It verifies the cryptographic signature of the JWT using that public key.
3. It inspects the claims within the payload to ensure the token has not expired (exp) and is being used by an authorized entity.
Workspace Run Token Analysis
For standard workspace runs, the token contains comprehensive metadata that allows for precise scoping. This prevents a "blast radius" issue where one compromised workspace could potentially modify resources belonging to another.
Table 1: Decoded HCP Terraform Workspace Token Claims
| Claim | Description | Example Value |
|---|---|---|
| jti | JWT ID; a unique identifier for each single token | 1192426d-b525-4fde-9d42-f238be437bbd |
| iss | The issuer of the token | https://app.terraform.io |
| aud | The intended audience for the token | my-example-audience |
| sub | The subject; a unique string identifying the workload | organization:my-org:project:Default Project:workspace:my-workspace:run_phase:apply |
| terraformorganizationid | Internal ID of the HCP Terraform organization | org-GRNbCjYNpBB6NEH9 |
| terraformorganizationname | Human-readable organization name | my-org |
| terraformprojectid | Internal ID of the project | prj-vegSA59s1XPwMr2t |
| terraformprojectname | Human-readable project name | Default Project |
| terraformworkspaceid | Internal ID of the workspace | ws-mbsd5E3Ktt5Rg2Xm |
| terraformworkspacename | Human-readable workspace name | my-workspace |
| terraformfullworkspace | Full path to the workspace | organization:my-org:project:Default Project:workspace:my-workspace |
| terraformrunid | Unique ID for the specific execution | run-X3n1AUXNGWbfECsJ |
| terraformrunphase | The current phase of the run (plan or apply) | apply |
Specialization for Module Testing
Terraform Workload Identity provides a distinct mechanism for module testing. Because module tests are read-only operations, they require different permissions and identity structures than standard workspace runs.
When a module test is triggered, HCP Terraform generates a specific token. The most significant difference is found in the subject (sub) claim and the terraform_run_phase. While a workspace run tracks the project and workspace, a module test token tracks the specific module being tested.
The subject format for module tests is:
organization:{ORGANIZATION_NAME}:module:{MODULE_NAME}:operation:test_run
Example: organization:my-org:module:terraform-aws-vpc:operation:test_run
This allows security administrators to create trust policies that permit a module test to perform "read" actions across a subscription without granting it the "write" permissions required for a full deployment. Furthermore, for module tests, the terraform_run_phase claim is always set to "plan," reflecting the read-only nature of the operation.
Implementing Workload Identity Federation in Azure DevOps
Workload Identity Federation in Azure DevOps removes the limitation that previously forced users to rely on self-hosted agents with managed identities if they wanted to avoid service principal secrets. By implementing this federation, Azure DevOps pipelines can use short-lived tokens to authenticate to Azure.
Architectural Configuration
To set up this federation, you must create a trust relationship between the Azure DevOps project and an Azure identity (typically a User Assigned Managed Identity). This is achieved by configuring Federated Credentials on the Azure identity, which tells Azure to trust tokens issued by the Azure DevOps OIDC provider if they contain specific claims.
The following Terraform configuration demonstrates how to automate the creation of this federation using the azurerm and azuredevops providers.
```hcl
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">=3.0.0"
}
azuredevops = {
source = "microsoft/azuredevops"
version = ">= 0.9.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azuredevopsproject" "example" {
name = "Example Project"
visibility = "private"
versioncontrol = "Git"
workitemtemplate = "Agile"
description = "Managed by Terraform"
}
resource "azurermresourcegroup" "identity" {
name = "identity"
location = "UK South"
}
resource "azurermuserassignedidentity" "example" {
location = azurermresourcegroup.identity.location
name = "example-identity"
resourcegroupname = azurermresource_group.identity.name
}
resource "azuredevopsserviceendpointazurerm" "example" {
projectid = azuredevopsproject.example.id
serviceendpointname = "example-federated-sc"
description = "Managed by Terraform"
serviceendpointauthentication_scheme = "WorkloadIdentityFederation"
credentials {
serviceprincipalid = azurermuserassignedidentity.example.clientid
}
azurermspntenantid = "00000000-0000-0000-0000-000000000000"
azurermsubscriptionid = "00000000-0000-0000-0000-000000000000"
azurermsubscriptionname = "Example Subscription Name"
}
```
Integrating Workload Identity with GitHub Actions
GitHub Actions can also leverage workload identity federation to interact with Azure. This eliminates the need to store ARM_CLIENT_SECRET in GitHub Secrets. Instead, the workflow requests a JWT from GitHub's OIDC provider, which is then swapped for an Azure access token.
Workflow Permissions and Environment Configuration
For this to function, the GitHub Actions workflow must explicitly request id-token: write permissions. Without this, the GitHub runner cannot request the OIDC JWT required for authentication.
The following example illustrates a complete deployment pipeline using the azure/login action and Terraform.
```yaml
.github/workflows/terraform.yml
name: Terraform Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
These permissions are required for OIDC token generation
permissions:
id-token: write # Required for requesting the OIDC JWT
contents: read # Required for actions/checkout
env:
ARMCLIENTID: ${{ secrets.AZURECLIENTID }}
ARMTENANTID: ${{ secrets.AZURETENANTID }}
ARMSUBSCRIPTIONID: ${{ secrets.AZURESUBSCRIPTIONID }}
ARMUSEOIDC: true
jobs:
terraform-plan:
name: Terraform Plan
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
# Authenticate with Azure using OIDC
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Terraform Init
run: terraform init
working-directory: infrastructure
- name: Terraform Plan
run: terraform plan -out=tfplan
working-directory: infrastructure
# Save the plan for the apply job
- name: Upload Plan
uses: actions/upload-artifact@v4
with:
name: tfplan
path: infrastructure/tfplan
terraform-apply:
name: Terraform Apply
# (Apply job configuration continues...)
```
In this configuration, the ARM_USE_OIDC: true environment variable signals the Terraform Azure provider to use the OIDC flow rather than looking for a client secret.
Security Advantages Over Traditional Authentication
The transition to Workload Identity Federation addresses several critical security gaps found in traditional Service Principal authentication.
Secret Elimination and Rotation
In a traditional setup, a developer creates a Service Principal, generates a client secret, and stores that secret in a CI/CD system. This secret is long-lived and must be rotated manually or via script, which is often overlooked. If the secret is leaked, the attacker has permanent access until the secret expires or is revoked. With Workload Identity, no client secret is ever created, stored, or rotated.
Temporal Security
The tokens used in Workload Identity are short-lived. An HCP Terraform workload identity token expires at the end of the plan or apply timeout. This significantly reduces the window of opportunity for an attacker to use a captured token.
Contextual Authorization
Traditional secrets are "bearer tokens"—anyone who has the secret has the permissions. Workload Identity allows for "contextual" permissions. A cloud administrator can write a policy that says: "Allow this identity to modify resources only if the token claims indicate it is coming from organization:my-org AND workspace:production AND the run_phase is apply."
Conclusion
Terraform Workload Identity and Workload Identity Federation represent the maturation of IaC security. By leveraging the OIDC protocol and JWTs, organizations can move away from the risky practice of storing long-lived credentials in CI/CD pipelines. Whether utilizing HCP Terraform's dynamic provider credentials to scope access by workspace and project, or implementing federation within Azure DevOps and GitHub Actions to remove service principal secrets, the result is a vastly improved security posture.
The ability to differentiate between standard workspace runs and module test runs via distinct subject claims further allows for the implementation of granular, read-only access for testing phases, ensuring that the principle of least privilege is maintained throughout the entire software development lifecycle. As emerging technologies continue to push toward "zero trust" architectures, the shift toward identity-based, short-lived authentication is no longer optional but a necessity for secure cloud operations.