azureazure-devopsterraformopenid-connectworkflow-identity-federation

Azure DevOps Pipelines: TerraformTaskV4: init with different subscription and Workflow Identity Federation


I have a Azure DevOps YAML pipeline and want to deploy resources with Terraform to Azure. As first step I need to run terraform init. For that I want to use TerraformTaskV4 (https://github.com/microsoft/azure-pipelines-terraform/blob/main/Tasks/TerraformTask/TerraformTaskV4/README.md):

- task: TerraformTaskV4@4
  displayName: Initialize Terraform
  inputs:
    provider: 'azurerm'
    command: 'init'
    backendServiceArm: 'your-backend-service-connection'
    backendAzureRmResourceGroupName: 'your-rg-name'
    backendAzureRmStorageAccountName: 'your-stg-name'
    backendAzureRmContainerName: 'your-container-name'
    backendAzureRmKey: 'state.tfstate'

But there are two challenges:

  1. I want to use another Azure subscription than related to the service connection. The service principal of the service connection has access to multiple subscriptions
  2. I am using a service connection with a service principal leveraging Workflow Identity Federation

How can I run terraform init accordingly?


Solution

    1. To my knowledge it is not possible to select the subscription with TerraformTaskV4@4. It always takes the subscription related to the service connection. Therefore I switched to using AzureCLI@2 (https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/azure-cli-v2?view=azure-pipelines)
    2. In a first step you need to have access to the service principal (via addSpnToEnvironment) in the AzureCLI@2 task and add the environment variables (https://developer.hashicorp.com/terraform/language/backend/azurerm#backend-azure-ad-service-principal-or-user-assigned-managed-identity-via-oidc-workload-identity-federation) to the environment. This has to happen in a first and separate task. Then you can create a subsequent task (in the same job) for e.g. terraform init

    Example:

    ...
    
    - task: AzureCLI@2
      displayName: "Set Environment Variables"
      inputs:
        azureSubscription: "${{ variables.service_connection_name }}"
        addSpnToEnvironment: true
        scriptType: pscore
        scriptLocation: inlineScript
        workingDirectory: $(System.DefaultWorkingDirectory)/terraform
        inlineScript: |
          Write-Host "##vso[task.setvariable variable=ARM_USE_OIDC]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]$(subscriptionId)"
          Write-Host "##vso[task.setvariable variable=ARM_TENANT_ID]$env:tenantId"
        
    - task: TerraformInstaller@1
      displayName: Install Terraform
      inputs:
        terraformVersion: ${{ variables.terraform_version }}
    
    - task: AzureCLI@2
      displayName: "Terraform init"
      inputs:
        azureSubscription: "${{ variables.service_connection_name }}"
        addSpnToEnvironment: true
        scriptType: pscore
        scriptLocation: inlineScript
        workingDirectory: $(System.DefaultWorkingDirectory)/terraform
        inlineScript: |
          terraform init -backend-config="resource_group_name=$(terraformBackendResourceGroupName)" -backend-config="storage_account_name=$(terraformBackendStorageAccountName)" -backend-config="container_name=$(terraformBackendContainerName)" -backend-config="key=$(terraformBackendKey)"
    
    ...