If you set up workload identity integration between HCP Terraform and Azure today, you need to configure the subject to match a specific HCP Terraform organization, project, workspace and run phase (plan or apply).
This differs from the corresponding setup on AWS. On AWS you can use a subject that includes a wildcard *
. This allows you to use the same configuration for both plan and apply operations for a given organization/project/workspace. You can even use the same configuration for multiple workspaces.
Fortunately, there is a new preview feature on Azure that enables this same behavior. In this blog post I will briefly outline the steps to set this up.
Set up required resources#
In the following sections we’ll briefly see how to set up demo resources on Azure and HCP Terraform to test this.
Providers, variables, outputs#
Configure the required providers:
terraform {
required_providers {
azuread = {
source = "hashicorp/azuread"
version = "3.1.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "4.16.0"
}
tfe = {
source = "hashicorp/tfe"
version = "0.62.0"
}
}
}
provider "azurerm" {
features {}
subscription_id = var.azure_subscription_id
}
provider "tfe" {
organization = var.hcp_terraform_organization
}
Here we are using implicit provider authentication using CLI credentials for Azure and HCP Terraform.
The provider
blocks reference the following variables:
variable "azure_subscription_id" {
type = string
description = "Azure subscription ID"
}
variable "hcp_terraform_organization" {
type = string
description = "HCP Terraform organization name"
}
Later when we set up the new flexible federated identity credentials we will need a few output values. The corresponding resources are set up in a later section.
output "organization" {
value = var.hcp_terraform_organization
}
output "project" {
value = tfe_project.default.name
}
output "workspace" {
value = tfe_workspace.default.name
}
output "object_id" {
value = azuread_application.hcp_terraform.object_id
}
Resources on Azure and Entra ID#
There is no current support to configure this new type of federated identity credentials using the Terraform provider for Azure or Entra ID. Your only options are to use the Azure portal, or the Graph API.
You can create most of the required resources using Terraform. We need an app registration with a corresponding service principal, and we need to give the service principal permissions for our Azure subscription to allow it to provision resources:
resource "azuread_application" "hcp_terraform" {
display_name = "hcp-terraform-workspace"
}
resource "azuread_service_principal" "hcp_terraform" {
client_id = azuread_application.hcp_terraform.client_id
}
data "azurerm_subscription" "current" {}
resource "azurerm_role_assignment" "contributor" {
scope = data.azurerm_subscription.current.id
principal_id = azuread_service_principal.hcp_terraform.object_id
role_definition_name = "Contributor"
}
Resources on HCP Terraform#
On the HCP Terraform side, we set up a new project with a single workspace:
data "tfe_organization" "current" {}
resource "tfe_project" "default" {
name = "mattias-engineer-demo"
}
resource "tfe_workspace" "default" {
name = "workspace-team-1"
project_id = tfe_project.default.id
}
To tell HCP Terraform to use a workload identity federation for plan and apply operations we must also configure a few environment variables that are applied in this new workspace. We use a variable set and configure four environment variables:
resource "tfe_variable_set" "azure" {
name = "AZURE"
}
resource "tfe_variable" "tfc_azure_run_client_id" {
key = "TFC_AZURE_RUN_CLIENT_ID"
value = azuread_service_principal.hcp_terraform.client_id
category = "env"
variable_set_id = tfe_variable_set.azure.id
}
resource "tfe_variable" "tfc_azure_provider_auth" {
key = "TFC_AZURE_PROVIDER_AUTH"
value = "true"
category = "env"
variable_set_id = tfe_variable_set.azure.id
}
resource "tfe_variable" "arm_subscription_id" {
key = "ARM_SUBSCRIPTION_ID"
value = data.azurerm_subscription.current.subscription_id
category = "env"
variable_set_id = tfe_variable_set.azure.id
}
resource "tfe_variable" "arm_tenant_id" {
key = "ARM_TENANT_ID"
value = data.azurerm_subscription.current.tenant_id
category = "env"
variable_set_id = tfe_variable_set.azure.id
}
resource "tfe_workspace_variable_set" "azure" {
variable_set_id = tfe_variable_set.azure.id
workspace_id = tfe_workspace.default.id
}
The environment variables are:
TFC_AZURE_RUN_CLIENT_ID
which is the service principal client ID that should be used to provision resources on Azure.TFC_AZURE_PROVIDER_AUTH
is set totrue
. This tells HCP Terraform to use workload identity federation.ARM_SUBSCRIPTION_ID
is the Azure subscription ID for the provider configuration. This could be set in theprovider
block instead.ARM_TENANT_ID
is the Azure tenant ID for the provider configuration. This could also be set in theprovider
block directly.
Set up flexible federated identity credentials#
After applying the Terraform configuration outlined above, we are ready to set up the flexible federated identity credentials.
We use the following bash script to set this up:
#!/bin/bash
organization="$(terraform output -raw organization)"
project="$(terraform output -raw project)"
workspace="$(terraform output -raw workspace)"
objectId="$(terraform output -raw object_id)"
sub="organization:$organization:project:$project:workspace:$workspace:run_phase:*"
cat <<EOF > payload.json
{
"audiences": [
"api://AzureADTokenExchange"
],
"name": "hcp-terraform-flexible-credential",
"issuer": "https://app.terraform.io",
"claimsMatchingExpression": {
"value": "claims['sub'] matches '$sub'",
"languageVersion": 1
}
}
EOF
az rest \
--method post \
--url "https://graph.microsoft.com/beta/applications/$objectId/federatedIdentityCredentials" \
--body @payload.json
rm payload.json
We build the subject as a string with the trailing *
representing both plan and apply operations:
organization:$organization:project:$project:workspace:$workspace:run_phase:*
The subject claims matching is created using the expression in the JSON payload:
claims['sub'] matches '$sub'
Conclusions#
After setting all of this up you can use workload identity federation from the newly created workspace on HCP Terraform.
This feature is in preview (on Azure), but hopefully it will be in GA soon and be supported using the Terraform provider as well.
The benefit is that you can skip setting up two federated credentials for each workspace (one for plan and one for apply operations). With this new feature you could even use the same federated credential for multiple workspaces or projects. However, it is a best practice to not use the same identity for multiple purposes - but your specific circumstances will determine what is best.