Guide: ARM Templates For Beginners

Beginners guide to understanding and create Azure Resource Manager (ARM) templates and deploy them using power shell or Azure portal

Introduction

Recently I have started learning and working with a number of Microsoft technologies such as Azure, Microsoft Defender, Sentinel & Office 365. As my knowledge of these areas develops I aim to create guides to support both my learning and contribute to the wider community.

This guide/blog post will hopefully be a multipart series starting with the very basics of ARM templates and progress to more complex templates & Azure blueprints. It is aimed at those who have had some experience with Azure infrastructure but very little with ARM templates.

One of the areas I have focused on is deploying infrastructure as code. Many of you that have worked on other cloud platforms such as AWS and GCP may be aware of Terraform; a tool created by HashiCorp that allows users to deploy resources using HashiCorp Configuration Language (HCL) or JSON. Azure Resource Manager (ARM) templates offer similar functionality to Terraform and are JSON files that define the infrastructure to be deployed.  

The question some of you may ask is, 'why is this necessary when I can go to the Azure/AWS/GCP web interface and create the resources?'

The Answer:

  1. Repeatability & Consistency - Infrastructure as code allows you to deploy the exact same resource in multiple regions or in different environments such as Dev/Test/Production.
  2. Automation - Rather than having to manually specify parameters when creating resources this can all be automated.
  3. Versioning - As with all code IaC can be committed to versioning control platforms such as Git which can support organisations with continuous delivery and integration.
  4. Efficiency - A task that may have previously taken an organisation months can be reduced to weeks.

What is an ARM template?

I've mentioned briefly that ARM templates are JSON files that define infrastructure to be deployed. So what do they look like?

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "location": {
            "type": "string"
        },
        "virtualNetworkName": {
            "type": "string"
        }
    },
    "variables": {},
    "resources": [
        {
            "name": "[parameters('virtualNetworkName')]",
            "type": "Microsoft.Network/VirtualNetworks",
            "apiVersion": "2019-09-01",
            "location": "[parameters('location')]",
            "dependsOn": [],
            "tags": {},
            "properties": {
                "addressSpace": {
                    "addressPrefixes": [
                        "10.1.0.0/16"
                    ]
                },
                "subnets": [
                    {
                        "name": "default",
                        "properties": {
                            "addressPrefix": "10.1.0.0/24"
                        }
                    },
                    {
                        "name": "subnet1",
                        "properties": {
                            "addressPrefix": "10.1.1.0/24"
                        }
                    }
                ],
                "enableDdosProtection": "[parameters('ddosProtectionPlanEnabled')]"
            }
        }
    ]
}
Sample ARM Template

If this appears complicated don't panic! I will break down each section to help you get a better understanding of what is contained in each section of the template.

Firstly let's have a look at what the basic layout of an ARM template is:

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {},
    "variables": {},
    "resources": []
}

$schema - Here you are defining the location of the JSON file that describes the template language.

contentVersion - This is a value you can specify to demonstrate the version of your file e.g. if minor changes were made you may change 1.0.0.0 to 1.0.0.1 or to 2.0.0.0 for a major change.

parameters - These are values that are to be provided when the resource is deployed. For example if you're using the template above to deploy a VNet you may use parameters to specify the name (prodVnet, testVnet, devVnet). When configuring parameters in an ARM template you can also add a description and a default value which will be automatically be populated in the Azure Portal. Parameters can be called in the resources section by using the code:

"[parameters('yourparameterhere')"]

variables - As in coding, variables allow you to hold values that may be used throughout the code. This makes the code much easier to manage especially when the variable is called multiple times. As with parameters variables are called in resources by using the code:

"[variables('yourvariablehere')]"

resources - Here we specify the type of resource we want to deploy. For example a VNET or a virtual machine.

Breakdown of a "Resource"

Resources are the heart of an ARM template, as mentioned above we define the resource we want to create in this section. In the example below we have created define a VNET:

    "resources": [
        {
            "name": "testVNET",
            "type": "Microsoft.Network/VirtualNetworks",
            "apiVersion": "2019-09-01",
            "location": "UK South",
            "dependsOn": [],
            "tags": {},
            "properties": {
                "addressSpace": {
                    "addressPrefixes": [
                        "10.1.0.0/16"
                    ]
                },
                "subnets": [
                    {
                        "name": "default",
                        "properties": {
                            "addressPrefix": "10.1.0.0/24"
                        }
                    },
                    {
                        "name": "subnet1",
                        "properties": {
                            "addressPrefix": "10.1.1.0/24"
                        }
                    }
                ],
                "enableDdosProtection": "[parameters('ddosProtectionPlanEnabled')]"
            }
        }
    ]
Sample Resource Template

name  - Here we define the name of the Virtual network that we are creating.

type - This is the type of resource that is going to be created in this case a Virtual Network.

apiVersion - The version of the REST API used to create the resource. The latest API versions can be found at Azure Resource Manager reference templates

location - The location that we want to deploy the resource in.

dependsOn - When creating multiple resources you can configure this to deploy a specific resource first e.g. A VNET peering cannot be created without creating a VNET first

tags - Allows you to organise resources

properties - that are specific to the resource being created e.g. in this case a Virtual Network needs an address prefix (10.1.0.0/16) and subnets. This section will vary for each resource and more information about this can be found at Azure Resource Manager reference templates reference.

