Nordlicht Reisen GmbH, a fictional travel agency, no longer wants to click its environments together by hand. A colleague has written a template for a storage account, and you are to read it, adapt it and deploy it. In addition, a resource group that someone created in the portal is to be captured as a template. This is exactly what the exam asks for: read, change and deploy an ARM template and export a deployment as an ARM template.

Prior knowledge. What Azure Resource Manager (ARM) is and why infrastructure as code makes sense is covered in "AZ-900 compact", lesson 3.3, and in K3, lesson 3.2.1. K3 teaches the Bicep language, modules and what-if in module 3.2 (lessons 3.2.2 to 3.2.4); lesson 3.2 of this course only adds what the exam requires about Bicep. This lesson is about the JSON format behind both.

The structure of an ARM template

An ARM template is a JSON file. In its simplest form it consists of these elements (the table follows Microsoft's description):

Element Required What for
$schema yes address of the JSON schema that describes the version of the template language; it depends on the scope of the deployment
contentVersion yes version of the template, for example 1.0.0.0; any value, for keeping track of important changes
parameters no values that you provide at deployment
variables no values that you reuse in the template to shorten expressions
functions no custom functions
resources yes the resource types that are deployed or updated
outputs no values that come back after the deployment

In addition there are the elements languageVersion, apiProfile and definitions, which play no role in this lesson. Important to know: With "languageVersion": "2.0", resources becomes an object instead of a list, and resources then have a symbolic name. This also happens when a Bicep file uses certain features; you only have to be able to recognize it.

A small, complete template that creates a storage account (Microsoft shows it in this form in the documentation on template specs):

JSON
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "type": "string",
      "minLength": 3,
      "maxLength": 24
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2025-06-01",
      "name": "[parameters('storageAccountName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "Standard_LRS"
      },
      "kind": "StorageV2",
      "properties": {
        "accessTier": "Hot"
      }
    }
  ]
}

This is how you read it:

  • Parameters have a type (string, securestring, int, bool, object, secureObject, array) and optionally defaultValue, allowedValues, minLength, maxLength, minValue, maxValue and metadata with a description. Here the name must be 3 to 24 characters long, and location takes the location of the resource group if nobody provides anything.
  • Expressions are in square brackets and in quotation marks: "[parameters('location')]" reads a parameter, [resourceGroup().location] calls a function.
  • Resources have type (provider and resource type, here Microsoft.Storage/storageAccounts), apiVersion (the version of the REST interface used to create the resource), name, usually location, and depending on the type sku, kind and properties. dependsOn sets an order; resources without a dependency are deployed in parallel. Microsoft advises: For a new template, use the current API version, and change it later only when you need a new feature. This lowers the risk that a new version changes the behavior.
  • Outputs return values. An output of type securestring doesn't appear in the deployment history.
  • Comments are possible in templates with // and /* ... */. For the Azure CLI, Microsoft names the switch --handle-extended-json-format for this, which the reference for az deployment group create now marks as deprecated. According to Microsoft, multiline strings in templates work only with Azure PowerShell or Azure CLI, not through the portal, a DevOps pipeline or the REST interface.

The limits from the documentation: at most 256 parameters, 256 variables, 800 resources and 64 outputs per template.