[HOWTO] Create Azure DevOps Service Connections with authentication method Workload Identity Federation using Terraform

Since fall 2023, it has been possible to use Workload Identity Federation for Azure service connections (public preview started in September 2023). Besides a short introduction to workload identity federation, this post shows how to automatically create service connections with authentication method Workload Identity Federation using terraform.

Let’s dive right into the topic.

What is Workload Identity Federation

In supported scenarios, workload identity federation allows you to access Microsoft Entra protected resources without having to care about secret management. It’s an OpenID Connect (OIDC) implementation which in context of Azure DevOps allows you to use short-lived credential free authentication to Azure.

Why should I use it?

To avoid the following error message and because it’s the recommended authentication method.

[error]Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired. For more information refer https://aka.ms/azureappservicedeploytsg

How does it work?

It’s about creating a trust relationship between an external Identity Provider (IdP) and a user-assigned managed identity or application (app registration) in Microsoft Entra ID.

A federated identity credential can either be configured on a user-assigned managed identity or on an app registration. The federated identity credential is used to specify which token from the external IdP should be trusted by your application or managed identity.

For more details see here.

Restrictions / Limitations

In context of Azure DevOps service connections be aware of the following restrictions / limitations.

  • Not all pipeline tasks support authentication method Workload Identity Federation
    i.e. Synapse workspace deployment@2 doesn’t
  • Azure DevOps extensions that changed the owner before authentication method Workload Identity Federation got generally available may need to be replaced
    i.e. Azure Pipelines Terraform Tasks

If you face any problems, see Troubleshoot an Azure Resource Manager workload identity service connection

Existing Azure DevOps Service Connections

Existing Azure DevOps Service connections of type Azure Resource Manager can easily be converted to use authentication method Workload Identity Federation.

Create Azure DevOps Service Connections using terraform

Instead of creating service connections in Azure DevOps manually by clicking around in wizards, they can also be created automatically using terraform. The following example uses different terraform providers to create a private Azure DevOps project ExampleProject with Git version control, an app registration with name ExampleAppReg and federated identity credential in Azure and a service connection with authentication method Workload Identity Federation in Az DevOps.

terraform {
  required_version = "~> 1.6.6"
  required_providers {
    azuread = {
      source  = "hashicorp/azuread"
      version = "~> 2.48.0"
    }
    azuredevops = {
      source  = "microsoft/azuredevops"
      version = ">= 1.0.1"
    }
  }
}


variable "organization_url" {
  type        = string
  description = "URL of Azure DevOps organization"
}
variable "personal_access_token" {
  type        = string
  description = "Azure DevOps personal access token with scopes Project and Team 'Read, write & manage'; Code 'Read, write & manage'; Service Connections 'Read, query, & manage'"
}
variable "subscription_name" {
  type        = string
  description = "Azure subscription name"
}
variable "subscription_id" {
  type        = string
  description = "Azure subscription ID"
}
variable "tenant_id" {
  type        = string
  description = "Microsoft Entra ID tenant ID"
}


provider "azuread" {
  tenant_id = var.tenant_id
}

# IMPORTANT NOTE: try to use service principal authentication for azuredevops provider instead of Personal Access Token (PAT)!
# If you still use PAT authentication, make sure you don't hardcode the PAT and don't commit it to your source code repository!
provider "azuredevops" {
  org_service_url       = var.organization_url
  personal_access_token = var.personal_access_token
}


resource "azuredevops_project" "example_project" {
  name               = "ExampleProject"
  visibility         = "private"
  version_control    = "Git"
  work_item_template = "Agile"
  description        = "Managed by Terraform"
}

resource "azuread_application" "example_appreg" {
  display_name = "ExampleAppReg"
}

resource "azuredevops_serviceendpoint_azurerm" "example_service_connection" {
  project_id                             = azuredevops_project.example_project.id
  service_endpoint_name                  = "example-federated-sc"
  description                            = "Managed by Terraform"
  service_endpoint_authentication_scheme = "WorkloadIdentityFederation"
  credentials {
    serviceprincipalid = azuread_application.example_appreg.client_id
  }
  azurerm_spn_tenantid      = var.tenant_id
  azurerm_subscription_id   = var.subscription_id
  azurerm_subscription_name = var.subscription_name
}

resource "azuread_application_federated_identity_credential" "example_federated_identity_cred" {
  display_name   = "example-federated-identity-credential"
  application_id = azuread_application.example_appreg.id
  audiences      = ["api://AzureADTokenExchange"]
  issuer         = azuredevops_serviceendpoint_azurerm.example_service_connection.workload_identity_federation_issuer
  subject        = azuredevops_serviceendpoint_azurerm.example_service_connection.workload_identity_federation_subject
}

Inspired by How to configure Workload identity federation using the Azure DevOps Terraform provider

The above example terraform code can be executed as follows.

terraform init

terraform plan -var tenant_id="TENANT_ID" -var subscription_id="SUBSCRIPTION_ID" -var subscription_name="SUBSCRIPTION_NAME" -var organization_url="https://dev.azure.com/ORGANIZATION_NAME/" -var personal_access_token="PERSONAL_ACCESS_TOKEN"

terraform apply -var tenant_id="TENANT_ID" -var subscription_id="SUBSCRIPTION_ID" -var subscription_name="SUBSCRIPTION_NAME" -var organization_url="https://dev.azure.com/ORGANIZATION_NAME/" -var personal_access_token="PERSONAL_ACCESS_TOKEN"

The properly modularized source code can be found on GitHub.

One thought on “[HOWTO] Create Azure DevOps Service Connections with authentication method Workload Identity Federation using Terraform

Add yours

Leave a Reply

Powered by WordPress.com.

Up ↑

Discover more from blog.rufer.be

Subscribe now to keep reading and get access to the full archive.

Continue reading