ARM Template Resources

Before going ahead and creating ARM templates we need to have an idea of what we can create and how to create it. There are 3 valuable resources to help you with this:

  1. Azure portal - When creating a deployment on Azure we have the option before creation to 'Download a template for automation' (Figure 1)
Figure 1 - 'Download Template for Automation'

This will provide you with an ARM template for the resource you created. In this case it is a VNET with 2 subnets and is shown in the code box below. As useful as this can be to create ARM templates I suggest you only use this to learn or when you have had enough experience manually creating ARM templates, as this method will not teach you the ins and outs of the templates.

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "location": {
            "type": "string"
        },
        "virtualNetworkName": {
            "type": "string"
        },
        "resourceGroup": {
            "type": "string"
        },
        "addressSpaces": {
            "type": "array"
        },
        "ipv6Enabled": {
            "type": "bool"
        },
        "subnetCount": {
            "type": "int"
        },
        "subnet0_name": {
            "type": "string"
        },
        "subnet0_addressRange": {
            "type": "string"
        },
        "subnet1_name": {
            "type": "string"
        },
        "subnet1_addressRange": {
            "type": "string"
        },
        "ddosProtectionPlanEnabled": {
            "type": "bool"
        },
        "firewallEnabled": {
            "type": "bool"
        },
        "bastionEnabled": {
            "type": "bool"
        }
    },
    "variables": {},
    "resources": [
        {
            "name": "[parameters('virtualNetworkName')]",
            "type": "Microsoft.Network/VirtualNetworks",
            "apiVersion": "2019-09-01",
            "location": "[parameters('location')]",
            "dependsOn": [],
            "tags": {},
            "properties": {
                "addressSpace": {
                    "addressPrefixes": [
                        "10.1.0.0/16"
                    ]
                },
                "subnets": [
                    {
                        "name": "default",
                        "properties": {
                            "addressPrefix": "10.1.0.0/24"
                        }
                    },
                    {
                        "name": "subnet1",
                        "properties": {
                            "addressPrefix": "10.1.1.0/24"
                        }
                    }
                ],
                "enableDdosProtection": "[parameters('ddosProtectionPlanEnabled')]"
            }
        }
    ]
}
Exported ARM Template

2. Do you need to create a keyvault, a compute resource or deploy an azure firewall? The information for this can be found at the following resource.

Azure Resource Manager template reference - ARM template reference
Find reference documentation for deploying resources through Azure Resource Manager templates. Shows all resource types.

This link will provide you with all the resource types and sample ARM templates. Despite the importance of this reference, I must admit I am not the biggest fan as the sample ARM templates provided have every optional extra included rather than providing a bare minimum template we can build on.

3. Azure quickstart templates provide 100's of templates you can use to quickly deploy infrastructure. It is an amazing community/Microsoft driven resource but once again I would recommend you learn how to create templates so you understand how everything works. As a beginner use this as a learning tool and as you become more advanced used this to support you with your deployments. Quickstart templates can be found here:

Azure Quickstart Templates
Deploy Azure resources through the Azure Resource Manager with community contributed<br>templates to get more done. Deploy, learn, fork and contribute back.

Deploying your templates

Now that we have created a template how do we deploy it to Azure. There are two ways to do this (if deploying to a resource group):

  1. We can deploy the template using the Azure Portal.
  2. First we go to the Azure portal and select 'Create a Resource.
  3. We then 'Template deployment (deploy using custom templates) (preview)'
  4. We then select 'build template in the editor'
  5. From here we can either copy & paste the code or upload a file containing the code.
  6. We can then save the template and assign it to a resource group, location and fill in any of the required parameters, in the case of this template; location and Virtual Network Name.

The second (recommended) option is to deploy using powershell with the following command:

Deploy to a resource group:

New-AzResourceGroupDeployment -ResourceGroupName <resource-group-name> -TemplateFile <path-to-template>

Using powershell we can also

Deploy to a subscription:

New-AzSubscriptionDeployment -Location <location> -TemplateFile <path-to-template>

Deploy to a management group

New-AzManagementGroupDeployment -Location <location> -TemplateFile <path-to-template>

Deploy to a tenant

New-AzTenantDeployment -Location <location> -TemplateFile <path-to-template>

If you have stored your templates on a shared location such as GitHub you can use the '-TemplateUri' parameter instead.  

Once the deployment has completed you should have a VNET in your tenant with the IP range you specified and the two subnets. Congratulations, you've successfully deployed your first ARM template!

Recommendations

I thought I would add this section in the end to summarise my recommendations for creating ARM templates.

  1. As I have mentioned multiple times, start from the very basics and work your way up.
  2. Use quickstart templates and Azure portal to help you learn and not just to create the templates you need.
  3. Play around with the code, as with all languages it's important you learn by making changes and understand what works or breaks the code.
  4. If you have not worked with JSON files before grasp a basic understanding of the syntax and formatting.
  5. Following on from the previous point, I would recommend using software such as VSCode to help you identify errors (finding that missing comma manually is not fun!)

I hope that this post has helped you grasp a better understanding of Azure Resource Manager templates. In the next post I will cover creating more complex templates.

If you have any questions/comments feel free to reach out to me on twitter: