Short on time? Bicep (the next module) supersedes ARM for anything you’d hand-write today — it compiles straight to ARM, so you lose nothing by starting there instead. This module is here because Bicep is ARM underneath, existing ARM templates are still common in the wild, and every limitation covered here still applies to Bicep too. Treat it as reference material you can skip on a first pass and come back to.
What Are ARM Templates?
ARM (Azure Resource Manager) Templates are the native JSON-based IaC format for Azure. Every Bicep file, every Terraform azurerm resource, and every portal deployment ultimately compiles down to an ARM Template before Azure processes it.
ARM Templates are JSON files that declare the resources you want to deploy, their properties, and the dependencies between them. They are submitted directly to the Azure Resource Manager API — the same API the Azure portal uses.
Key Characteristics
Declarative — describe what you want, not how to create it
Idempotent — running the same template multiple times produces the same result
Azure-native — full coverage of every Azure resource type and API version on day 0
No state file — Azure tracks the state of your resources
Deployment modes — Incremental (add/update only) or Complete (remove resources not in template)
Pros
Full Azure API coverage — every new Azure feature is available in ARM the day it launches
No abstraction layer — what you write is what Azure receives; no transpilation surprises
Built-in expression functions — [concat()], [resourceId()], [reference()], etc.
Linked/nested templates — compose large deployments from smaller, reusable templates
What-if deployments — preview changes before applying
# Deploy
az deployment group create \--resource-group my-rg \--template-file storage.json \--parametersstorageAccountName=mystorageaccount123
# Preview changes first
az deployment group what-if \--resource-group my-rg \--template-file storage.json \--parametersstorageAccountName=mystorageaccount123
Migrating to Bicep
ARM is largely superseded by Bicep for human-authored templates. You can decompile any ARM JSON file to Bicep as a starting point:
az bicep decompile --file storage.json
# Produces: storage.bicep
This ARM resource block is missing two of the hardening properties shown in the example above. Add a "kind" of StorageV2, and set minimumTlsVersion to TLS1_2 inside properties.