Posted in:

So far in my tutorial series on the Azure CLI, I’ve shown you how easy it is to automate the creation of all kinds of Azure resources such as Virtual Machines, Web Apps and SQL Databases, Storage Accounts and Blobs, and Queues.

And so you might think that I would recommend you write a script to automate the deployment of your applications with a series of Azure CLI commands.

But whilst that certainly is possible, if you have a cloud based application that you are going to regularly deploy (e.g. you’re developing an app and want continuous deployment set up), then it’s a good idea to put the effort into creating an ARM template, and using that as your deployment mechanism.

Benefits of using Azure Resource Manager templates

ARM templates offer several benefits over writing your own resource creation scripts.

They’re declarative, rather than imperative, keeping the focus on what should be in a resource group instead of what steps need to be taken to create everything in the group.

They’re parameterizable, allowing you to easily deploy different variants of your resource group. For example, you could deploy it once to North Europe and once to West Europe. Or you could use a premium tier database for production and a basic tier database for testing.

They support more efficient deployment, as the Azure Resource Manager can intelligently identify resources that could get created in parallel.

And they support incremental deployment, making it easy to add a new resource to an existing group simply by changing your template and redeploying it.

So let’s see how we can deploy ARM templates with the Azure CLI

Generating an ARM Template

The Azure CLI has a command that can take any existing resource group and generate an ARM template to represent it. The command is simply az group export, passing in the name of the resource group.

az group export -n MyResourceGroup

If you try this though, you may be in for disappointment. Currently (in version 2.20), the Azure CLI has a bug that means that if there are any warnings in the template then the CLI will report them as errors and not emit a template at all.

Hopefully this issue will be fixed soon, but don’t worry, there’s an easy enough workaround. Simply navigate to the resource group in the Azure portal and select the “Automation Script” option, which will generate the same thing.

The template it generates will try to be helpful by parameterizing the names of various resources, but often you’ll find that the template is overly verbose and doesn’t parameterize the the bits you might want it to, such as the pricing tier of an app service plan for example.

So I tend to view this exported template simply as a helpful guide, and also make use of the Visual Studio tooling to create an Azure Resource Group project, which makes it easier to navigate the template, and add new resources to it.

I also absolutely love the Azure Quickstart Templates GitHub repository, which contains hundreds of sample ARM templates showing you how to achieve all kinds of common tasks.

So between the output of az group export, the examples at GitHub and the help of Visual Studio, you ought to be able to create an ARM template that describes exactly how you want your resource group set up, and has parameters that let you change the things that you want to vary between deployments.

Deploying an Online Template

A great way to get started is to actually try to deploy one of the templates in the Azure Quickstart Templates repository. For this demo, I’ve picked out the WordPress on Docker example, which deploys a single Ubuntu VM, installs Docker on it and then runs a MySQL and WordPress container, giving you a very simple way to get WordPress up and running.

To deploy it we first need to create a resource group (with az group create), and then we use az group deployment create, passing in the target resource group and URI of the template. We can also give our deployment a name (I’ve chosen “TestDeployment”), and customize the parameters.

# create a resource group
az group create -l $location -n $resourceGroup

# the template we will deploy

# deploy, specifying all template parameters directly
az group deployment create \
    --name TestDeployment \
    --resource-group $resourceGroup \
    --template-uri $templateUri \
    --parameters 'newStorageAccountName=myvhds96' \
                 'mysqlPassword=My5q1P@s5w0rd!' \
                 'adminUsername=mheath' \
                 'adminPassword=Adm1nP@s5w0rd!' \

You’ll see that I’m passing multiple parameters with the --parameters argument. You have to supply values for all template parameters that don’t have a default specified. This template requires five arguments, and two of them will form part of a domain name, so you will need to provide unique values across Azure for this deployment to succeed.

Once the deployment has completed we can explore what’s in the resource group and find the domain name to visit to try out our WordPress site with the following commands:

# see what's in the group we just created
az resource list -g $resourceGroup -o table

# find out the domain name we can access this from
az network public-ip list -g $resourceGroup --query "[0].dnsSettings.fqdn" -o tsv

By the way, if you try this and find that the site isn’t working, it may be that the WordPress Docker container stopped because the MySql Docker container failed to start quickly enough. That’s easily fixed by SSHing into the VM, finding out the id of the WordPress container, and restarting it if it has exited. Here’s a few commands to get you started with that:

# if it doesn't work, SSH in:
ssh [email protected]

# see if the wordpress container has exited
docker ps --all

# restart the wordpress container (replace e03 with the id of the exited container)
docker start e03

Deploying a Local Template

As well as deploying from an online template, you can provide the az group deployment create command the path of a local template file.

In this example I have a local ARM template called MySite.json and a local parameter file called MySite.parameters.json. I can point at the template with the --template-file argument and at the parameters file using the special @ prefix on the filename. Notice I can still proved additional parameters overrides if I want.

# create a resource group for this deployment
az group create -n $resourceGroup -l westeurope

# perform the initial deployment
az group deployment create -g $resourceGroup -n $deploymentName \
        --template-file MySite.json \
        --parameters @MySite.parameters.json \
        --parameters "administratorLoginPassword=$sqlPassword"

Updating an existing deployment

Once this deployment completes, we can easily update it by deploying an updated version of our ARM template to the same resource group over the top of the existing deployment. It’s pretty straightforward but there is one key argument to pay attention to. If --mode is set to Incremental, the deployment will be allowed to create any missing resources but not to delete anything. If its set to Complete, then any resources in the target resource group that are not specified in the ARM template will get deleted. Obviously Complete mode is more dangerous, but has the benefit of not leaving old unused infrastructure around wasting money.

az group deployment create -g $resourceGroup -n $deploymentName \
        --template-file MySite.json \
        --parameters @MySite.parameters.json \
        --parameters "administratorLoginPassword=$sqlPassword" \
        --mode Complete


So don’t think of the Azure CLI as an alternative to using Azure Resource Manager Templates, but as a useful tool for deploying them. In my opinion, whenever you have an application you’re planning to deploy more than once, it’s worth the time investment to generate an ARM template for it.

For previous entries in my Azure CLI tutorial series:

Want to learn more about the Azure CLI? Be sure to check out my Pluralsight course Azure CLI: Getting Started.


Comment by Irshad Alam

Good article Mark .Thanks.
Please update the it as i tried and got below message --
This command is implicitly deprecated because command group 'group deployment' is deprecated and will be removed in a future release. Use 'deployment group' instead.

Irshad Alam