Stage 4 · Provision
Bicep & ARM Templates for Azure
ARM Template Deep Dive
Template structure, functions, deployment modes, and linked templates — mastering Azure Resource Manager.
Template Structure
ARM templates are JSON files that define Azure resources. Every template has a schema version, content version, parameters, variables, resources, and outputs. Understanding this structure is essential for debugging and advanced patterns.
{
"\$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environment": {
"type": "string",
"allowedValues": ["dev", "staging", "prod"]
}
},
"variables": {
"storageName": "[concat('stg', parameters('environment'), uniqueString(resourceGroup().id))]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[variables('storageName')]",
"location": "[resourceGroup().location]",
"sku": { "name": "Standard_LRS" },
"kind": "StorageV2"
}
],
"outputs": {
"storageId": {
"type": "string",
"value": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageName'))]"
}
}
}The $schema field points to the ARM template schema. contentVersion tracks template versions. The resources array contains all resources to deploy. Outputs expose values after deployment.
Parameters and Variables
Parameters are inputs to the template. Variables are computed values for internal use. Both support ARM template functions for dynamic value calculation.
{
"parameters": {
"instanceCount": {
"type": "int",
"defaultValue": 1,
"minValue": 1,
"maxValue": 10,
"metadata": {
"description": "Number of instances to deploy"
}
},
"adminPassword": {
"type": "securestring",
"metadata": {
"description": "Admin password (masked in logs)"
}
}
},
"variables": {
"storageSku": "[if(equals(parameters('environment'), 'prod'), 'Standard_GRS', 'Standard_LRS')]",
"tags": {
"Environment": "[parameters('environment')]",
"ManagedBy": "ARM"
}
}
}The securestring type masks the parameter in deployment logs. The defaultValue makes parameters optional. Variables use ARM functions to compute values dynamically.
ARM Functions
ARM templates have a rich set of built-in functions for string manipulation, math, logic, and resource references. These functions are evaluated at deployment time.
| Function | Purpose | Example |
|---|---|---|
| concat | Join strings | [concat('stg', parameters('env'))] |
| uniqueString | Generate unique hash | [uniqueString(resourceGroup().id)] |
| resourceId | Get resource ID | [resourceId('Microsoft.Storage/storageAccounts', 'name')] |
| if | Conditional value | [if(condition, trueVal, falseVal)] |
| equals | Equality check | [equals(parameters('env'), 'prod')] |
| length | Array/string length | [length(variables('subnets'))] |
{
"variables": {
"subnetNames": "[array('web', 'api', 'data')]",
"subnetCount": "[length(variables('subnetNames'))]",
"firstSubnet": "[variables('subnetNames')[0]]",
"isProduction": "[equals(parameters('environment'), 'prod')]",
"vmSize": "[if(variables('isProduction'), 'Standard_D4s_v3', 'Standard_B2s')]",
"tags": "[union(variables('commonTags'), parameters('additionalTags'))]"
}
}ARM functions can be nested and combined. Array indexing uses zero-based brackets. The union function merges objects, with later values overriding earlier ones.
Deployment Modes
ARM supports incremental and complete deployment modes. Incremental mode adds new resources and modifies existing ones. Complete mode deletes resources not in the template. Complete mode is dangerous — use it carefully.
# Incremental mode (default)
$ az deployment group create \
--resource-group myRG \
--template-file main.json \
--mode Incremental
# Complete mode (deletes resources not in template)
$ az deployment group create \
--resource-group myRG \
--template-file main.json \
--mode CompleteIncremental mode is safe — it only manages resources defined in the template. Complete mode removes any resource in the resource group that is not in the template. Never use complete mode without thorough testing.
Linked Templates
Linked templates allow you to split large ARM templates into smaller, reusable files. The main template links to child templates using Microsoft.Resources/deployments. Linked templates must be hosted in a publicly accessible URL or Azure storage.
{
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2023-05-01",
"name": "storageDeployment",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "https://myaccount.blob.core.windows.net/templates/storage.json",
"contentVersion": "1.0.0.0"
},
"parameters": {
"storageAccountName": { "value": "[parameters('storageName')]" }
}
}
}
]
}The templateLink points to a hosted template file. Parameters are passed via the parameters object. The linked deployment runs as a nested deployment within the parent.
When to Use ARM
- When you need features unique to ARM — complete mode deployments, template spec links.
- When Azure Policy requires ARM templates for remediation tasks.
- When debugging Bicep — the transpiled ARM JSON reveals exactly what Bicep generates.
- When maintaining legacy templates that already work.
- When Bicep does not yet support a new Azure resource type.
For new Azure IaC projects, start with Bicep. It transpiles to ARM, so you get all ARM benefits with a better authoring experience. Reserve raw ARM for edge cases where Bicep falls short.
Complete mode deletes any resource in the resource group that is not defined in the template. If your template misses a resource, it will be deleted during deployment. Always use incremental mode for production.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.