Azure DevOps: using DefaultAzureCredential in an Azure Pipeline with AzureCLI@2
I frequently build scripts that work against Azure resources using the Azure SDK for JavaScript. I use the DefaultAzureCredential
to authenticate against Azure resources - this is also available in other platforms such as .NET.
The DefaultAzureCredential
is a great way to authenticate locally; I can az login
and then run my script, safe in the knowledge that the DefaultAzureCredential
will authenticate successfully. However, how can I use the DefaultAzureCredential
in an Azure DevOps pipeline?
This post will show you how to use the DefaultAzureCredential
in an Azure DevOps pipeline, specifically by using the AzureCLI@2
task.
What is DefaultAzureCredential
?
To quote the documentation:
DefaultAzureCredential
is an opinionated, preconfigured chain of credentials. It's designed to support many environments, along with the most common authentication flows and developer tools. In graphical form, the underlying chain looks like this:
The first credential in the chain is EnvironmentCredential
, which looks for environment variables to authenticate. This means that if you set the right environment variables, you can use DefaultAzureCredential
and it will authenticate with them.
The specific environment variables that DefaultAzureCredential
looks for are:
AZURE_TENANT_ID
- The Microsoft Entra tenant (directory) ID.AZURE_CLIENT_ID
- The client (application) ID of an App Registration in the tenant.AZURE_CLIENT_SECRET
- A client secret that was generated for the App Registration.
The fifth credential in the chain is AzureCliCredential
, which uses the Azure CLI to authenticate. This means that if you have already authenticated using az login
, you can use DefaultAzureCredential
without setting any environment variables.
Why have you told me about EnvironmentCredential
and AzureCliCredential
?
Great question! When I'm developing locally, I can use DefaultAzureCredential
without thinking further about it. I run az login
and then run my script. DefaultAzureCredential
will do what I need.
You can make use of AzureCliCredential
, both locally and in an Azure DevOps pipeline, and it can be used for authentication as long as you have a service connection set up in Azure DevOps that uses the same credentials as your local az login
. Should you need it, EnvironmentCredential
is another option, and we'll demonstrate it too.
The nice thing about DefaultAzureCredential
is that it supports both approaches, so you can use it in your code without having to specifically cater for the credential type being used.
Using the AzureCLI@2
task with AzureCliCredential
To use the Azure CLI in an Azure DevOps pipeline, you can use the AzureCLI@2
task. This task allows you to run Azure CLI commands in your pipeline, given it is configured to use a service connection that has the necessary permissions to access your Azure resources.
Consider the following example pipeline YAML:
- task: AzureCLI@2
displayName: Run with AzureCliCredential
inputs:
azureSubscription: myServiceConnection # this is the name of your Azure service connection in Azure DevOps
scriptType: bash
scriptLocation: inlineScript
inlineScript: npm start # where `npm start` is your command that uses DefaultAzureCredential
The above will run the npm start
command in the context of the Azure CLI. We won't document it here, but imagine the npm start
command runs a Node.js script which makes use of DefaultAzureCredential
. The supplied service connection will authenticate using the credentials of the associated service principal. When the code runs and DefaultAzureCredential
is used, the AzureCliCredential
will be used to authenticate, as at this point the pipeline is effectively a logged user with the Azure CLI.
Using the AzureCLI@2
task with EnvironmentCredential
If, for whatever reason, you want to use EnvironmentCredential
in your Azure DevOps pipeline, you can do so by setting the necessary environment variables in the pipeline. I don't have a specific reason to do this, but you may. To achieve this, you can modify the approach as follows:
- task: AzureCLI@2
displayName: Set service principal variables
inputs:
azureSubscription: myServiceConnection # this is the name of your Azure service connection in Azure DevOps
scriptType: bash
scriptLocation: inlineScript
addSpnToEnvironment: true
inlineScript: |
echo "##vso[task.setvariable variable=AZURE_CLIENT_ID;issecret=true]${servicePrincipalId}"
echo "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]${servicePrincipalKey}"
echo "##vso[task.setvariable variable=AZURE_SUBSCRIPTION_ID;issecret=true]$(az account show --query 'id' -o tsv)"
echo "##vso[task.setvariable variable=AZURE_TENANT_ID;issecret=true]${tenantId}"
- bash: npm start
displayName: 'Run with EnvironmentCredential'
env:
# see https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/README.md#environment-variables
AZURE_CLIENT_ID: $(AZURE_CLIENT_ID)
AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET)
AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID)
AZURE_TENANT_ID: $(AZURE_TENANT_ID) # this is optional and not necessary for EnvironmentCredential to work
You can see this is a little different from the previous example. We're now using the AzureCLI@2
task to set the necessary environment variables for DefaultAzureCredential
to work. The addSpnToEnvironment
input is set to true
, which ensures that the service principal's credentials are added to the environment. We then map those credentials to the names that the DefaultAzureCredential
expects and write them to the pipeline to be used in the next task.
The second task is a simple bash task that runs npm start
, but it now has an env
section that sets the necessary environment variables for DefaultAzureCredential
to work. The environment variables are set using the variables exposed by the previous task.
Conclusion
In this post, we explored how to use DefaultAzureCredential
in an Azure DevOps pipeline, specifically when using the AzureCLI@2
task. We discussed the two main approaches: using AzureCliCredential
and EnvironmentCredential
, and how to configure the pipeline to support both.
It's worth noting that whilst my own use case is JavaScript / TypeScript, the same principles apply to other languages that support DefaultAzureCredential
, such as .NET. The key takeaway is that you can use DefaultAzureCredential
in an Azure DevOps pipeline just as easily as you can locally, allowing for a consistent development and deployment experience.