This document will show you how to configure an Azure DevOps pipeline that uses the Azure Developer CLI. This logic will eventually be integrated into the azd pipeline config
command.
You will find a default Azure DevOps pipeline file in ./.azdo/pipelines/azure-dev.yml
. It will provision your Azure resources and deploy your code upon pushes and pull requests.
You are welcome to use that file as-is or modify it to suit your needs.
The following doc has many bash
commands that need to be executed to get this setup. You can do them one-by-one, or you can scroll to the bottom of this page to copy and paste them all.
Your pipeline will need to know which Azure Developer environment and location you are targeting. Update the values below and run the following commands to set those values to be used later in this doc.
export AZURE_ENV_NAME=
export AZURE_LOCATION=
AZURE_ENV_NAME: This is the name of the environment you want your pipeline to work with. You can find this in the .azure folder.
AZURE_LOCATION: This is the location you used to deploy your azure developer environment to. You can find this in the .env file in the .azure folder for your environment.
Your pipeline will also need to know what subscription you are targeting. Let's configure those environment variables now.
- Run the following command to get the values needed:
az account show
- Copy and paste those values after the '=' and run those commands:
export AZURE_SUBSCRIPTION_NAME=
export AZURE_SUBSCRIPTION_ID=
export AZURE_TENANT_ID=
You'll need to create or use an existing Service Principal to manipulate Azure resources from the Azure DevOps pipeline.
If you have an existing Service Principal you'd like to use then you can skip creating one now and just set those environment variable values in the next step.
- Run the following command to create a new service principal
az ad sp create-for-rbac --role Contributor --scopes /subscriptions/${AZURE_SUBSCRIPTION_ID}
- Using the output from the
create-for-rbac
command above or your existing service principal, copy the values to the following environment variables and execute the commands to set them.
export AZURE_SERVICE_PRINCIPAL_ID=
export AZURE_DEVOPS_EXT_AZURE_RM_SERVICE_PRINCIPAL_KEY=
AZURE_SERVICE_PRINCIPAL_ID: The ID of the service principal you just created or existing one that you want to use. It will be the
appId
that is returned from thecreate-for-rbac
command.
AZURE_DEVOPS_EXT_AZURE_RM_SERVICE_PRINCIPAL_KEY: The service principal password. It will be the
password
that is returned from thecreate-for-rbac
command.
To run a pipeline in Azure DevOps, you'll first need an Azure DevOps organization. You must create an organization using the Azure DevOps portal here: https://dev.azure.com.
Once you have that organization, copy and paste it below, then run the commands to set those environment variables.
export AZURE_DEVOPS_ORG_NAME=
export AZURE_DEVOPS_ORG_URI=https://dev.azure.com/${AZURE_DEVOPS_ORG_NAME}
AZURE_DEVOPS_ORG_NAME: The name of the Azure DevOps organization that you just created or existing one that you want to use.
To run a pipeline in Azure DevOps, you're organization will need a project and a repo.
First, set the environment variable with the name of the project you want to create or use:
You don't have to change this value if you want to use the same name as your env name.
export AZURE_DEVOPS_PROJECT_NAME=${AZURE_ENV_NAME}
AZURE_DEVOPS_PROJECT_NAME: The name of the Azure DevOps project that you want to create or use an existing one.
Then, if you want to create a new project, you can do so with the following command:
az devops project create --name ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI}
If you created the Project with the CLI command above then it has also created a new repo with the same name as your project. You can use that repo for this pipeline, or you can select a different one.
If you want to use the repo that was created, then run this command.
export AZURE_DEVOPS_REPO_NAME=${AZURE_DEVOPS_PROJECT_NAME}
If you want to use a different repo, then input that repo name below and run that command:
export AZURE_DEVOPS_REPO_NAME=
AZURE_DEVOPS_REPO_NAME: The name of the Azure DevOps repo that you want to use, if you don't want to use the default.
NOTE: You can get repo info using the following command:
az repos show --repository ${AZURE_DEVOPS_REPO_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI}
Now that we have the Azure DevOps org, project, and repo setup, we can push the code to that repo.
Let's get the remote url into an environment variable:
export BRANCH_NAME=main
export AZURE_REPO_REMOTE_URL=$(az repos show --repository ${AZURE_DEVOPS_REPO_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --query remoteUrl -o tsv)
Initialize the current directory as a git repo (if not already done):
This script assumes your default branch is 'main'. You can set your default branch with this command:
git config --global init.defaultBranch main
and see what your current default branch is with this command:git config --list
.
git init
git checkout -b ${BRANCH_NAME}
git add .
git commit -m "init"
Then add that as a remote:
git remote add ${AZURE_DEVOPS_REPO_NAME} ${AZURE_REPO_REMOTE_URL}
Then push the code:
git push -u ${AZURE_DEVOPS_REPO_NAME} ${BRANCH_NAME}
You may get prompted to enter a password to Azure DevOps here. Go to https://dev.azure.com and get a personal access token and then paste it into the console.
Azure DevOps uses a "Service Connection" to communicate with Azure from an Azure DevOps pipeline.
First set the connection name by running the following command:
export AZURE_DEVOPS_SERVICE_CONNECTION_NAME=azconnection
If you change
AZURE_DEVOPS_SERVICE_CONNECTION_NAME
to something other thanazconnection
, then you also need to update theazureSubscription
value in./.azdo/pipelines/azure-dev.yml
to match that new name.
Now, let's create the service connection:
az devops service-endpoint azurerm create --azure-rm-service-principal-id ${AZURE_SERVICE_PRINCIPAL_ID} --azure-rm-subscription-id ${AZURE_SUBSCRIPTION_ID} --azure-rm-subscription-name ${AZURE_SUBSCRIPTION_NAME} --azure-rm-tenant-id ${AZURE_TENANT_ID} --name ${AZURE_DEVOPS_SERVICE_CONNECTION_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI}
If you didn't set the AZURE_DEVOPS_EXT_AZURE_RM_SERVICE_PRINCIPAL_KEY
environment variable to your Service Principal's password, then you'll be prompted to enter that password.
Next, we need to update the service connection to allow connections from any pipeline. We can do so with the following command:
export AZURE_DEVOPS_SERVICE_CONNECTION_ID=$(az devops service-endpoint list --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --query "[?contains(name, '${AZURE_DEVOPS_SERVICE_CONNECTION_NAME}')].id" -o tsv)
az devops service-endpoint update --id ${AZURE_DEVOPS_SERVICE_CONNECTION_ID} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --enable-for-all true
Now we are ready to create the pipeline in Azure DevOps.
Let's first set the pipeline name environment variable. You can change this value if you'd like to.
export AZURE_DEVOPS_PIPELINE_NAME=azdpipeline
Now let's create the pipeline by running the following command:
az pipelines create --name ${AZURE_DEVOPS_PIPELINE_NAME} --branch ${BRANCH_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --repository ${AZURE_DEVOPS_REPO_NAME} --repository-type tfsgit --yml-path "./.azdo/pipelines/azure-dev.yml" --service-connection ${AZURE_DEVOPS_SERVICE_CONNECTION_NAME} --skip-first-run
Run the following commands to set the environment variables in the Azure DevOps pipeline needed to run the pipeline and target the appropriate environment.
az pipelines variable create --name AZURE_SUBSCRIPTION_ID --org ${AZURE_DEVOPS_ORG_URI} --pipeline-name ${AZURE_DEVOPS_PIPELINE_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --value ${AZURE_SUBSCRIPTION_ID} --allow-override true
az pipelines variable create --name AZURE_LOCATION --org ${AZURE_DEVOPS_ORG_URI} --pipeline-name ${AZURE_DEVOPS_PIPELINE_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --value ${AZURE_LOCATION} --allow-override true
az pipelines variable create --name AZURE_ENV_NAME --org ${AZURE_DEVOPS_ORG_URI} --pipeline-name ${AZURE_DEVOPS_PIPELINE_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --value ${AZURE_ENV_NAME} --allow-override true
If you would like the pipeline to run every time a PR is created, then you'll need to create a build policy to do so.
Run these commands to set the environment variables needed for the next command:
export AZURE_DEVOPS_REPO_ID=$(az repos show --repository ${AZURE_DEVOPS_REPO_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --query id -o tsv)
export AZURE_DEVOPS_PIPELINE_BUILD_DEFINITION_ID=$(az pipelines build definition show --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --name ${AZURE_DEVOPS_PIPELINE_NAME} --query id)
Run this command to create the policy:
az repos policy build create --blocking true --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --branch ${BRANCH_NAME} --repository-id ${AZURE_DEVOPS_REPO_ID} --enabled true --build-definition-id ${AZURE_DEVOPS_PIPELINE_BUILD_DEFINITION_ID} --queue-on-source-update-only false --manual-queue-only false --display-name "PR Build Policy" --valid-duration 0
That is everything you need to have in place to get the Azure DevOps pipeline running. You can verify that it is working by going to the Azure DevOps portal (https://dev.azure.com) and finding the pipeline you just created.
You can copy and paste the following into a text editor so you can easily fill in the env var values.
# AZURE DEV ENV VARS
# Get the following values from .azure folder
export AZURE_ENV_NAME=
export AZURE_LOCATION=
# AZURE ENV VARS
# Run this:
az account show
# Put values here:
export AZURE_SUBSCRIPTION_NAME=
export AZURE_SUBSCRIPTION_ID=
export AZURE_TENANT_ID=
# SERVICE PRINCIPAL
# Run this to create SP
az ad sp create-for-rbac --role Contributor --scopes /subscriptions/${AZURE_SUBSCRIPTION_ID}
# Save the above output just in case you need it later
# Put values here:
export AZURE_SERVICE_PRINCIPAL_ID=
export AZURE_DEVOPS_EXT_AZURE_RM_SERVICE_PRINCIPAL_KEY=
# AZURE DEVOPS ORG
export AZURE_DEVOPS_ORG_NAME=
# You don't need to update the following line:
export AZURE_DEVOPS_ORG_URI=https://dev.azure.com/${AZURE_DEVOPS_ORG_NAME}
# AZURE DEVOPS PROJECT
# You can change the AZURE_DEVOPS_PROJECT_NAME value if you don't want it to be the same as your env name
export AZURE_DEVOPS_PROJECT_NAME=${AZURE_ENV_NAME}
az devops project create --name ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI}
# AZURE DEVOPS REPO
export AZURE_DEVOPS_REPO_NAME=${AZURE_DEVOPS_PROJECT_NAME}
# Use following if you change the repo name to something other than the project name
# export AZURE_DEVOPS_REPO_NAME=
# PUSH CODE
# Change branch name if necessary
export BRANCH_NAME=main
export AZURE_REPO_REMOTE_URL=$(az repos show --repository ${AZURE_DEVOPS_REPO_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --query remoteUrl -o tsv)
git init
git checkout -b ${BRANCH_NAME}
git add .
git commit -m "init"
git remote add ${AZURE_DEVOPS_REPO_NAME} ${AZURE_REPO_REMOTE_URL}
git push -u ${AZURE_DEVOPS_REPO_NAME} ${BRANCH_NAME}
# You may get prompted to enter a password to Azure DevOps here. Go to https://dev.azure.com and get a personal access token and then paste it into the console.
# AZURE DEVOPS SERVICE CONNECTION
export AZURE_DEVOPS_SERVICE_CONNECTION_NAME=azconnection
az devops service-endpoint azurerm create --azure-rm-service-principal-id ${AZURE_SERVICE_PRINCIPAL_ID} --azure-rm-subscription-id ${AZURE_SUBSCRIPTION_ID} --azure-rm-subscription-name ${AZURE_SUBSCRIPTION_NAME} --azure-rm-tenant-id ${AZURE_TENANT_ID} --name ${AZURE_DEVOPS_SERVICE_CONNECTION_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI}
# AZURE DEVOPS SERVICE CONNECTION PERMISSIONS
export AZURE_DEVOPS_SERVICE_CONNECTION_ID=$(az devops service-endpoint list --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --query "[?contains(name, '${AZURE_DEVOPS_SERVICE_CONNECTION_NAME}')].id" -o tsv)
az devops service-endpoint update --id ${AZURE_DEVOPS_SERVICE_CONNECTION_ID} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --enable-for-all true
# AZURE DEVOPS PIPELINE
export AZURE_DEVOPS_PIPELINE_NAME=azdpipeline
az pipelines create --name ${AZURE_DEVOPS_PIPELINE_NAME} --branch ${BRANCH_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --repository ${AZURE_DEVOPS_REPO_NAME} --repository-type tfsgit --yml-path "./.azdo/pipelines/azure-dev.yml" --service-connection ${AZURE_DEVOPS_SERVICE_CONNECTION_NAME} --skip-first-run
# AZURE DEVOPS PIPELINE VARIABLES
az pipelines variable create --name AZURE_SUBSCRIPTION_ID --org ${AZURE_DEVOPS_ORG_URI} --pipeline-name ${AZURE_DEVOPS_PIPELINE_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --value ${AZURE_SUBSCRIPTION_ID} --allow-override true
az pipelines variable create --name AZURE_LOCATION --org ${AZURE_DEVOPS_ORG_URI} --pipeline-name ${AZURE_DEVOPS_PIPELINE_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --value ${AZURE_LOCATION} --allow-override true
az pipelines variable create --name AZURE_ENV_NAME --org ${AZURE_DEVOPS_ORG_URI} --pipeline-name ${AZURE_DEVOPS_PIPELINE_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --value ${AZURE_ENV_NAME} --allow-override true
# AZURE DEVOPS BUILD POLICY
export AZURE_DEVOPS_REPO_ID=$(az repos show --repository ${AZURE_DEVOPS_REPO_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --query id -o tsv)
export AZURE_DEVOPS_PIPELINE_BUILD_DEFINITION_ID=$(az pipelines build definition show --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --name ${AZURE_DEVOPS_PIPELINE_NAME} --query id)
az repos policy build create --blocking true --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI} --branch ${BRANCH_NAME} --repository-id ${AZURE_DEVOPS_REPO_ID} --enabled true --build-definition-id ${AZURE_DEVOPS_PIPELINE_BUILD_DEFINITION_ID} --queue-on-source-update-only false --manual-queue-only false --display-name "PR Build Policy" --valid-duration 0
# RUN AZURE DEVOPS PIPELINE
az pipelines run --name ${AZURE_DEVOPS_PIPELINE_NAME} --project ${AZURE_DEVOPS_PROJECT_NAME} --org ${AZURE_DEVOPS_ORG_URI}