HashiCorp’s AzureAD provider introduces a new resource in the 3.7 release: azuread_application_flexible_federated_identity_credential. This unlocks dynamic credentials using flexible claim matching, so you can scope access to specific runs, workspaces, or phases for terraform cloud.
What’s new
- Flexible claim matching: Match on claims like sub using expressions (e.g., by organization, project, workspace, and run phase).
- Wildcard TFC integration: Ability to wildcard within the sub.
Quick walkthrough (HCP Terraform)
The snippet below creates:
- An Azure AD application and service principal
- A subscription‑scoped role assignment
- A TFC workspace to consume the credentials
Setup Azure Application
terraform {
required_providers {
azuread = {
source = "hashicorp/azuread"
version = ">= 3.7.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.0.0"
}
tfe = {
source = "hashicorp/tfe"
version = ">= 0.53.0"
}
}
}
provider "azurerm" {
features {}
}
data "azuread_client_config" "current" {}
data "azurerm_subscription" "current" {}
resource "azuread_application" "tfc_application" {
display_name = "tfc-application"
owners = [data.azuread_client_config.current.object_id]
}
resource "azuread_service_principal" "tfc_service_principal" {
client_id = azuread_application.tfc_application.client_id
}
resource "azurerm_role_assignment" "tfc_role_assignment" {
scope = data.azurerm_subscription.current.id
principal_id = azuread_service_principal.tfc_service_principal.object_id
role_definition_name = "Contributor"
}
variable "tfe_organization" {
type = string
}
data "tfe_project" "default" {
name = "Default Project"
organization = var.tfe_organization
}
resource "tfe_workspace" "team_1" {
name = "workspace-team-1"
project_id = data.tfe_project.default.id
organization = var.tfe_organization
}Enable variables in your TFC workspace
Set the required environment variables so that runs authenticate via Workload Identity:
resource "tfe_variable" "enable_azure_provider_auth" {
workspace_id = tfe_workspace.team_1.id
key = "TFC_AZURE_PROVIDER_AUTH"
value = "true"
category = "env"
description = "Enable the Workload Identity integration for Azure."
}
resource "tfe_variable" "tfc_azure_client_id" {
workspace_id = tfe_workspace.team_1.id
key = "TFC_AZURE_RUN_CLIENT_ID"
value = azuread_application.tfc_application.id
category = "env"
description = "The Azure Client ID runs will use to authenticate."
}
# Optional: override the default audience used in tokens
resource "tfe_variable" "tfc_azure_audience" {
workspace_id = tfe_workspace.team_1.id
key = "TFC_AZURE_WORKLOAD_IDENTITY_AUDIENCE"
value = "api://AzureADTokenExchange"
category = "env"
description = "The value to use as the audience claim in run identity tokens."
}
resource "tfe_variable" "arm_subscription_id" {
workspace_id = tfe_workspace.team_1.id
key = "ARM_SUBSCRIPTION_ID"
value = data.azurerm_subscription.current.subscription_id
category = "env"
}
resource "tfe_variable" "arm_tenant_id" {
workspace_id = tfe_workspace.team_1.id
key = "ARM_TENANT_ID"
value = data.azurerm_subscription.current.tenant_id
category = "env"
}Configure single flexible credential (wildcard)
Historically, Terraform Cloud runs often required separate federated identity credentials per run_phase (for example, one for plan and another for apply). With flexible federated identity credentials, you can reduce this management overhead by using the restricted expression language to wildcard the sub claim and authorize multiple phases under a single credential using the matches operator. See Microsoft’s guidance for supported issuers and operators. Microsoft Entra flexible federated identity credentials (preview)
Before: per‑phase credentials
resource "azuread_application_federated_identity_credential" "tfc_federated_credential_plan" {
application_id = azuread_application.tfc_application.id
display_name = "tfc-federated-credential-plan"
audiences = ["api://AzureADTokenExchange"]
issuer = "https://app.terraform.io"
subject = "organization:${var.tfe_organization}:project:${data.tfe_project.default.name}:workspace:${tfe_workspace.team_1.name}:run_phase:plan"
}
resource "azuread_application_federated_identity_credential" "tfc_federated_credential_apply" {
application_id = azuread_application.tfc_application.id
display_name = "tfc-federated-credential-apply"
audiences = ["api://AzureADTokenExchange"]
issuer = "https://app.terraform.io"
subject = "organization:${var.tfe_organization}:project:${data.tfe_project.default.name}:workspace:${tfe_workspace.team_1.name}:run_phase:apply"
}After: Single Wildcarded Credential
resource "azuread_application_flexible_federated_identity_credential" "plan_and_apply" {
application_id = azuread_application.tfc_application.id
display_name = "tfc-federated-flexible-credential"
audience = "api://AzureADTokenExchange"
issuer = "https://app.terraform.io"
claims_matching_expression = "claims['sub'] matches 'organization:${var.tfe_organization}:project:${data.tfe_project.default.name}:workspace:${tfe_workspace.team_1.name}:run_phase:*'"
}