Azure Resource Manager (ARM) is the deployment engine behind every Azure resource — whether you click in the portal, run a CLI command, or deploy a template, ARM receives the request and makes the infrastructure match. ARM templates describe that infrastructure as code. What has changed since this topic was young: you no longer write the JSON by hand. Bicep is Microsoft's language for authoring ARM templates — it compiles to the same JSON, deploys through the same engine, and is what Microsoft's own documentation and examples use today.

What are Azure Resource Manager (ARM) templates?
Azure Resource Manager templates (ARM templates) are JSON files that describe Azure infrastructure declaratively — you state what should exist (a VM, a storage account, a network) and Azure Resource Manager works out how to create or update it, idempotently. Bicep, used throughout this article, is the modern authoring language that compiles directly to ARM template JSON: same engine, same capabilities, dramatically better syntax. Everything you deploy with Bicep is an ARM template under the hood.
This overview covers how ARM works, why templates beat scripting, and a complete Bicep template you can deploy.
What Azure Resource Manager does
ARM is the management layer for your subscription. Every operation — portal, Azure CLI, PowerShell, SDKs, templates — goes through it, which gives you consistent:
- Declarative deployments — you describe the end state; ARM figures out create vs. update.
- Grouping — resources deploy into resource groups and can be managed, secured, and deleted as a unit.
- Access control and tagging — RBAC and tags apply uniformly because everything passes through one API.
- Dependency ordering — ARM builds a dependency graph and creates resources in the right order, parallelizing what it can.
Why templates instead of scripts?
A CLI script says how — create this, then that, and fails halfway when something already exists. A template says what — the desired end state. Deployments are idempotent: deploy the same template twice and the second run changes nothing. That makes templates safe to run from CI/CD on every release, reviewable in pull requests, and the single source of truth for an environment.
ARM JSON vs. Bicep
An ARM template is JSON with sections for parameters, variables, resources, and outputs. It works, but it is verbose and easy to get wrong by hand. Bicep is a thin language over exactly the same model — every Bicep file compiles (bicep build) to an ARM JSON template, so there is nothing Bicep can't do that ARM JSON can. You get shorter files, type checking, editor IntelliSense for every resource type, and no [concat(...)] string gymnastics.
A complete Bicep template
main.bicep — a storage account with sensible security defaults:
@description('Name of the storage account — must be globally unique')
param storageAccountName string = 'geekstore${uniqueString(resourceGroup().id)}'
@description('Azure region for all resources')
param location string = resourceGroup().location
@allowed(['Standard_LRS', 'Standard_GRS'])
param skuName string = 'Standard_LRS'
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: storageAccountName
location: location
sku: {
name: skuName
}
kind: 'StorageV2'
properties: {
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
}
}
output storageEndpoint string = storageAccount.properties.primaryEndpoints.blob
The pieces map one-to-one onto ARM template concepts:
param— inputs with defaults, descriptions, and validation (@allowedrestricts values at submit time).resource— type and API version (Microsoft.Storage/storageAccounts@2023-05-01), then the desired properties.- Functions —
uniqueString(),resourceGroup().locationand friends are the same functions ARM JSON uses, minus the bracket syntax. output— values handed back after deployment (endpoints, connection info) for scripts or dependent deployments to consume.
Compiling it shows the relationship — bicep build main.bicep produces the classic ARM JSON:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"type": "string",
"defaultValue": "[format('geekstore{0}', uniqueString(resourceGroup().id))]",
...
You never need to read or edit that JSON — but knowing it's there demystifies Bicep: it is ARM templates with better ergonomics, not a different deployment system.
Deploying
The Azure CLI compiles Bicep automatically — deploy the .bicep file directly:
az group create --name geekstore-rg --location eastus
az deployment group create \
--resource-group geekstore-rg \
--template-file main.bicep \
--parameters skuName=Standard_LRS
Before deploying to anything that matters, preview the changes:
az deployment group what-if \
--resource-group geekstore-rg \
--template-file main.bicep
what-if prints exactly what would be created, changed, or deleted — the ARM equivalent of a Terraform plan, and the habit that prevents surprise deletions.
You will need your subscription and credentials configured first — see getting the Azure subscription, tenant, client id and secret for setting up a service principal for automation.
Multiple resources and dependencies
Reference one resource from another and ARM infers the deployment order — no explicit dependsOn needed in Bicep for the common case:
resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
name: 'geekstore-plan'
location: location
sku: { name: 'B1' }
}
resource webApp 'Microsoft.Web/sites@2023-12-01' = {
name: 'geekstore-web'
location: location
properties: {
serverFarmId: appServicePlan.id // implicit dependency — plan deploys first
}
}
When you still meet raw ARM JSON
- Existing pipelines and older docs — everything still works; Bicep can even decompile them (
bicep decompile template.json) as a starting point for migration. - Portal "Export template" — exports JSON, which you can decompile to Bicep.
- Some marketplace/quickstart content still ships JSON-first.
For anything new, author in Bicep. If your team is multi-cloud, Terraform (or its Azure-verified provider) fills the same role across providers — but within Azure, Bicep has no state file to manage and is always current with new resource types.
Comments (0)
No comments yet — be the first to share your thoughts.