In a previous article I talked about some hoops I needed to jump through when setting up a vm scale set as a self hosted build agent. One of them is getting the Azure CLI task to work. I wanted to talk about how we handled this.

The starting point is my build is running and I want to execute an Azure CLI task. I had my pipeline task setup with a service connection but I was getting the below error:

Azure CLI 2.x is not installed on this machine

This task attempts to do a login for the Azure CLI before running your command but then it gets the error that it cant find Azure CLI.

Test if Azure CLI is installed

I wanted to check if the Azure CLI is actually installed, I can do this with the below command.

az version

I get the below error so I know its not and I need to do something:

The term 'az' is not recognized as the name of a cmdlet, function, script file, or operable program

How to install the Azure CLI

There are a couple of different ways you can install the Azure CLI. These include:

  • Using the Powershell command
  • Installing with chocolatey
  • Using Custom Extensions

I had quite a bit of trial and error here to get it to work but Ill cover what worked first and then some notes on what didnt.

Which approach worked?

The way I ended up doing this was to create a powershell script which does the following:

  • Installs Chocolatey
  • Installs the Azure CLI using chocolatey
  • Adds the path to the Azure CLI to the Path environment variable

The script is below:

#Install Chocolatey so we can simplify the install of some tools and apps for the build agent
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString(''))

#Install the Azure CLI
choco install azure-cli -y

#After we install the Azure CLI we need to add to the path environment variable so that az commands will work
$env:Path += ";C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin"

I then added this script to a container in a storage account and on my Azure VM Scale set I added the Custom Script extension and pointed it to the script.

When I kicked off a build and it recreates a new vm in the scale set it runs this script which does the tasks needed.

How did I test it had worked

In my pipeline I run 3 tasks to check different bits to make sure its working ok. The first is the one above which uses the Powershell task to run the below command:

# List Paths

$folderToCheck = 'C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin'

Get-ChildItem -Path $folderToCheck -Recurse | Where-Object {$_.PSIsContainer -eq $false}  

This task was for diagnostics purposes to let me check the path to the Azure CLI was in the environment variable and also to list the contents of the directory im expecting it to be installed to.

Next I ran the below command to check I can execute the az command.

az version

This checks I can execute the az command and that its installed. I think in my various approaches the cli might have been getting installed but the path wasnt modified so it wasnt picking it up.

The next test in the pipeline is to run a command which checks I can access Azure. I configured my Azure CLI devops task to use a service connection and then the below command will check for storage accounts in a resource group which is a pretty simple command which checks I can do a basic Azure CLI command and that I should be good.

az storage account list --resource-group [MikesResourceGroup] --subscription [Mikes subscription id]

Which ways didnt work for me

I tried a few of the below ways but they didnt work for me. I have a feeling I might be able to get some of them to work now that I realised part of my issue was the Path variable not being updated. For me I quite liked the chocolatey approach because it is an easy way for me to install other software on the agent so Im going to stick with that.

I thought id share a couple of notes on the problems I did have to maybe help others.

Normal silent install in pipeline

I tried to run a task in the pipeline using the below command. It ran but I was still getting the original error.

$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -Uri -OutFile .\AzureCLI.msi; Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'; rm .\AzureCLI.msi

I think with this I fell into the original trap from my previous article where I had been running the pipeline in interactive mode so I got some problems doing the install. I also felt like when I got past that the install either didnt set the path variable or that because the pipeline was already running it didnt pick up a refreshed environment variable.

None Admin install in Pipeline

You can do an install which doesnt require admin permissions using the below script. I also tried this in my pipeline thinking I might avoid the blockers I was getting. I think if Id gotten this to work I would have just had the path issue after it.

Custom Extension with Silent Installs

The above 2 approaches I tried to use with a custom extension and again it wasnt working. One of the problems I found with the custom extension was its difficult to troubleshoot what happened if your script doesnt work.

On reflection I think this might have been missing the step to add it to the path variable. Id already reverted to chocolatey by the time id figured this and didnt want to go back to the custom install.

Install with Chocolatey in Pipeline

With Chocolatey I initially tried just installing it in the pipeline. There are some out of the box tasks which let you install chocolatey and add packages. Initially I fell into the interactive logon problem where not having the elevated privileges gave me a problem installing the packages and I could install it to a custom directory but that just introduced other hurdles.

I then changed the build agent to not use the interactive logon and managed to get past that problem but I seemed to then just hit the problem which turned out to be the Path problem.

I could definitely install some packages but was having problems after id installed Azure CLI running it.


For me there are some really interesting discussion areas covered here, which things do I try to do in a custom extension script vs which things do I do in the pipeline.

I think installing chocolatey and Azure CLI in the custom extension make sense. They are common things you are likely to use in most of your pipelines so lets get them on the vm regardless.

There may be other software which is specific to just one pipeline that I can now install with the choco install command in a pipeline so its there for just that run and when the vm is re-imaged for the next build its not on the vm again.

Anyway, this is how I got the Azure CLI task to work on my basic imaged Azure VM scale set and hopefully this makes someone elses like a little less painful