I am getting the below error while my Terraform Plan stage is running in Azure DevOps pipeline
│ Error: building account: could not acquire access token to parse claims: clientCredentialsToken: received HTTP status 400 with response: {"error":"invalid_request","error_description":"AADSTS900023: Specified tenant identifier '$(tf_var_tenant_id)' is neither a valid DNS name, nor a valid external domain. Trace ID: 36698f56-c6c2-4135-b36c-c425b7260a00 Correlation ID: 2adf5100-4998-44bd-94df-58f47a860103 Timestamp: 2025-01-09 01:00:49Z","error_codes":[900023],"timestamp":"2025-01-09 01:00:49Z","trace_id":"36698f56-c6c2-4135-b36c-c425b7260a00","correlation_id":"2adf5100-4998-44bd-94df-58f47a860103","error_uri":"https://login.microsoftonline.com/error?code=900023"}
It was working fine but suddenly this error started coming. I checked my Tenant ID and it is correct in the variable group
Any help will be appreciated
This is my Pipeline Code YAML File
# Variable 'build_type' was defined in the Variables tab
# Variable 'code_directory' was defined in the Variables tab
variables:
- group: MN-ALZTF-ADO-001
- group: MN-ALZTF-BACKEND-001
parameters:
- name: management_group
displayName: Select Management Group
type: string
default: PLATFORM
values:
- LANDINGZONES
- PLATFORM
- name: terraform_action
displayName: Select Terraform action
type: string
default: Validate
values:
- Validate
- Plan
- Apply
- Destroy
- name: project_directory
displayName: Select Project Directory
type: string
default: platform_landing_zone
values:
- platform_landing_zone
trigger: none # Disable CI triggers.
jobs:
- job: Terraform
timeoutInMinutes: 20 # 720 minutes = 12 hours // see: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#timeouts
cancelTimeoutInMinutes: 10 # how much time to give 'run always even if cancelled tasks' before stopping them
variables:
- group: MN-ALZTF-ADO-001
- group: MN-ALZTF-BACKEND-001
# - group: Infracost
displayName: "Terraform - ${{ parameters.terraform_action }} on ${{ parameters.management_group }} for ${{ parameters.project_directory }}"
# NOTE: Comment out the Azure Pipelines pool if using a self-hosted agent
pool:
# Hosted
name: Azure Pipelines
vmImage: ubuntu-latest
# Self-Hosted
#name: Self-hosted Terraform
steps:
- checkout: self # NOTE: if checking out only "self", the contents of the repo are placed in the root folder instead of inside a folder the same name as the repo unless a "path" is given
path: $(Build.Repository.Name)
- task: Bash@3
displayName: 'Debug: Bash'
inputs:
targetType: 'inline'
script: |
echo "TF_VAR_tenant_id = $(TF_VAR_tenant_id)"
echo "TF_VAR_client_id = $(TF_VAR_client_id)"
echo "ARM_TENANT_ID = $(ARM_TENANT_ID)"
echo "ARM_CLIENT_ID = $(ARM_CLIENT_ID)"
echo "ARM_SUBSCRIPTION_ID = $(ARM_SUBSCRIPTION_ID)"
echo "TF_VAR_connectivity_subscription_id = $(TF_VAR_connectivity_subscription_id)"
echo "TF_VAR_devtest_subscription_id = $(TF_VAR_devtest_subscription_id)"
echo "TF_VAR_identity_subscription_id = $(TF_VAR_identity_subscription_id)"
echo "TF_VAR_management_subscription_id = $(TF_VAR_management_subscription_id)"
echo "TF_VAR_production_subscription_id = $(TF_VAR_production_subscription_id)"
echo "terraform_action = "${{ parameters.terraform_action }}""
echo "ADO Project Name = $(System.TeamProject)"
echo "Agent.BuildDirectory: $(Agent.BuildDirectory)"
echo "Agent.HomeDirectory: $(Agent.HomeDirectory)"
echo "Agent.TempDirectory: $(Agent.TempDirectory)"
echo "Agent.WorkFolder: $(Agent.WorkFolder)"
echo "Build.ArtifactStagingDirectory: $(Build.ArtifactStagingDirectory)"
echo "Build.Repository.LocalPath: $(Build.Repository.LocalPath)"
echo "Build.SourcesDirectory: $(Build.SourcesDirectory)"
echo "Build.StagingDirectory: $(Build.StagingDirectory)"
echo "Pipeline.Workspace: $(Pipeline.Workspace)"
echo "System.DefaultWorkingDirectory: $(System.DefaultWorkingDirectory)"
echo "workingDirectory: $(System.DefaultWorkingDirectory)/${{ parameters.project_directory }}"
echo "project_directory = "${{ parameters.project_directory }}""
echo "templates_branch = "$(Build.SourceBranchName)""
echo "state_file_name = mg-${{ parameters.management_group }}.repo-$(Build.Repository.Name).branch-$(Build.SourceBranchName).template-${{ parameters.project_directory }}.tfstate"
echo "ls $(System.DefaultWorkingDirectory)"
ls "$(System.DefaultWorkingDirectory)"
echo "ls $(System.DefaultWorkingDirectory)/${{ parameters.project_directory }}"
ls "$(System.DefaultWorkingDirectory)/${{ parameters.project_directory }}"
- task: TerraformInstaller@0
displayName: Install Terraform latest
- task: TerraformTaskV3@3
displayName: 'Terraform : Init'
env:
TF_VAR_tenant_id: $(TF_VAR_tenant_id)
TF_VAR_client_secret: $(TF_VAR_client_secret)
TF_VAR_client_id: $(TF_VAR_client_id)
ARM_CLIENT_ID: $(CLIENT_ID)
ARM_CLIENT_SECRET: $(CLIENT_SECRET)
ARM_TENANT_ID: $(TENANT_ID)
ARM_SUBSCRIPTION_ID: $(SUBSCRIPTION_ID)
ARM_ACCESS_KEY: $(BACKEND_STORAGE_ACCOUNT_ACCESS_KEY)
TF_VAR_connectivity_subscription_id: $(TF_VAR_connectivity_subscription_id)
TF_VAR_devtest_subscription_id: $(TF_VAR_devtest_subscription_id)
TF_VAR_identity_subscription_id: $(TF_VAR_identity_subscription_id)
TF_VAR_management_subscription_id: $(TF_VAR_management_subscription_id)
TF_VAR_production_subscription_id: $(TF_VAR_production_subscription_id)
inputs:
provider: 'azurerm'
command: 'init'
commandOptions: "-reconfigure"
#commandOptions: "-backend-config=subscription_id=$(ARM_SUBSCRIPTION_ID)"
workingDirectory: $(System.DefaultWorkingDirectory)/${{ parameters.project_directory }}
backendServiceArm: $(backendServiceArm)
backendAzureRmResourceGroupName: $(backendAzureRmResourceGroupName)
backendAzureRmStorageAccountName: $(backendAzureRmStorageAccountName)
backendAzureRmContainerName: $(backendAzureRmContainerName)
backendAzureRmKey: "mg-${{ parameters.management_group }}.repo-$(Build.Repository.Name).branch-$(Build.SourceBranchName).template-${{ parameters.project_directory }}.tfstate"
- task: TerraformTaskV4@4
displayName: 'Terraform : Validate'
inputs:
provider: 'azurerm'
command: 'validate'
workingDirectory: $(System.DefaultWorkingDirectory)/${{ parameters.project_directory }}
backendServiceArm: $(backendServiceArm)
backendAzureRmResourceGroupName: $(backendAzureRmResourceGroupName)
backendAzureRmStorageAccountName: $(backendAzureRmStorageAccountName)
backendAzureRmContainerName: $(backendAzureRmContainerName)
backendAzureRmKey: "mg-${{ parameters.management_group }}.repo-$(Build.Repository.Name).branch-$(Build.SourceBranchName).template-${{ parameters.project_directory }}.tfstate"
# - task: InfracostSetup@1
# displayName: 'Setup Infracost'
# condition: and(succeeded(), eq('${{ parameters.terraform_action }}', 'Plan'))
# inputs:
# apiKey: $(INFRACOST_API_KEY)
# - task: Bash@3
# displayName: 'Run Infracost'
# condition: and(succeeded(), eq('${{ parameters.terraform_action }}', 'Plan'))
# inputs:
# targetType: 'inline'
# script: |
# echo '[INFRACOST]'
# infracost breakdown --path="$(System.DefaultWorkingDirectory)/${{ parameters.project_directory }}" --format=table
# echo ''
- task: CmdLine@2
displayName: 'Terraform: Plan'
condition: and(succeeded(), eq('${{ parameters.terraform_action }}', 'Plan'))
env:
TF_VAR_tenant_id: $(TF_VAR_tenant_id)
TF_VAR_client_secret: $(TF_VAR_client_secret)
TF_VAR_client_id: $(TF_VAR_client_id)
ARM_CLIENT_ID: $(CLIENT_ID)
ARM_CLIENT_SECRET: $(CLIENT_SECRET)
ARM_TENANT_ID: $(TENANT_ID)
ARM_SUBSCRIPTION_ID: $(MANAGEMENT_SUBSCRIPTION_ID)
ARM_ACCESS_KEY: $(BACKEND_STORAGE_ACCOUNT_ACCESS_KEY)
TF_VAR_connectivity_subscription_id: $(TF_VAR_connectivity_subscription_id)
TF_VAR_devtest_subscription_id: $(TF_VAR_devtest_subscription_id)
TF_VAR_identity_subscription_id: $(TF_VAR_identity_subscription_id)
TF_VAR_management_subscription_id: $(TF_VAR_management_subscription_id)
TF_VAR_production_subscription_id: $(TF_VAR_production_subscription_id)
TF_VAR_vm_admin_password: $(TF_VAR_vm_admin_password)
inputs:
script: |
cd $(System.DefaultWorkingDirectory)/${{ parameters.project_directory }}
terraform plan --var-file=centralus.tfvars -input=false
- task: CmdLine@2
displayName: 'Terraform: Apply'
condition: and(succeeded(), eq('${{ parameters.terraform_action }}', 'Apply'))
env:
TF_VAR_tenant_id: $(TF_VAR_tenant_id)
TF_VAR_client_secret: $(TF_VAR_client_secret)
TF_VAR_client_id: $(TF_VAR_client_id)
ARM_CLIENT_ID: $(CLIENT_ID)
ARM_CLIENT_SECRET: $(CLIENT_SECRET)
ARM_TENANT_ID: $(TENANT_ID)
ARM_SUBSCRIPTION_ID: $(MANAGEMENT_SUBSCRIPTION_ID)
ARM_ACCESS_KEY: $(BACKEND_STORAGE_ACCOUNT_ACCESS_KEY)
TF_VAR_connectivity_subscription_id: $(TF_VAR_connectivity_subscription_id)
TF_VAR_devtest_subscription_id: $(TF_VAR_devtest_subscription_id)
TF_VAR_identity_subscription_id: $(TF_VAR_identity_subscription_id)
TF_VAR_management_subscription_id: $(TF_VAR_management_subscription_id)
TF_VAR_production_subscription_id: $(TF_VAR_production_subscription_id)
inputs:
script: |
cd $(System.DefaultWorkingDirectory)/${{ parameters.project_directory }}
terraform apply -auto-approve
- task: CmdLine@2
displayName: 'Terraform: Destroy'
condition: and(succeeded(), eq('${{ parameters.terraform_action }}', 'Destroy'))
env:
TF_VAR_tenant_id: $(TF_VAR_tenant_id)
TF_VAR_client_secret: $(TF_VAR_client_secret)
TF_VAR_client_id: $(TF_VAR_client_id)
ARM_CLIENT_ID: $(CLIENT_ID)
ARM_CLIENT_SECRET: $(CLIENT_SECRET)
ARM_TENANT_ID: $(TENANT_ID)
ARM_SUBSCRIPTION_ID: $(MANAGEMENT_SUBSCRIPTION_ID)
ARM_ACCESS_KEY: $(BACKEND_STORAGE_ACCOUNT_ACCESS_KEY)
TF_VAR_connectivity_subscription_id: $(TF_VAR_connectivity_subscription_id)
TF_VAR_devtest_subscription_id: $(TF_VAR_devtest_subscription_id)
TF_VAR_identity_subscription_id: $(TF_VAR_identity_subscription_id)
TF_VAR_management_subscription_id: $(TF_VAR_management_subscription_id)
TF_VAR_production_subscription_id: $(TF_VAR_production_subscription_id)
inputs:
script: |
cd $(System.DefaultWorkingDirectory)/${{ parameters.project_directory }}
terraform destroy -auto-approve
...
Variables in Azure pipelines are automatically converted to environment variables in script steps. They do get transformed (all upper case and unsupported characters are converted to _
). This is true for all variables, except for secrets.
I suspect you can reduce the env:
blocks considerably because of that. I also suspect that the uppercasing of variables might be why your variables aren't being translated as you expect:
- task: TerraformTaskV3@3
displayName: 'Terraform : Init'
env:
# It might make sense to uppercase the variable below to match Azure Pipeline's behavior
TF_VAR_CLIENT_SECRET: $(TF_VAR_client_secret)
ARM_CLIENT_SECRET: $(CLIENT_SECRET)
ARM_ACCESS_KEY: $(BACKEND_STORAGE_ACCOUNT_ACCESS_KEY)
# These can probably be removed
# TF_VAR_connectivity_subscription_id: $(TF_VAR_connectivity_subscription_id)
# TF_VAR_devtest_subscription_id: $(TF_VAR_devtest_subscription_id)
# TF_VAR_identity_subscription_id: $(TF_VAR_identity_subscription_id)
# TF_VAR_management_subscription_id: $(TF_VAR_management_subscription_id)
# TF_VAR_production_subscription_id: $(TF_VAR_production_subscription_id)