Azure DevOps supports Workload Identity federation (powered by OpenID Connect) to authenticate to Azure and Azure DevOps from your pipelines for deploying Terraform configurations. Workload Identity federation

To follow along with this tutorial, you will need:

  • Access to an Azure DevOps project
  • A service connection using Workload Identity (link)
  • Access to an Azure subscription

To follow along with more code examples, check out this GitHub repository:
JeffBrownTech/azure-devops-terraform-oidc-examples

What is Workload Identity federation?

The legacy method for authenticating to Azure from your pipeline was service principals with client secrets. A disadvantage of the service principal method was the client secret rotation due to expiration dates, and you had to store the client secret somewhere for your pipeline to use for authentication.

Workload Identity federation uses OpenID Connect (OIDC) to create short-lived tokens for authenticating to Azure and Azure DevOps. This method increases security posture and removes the requirement to manage secrets.

You create a service connection between your Azure DevOps organization and your Azure tenant using a service principal or user-assigned managed identity. Azure DevOps provides a token to Azure that is used to authenticate to the Azure API.

Read More: Azure DevOps Service Connection Configuration Guide

Many Azure DevOps tasks, such as TerraformTask and Azure CLI, now support Workload Identity federation. The following sections outline how to Workload Identity your Azure DevOps pipelines to deploy resources managed by Terraform to both Azure and Azure DevOps. The AzureRM Terraform provider version 3.7.0 and higher supports OIDC, while the AzureDevOps Terraform provider provides support starting in version 1.0.0.

Terraform Configuration Example Code

Here is an example Terraform configuration (main.tf) that uses AzureRM and AzureDevOps providers. The configuration creates a resource group named terraformtaskdemo-rg and an Azure DevOps project named TerraformTask Example.

Note that in the azuredevops provider block you need to add use_oidc = true for the provider to authenticate with OIDC. You will also need to update the org_service_url with your Azure DevOps organization name.

Using TerraformTask with Terraform Deployments

The Terraform extension by Microsoft DevLabs (TerraformTask) enables DevOps engineers to initialize, validate, plan, and apply Terraform configurations. TerraformTask relies on service connections to connect to external environments and can use remote backends in Azure, AWS, and GCP.

As mentioned above, TerraformTask natively supports OIDC. In fact, if you are already using TerraformTask in your pipelines, you can update your existing service connection to use Workload Identity federation and not have to make any other changes to your pipeline. TerraformTask knows how to authenticate to Azure through the Service Connection and enable the environment variables needed for OIDC authentication.

Below is an example of a multi-stage pipeline using TerraformTask with a service connection configured for Workload Identity federation. Note the use of ARM_USE_AZUREAD: true as an environment variable on several tasks. This setting informs Terraform to use Azure AD (or Entra ID) authentication to the storage account to read and write the state file. The service principal or managed identity used in the service connection requires a blob data role, such as Storage Blob Data Contributor or Storage Blob Data Owner, meaning you can disable access keys on the storage account as a security measure.

Note: In the example code below, be sure to replace any entries of <…> with your environment information.

Using Command Line with Terraform Deployments

You can also use the command line to perform Terraform deployments within a pipeline as an alternative to TerraformTask. Many people suggest using the command line as an alternative to pipeline tasks, as pipeline tasks introduce another layer of abstraction. You can also take advantage of unique situations that the task cannot handle. Let’s take a look at how to use Workload Identity in this scenario.

Read More: Terraform on Azure Pipelines Best Practices | Julie Ng

Like the TerraformTask, the AzureCLI task supports Workload Identity federation when the service connection is configured to use that authentication method. You can then access the workload identity token by setting addSpnToEnvironment to true, which adds the token value to the task execution environment.

To authenticate using OIDC from Terraform, you need to populate several environment variables, including:

  • ARM_CLIENT_ID
  • ARM_SUBSCRIPTION_ID
  • ARM_TENANT_ID
  • ARM_OIDC_TOKEN
  • ARM_USE_OIDC

When configuring the AzureCLI tasks, you can use a script file or include an inline script to set pipeline variables with these values. The following sections document both of these steps.

InlineScript

When configuring the AzureCLI task, you can include executable commands directly within the task using an inline script. The Terraform documentation (link) outlines the following inline script to use to set the necessary pipeline variables to use OIDC with Terraform. The example below is for use in Azure DevOps pipelines.

- task: AzureCLI@2
  name: set_variables
  displayName: set terraform credentials
  inputs:
    azureSubscription: '<service-connection-name>'
    addSpnToEnvironment: true
    scriptType: pscore
    scriptLocation: inlineScript
    inlineScript: |
      Write-Host "##vso[task.setvariable variable=ARM_USE_OIDC]true"
      Write-Host "##vso[task.setvariable variable=ARM_USE_AZUREAD]true"
      Write-Host "##vso[task.setvariable variable=ARM_OIDC_TOKEN]$env:idToken"
      Write-Host "##vso[task.setvariable variable=ARM_CLIENT_ID]$env:servicePrincipalId"
      Write-Host "##vso[task.setvariable variable=ARM_SUBSCRIPTION_ID]$(az account show --query id -o tsv)"
      Write-Host "##vso[task.setvariable variable=ARM_TENANT_ID]$env:tenantId"

Script file

An alternative is to use a PowerShell script to set these variables. The example script below is a bit more robust in that it verifies if the AzureCLI task authenticated to Azure using a service principal and if ARM_CLIENT_SECRET and ARM_OIDC_TOKEN are present. If these components are not found, the script errors out and will stop the pipeline from continuing to run.

Note: The example script below is a modified version of the script found here.

Below is an example Azure DevOps multi-stage pipeline utilizing an AzureCLI task and calling the script to set the pipeline variables. Note that you will need to execute the script for each new job in the pipeline to export the necessary values for Terraform execution.

For more examples, check out this GitHub repository:
JeffBrownTech/azure-devops-terraform-oidc-examples

Summary

Workload Identity federation is a great new feature of Azure DevOps service connections for securing your pipelines and authenticating your Terraform deployments. While many pipeline tasks and steps now support it, you can also take advantage of exporting the OIDC token for use in custom script steps.