Introduction
Azure Resource Manager (ARM) templates are JSON files that define the infrastructure and configuration for your Azure deployments. They enable Infrastructure as Code (IaC), allowing you to deploy, update, and delete all the resources for your solution in a consistent and repeatable way. ARM templates facilitate declarative syntax, making it possible to state “what” you want to deploy without having to write the sequence of programming commands to create it.
Core Concepts
Concept | Description |
---|
Resource Manager | Azure service that provides a management layer to create, update, and delete resources |
Template | JSON file that defines resources to deploy |
Resource | Individual Azure service component (VM, storage account, etc.) |
Resource Group | Container for related resources that share a lifecycle |
Deployment | Process of putting resources defined in template into Azure |
Parameters | Values provided during deployment to customize the template |
Variables | Values used throughout a template for consistency |
Functions | Operations for constructing values during template deployment |
Outputs | Values returned after deployment |
Dependencies | Resources that must be deployed before others |
ARM Template Structure
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"apiProfile": "2019-03-01-hybrid",
"parameters": { },
"variables": { },
"functions": [ ],
"resources": [ ],
"outputs": { }
}
Mandatory Elements
- $schema: URI that specifies the JSON schema version
- contentVersion: Your version for the template (e.g., “1.0.0.0”)
- resources: The Azure resources to deploy
Optional Elements
- parameters: Values provided during deployment
- variables: Values used in the template
- functions: User-defined functions
- outputs: Values returned after deployment
- apiProfile: API versions collection for resource types
Parameters
Parameter Definition
"parameters": {
"storageAccountName": {
"type": "string",
"metadata": {
"description": "Name of the storage account"
},
"minLength": 3,
"maxLength": 24
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources"
}
},
"storageAccountType": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Standard_ZRS",
"Premium_LRS"
],
"metadata": {
"description": "Storage Account type"
}
},
"enableDiagnostics": {
"type": "bool",
"defaultValue": true,
"metadata": {
"description": "Enable diagnostics"
}
}
}
Parameter Types
- string: Text values
- secureString: Text values that will be hidden in deployment logs
- int: Integer values
- bool: True or false
- object: Complex structure
- secureObject: Complex structure that will be hidden in logs
- array: Collection of values
Parameter File (parameters.json)
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"value": "mystorageacct123"
},
"location": {
"value": "eastus"
},
"storageAccountType": {
"value": "Standard_GRS"
}
}
}
Variables
Variable Definition
"variables": {
"storageName": "[toLower(concat(parameters('storagePrefix'), uniqueString(resourceGroup().id)))]",
"defaultTags": {
"Environment": "Production",
"Project": "Infrastructure"
},
"vnetAddressPrefix": "10.0.0.0/16",
"subnet1Prefix": "10.0.1.0/24",
"subnet1Name": "subnet-1",
"subnet2Prefix": "10.0.2.0/24",
"subnet2Name": "subnet-2"
}
Resources
Resource Definition
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"tags": "[variables('defaultTags')]",
"sku": {
"name": "[parameters('storageAccountType')]"
},
"kind": "StorageV2",
"properties": {
"accessTier": "Hot",
"supportsHttpsTrafficOnly": true,
"minimumTlsVersion": "TLS1_2"
}
}
]
Key Resource Properties
- type: Resource provider and type
- apiVersion: API version of the resource provider
- name: Name of the resource
- location: Where to deploy the resource
- tags: Metadata tags for the resource
- sku: Billing tier/capabilities
- kind: Type of resource (varies by resource type)
- properties: Resource-specific configuration settings
- dependsOn: Resources that must be deployed first
- condition: Whether to deploy the resource
- copy: For creating multiple instances of a resource
Defining Dependencies
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "[parameters('storageAccountType')]"
}
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2020-12-01",
"name": "[parameters('appServicePlanName')]",
"location": "[parameters('location')]",
"sku": {
"name": "S1",
"tier": "Standard"
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2020-12-01",
"name": "[parameters('webAppName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
],
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]",
"siteConfig": {
"appSettings": [
{
"name": "STORAGE_CONNECTION_STRING",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('storageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2021-04-01').keys[0].value, ';EndpointSuffix=core.windows.net')]"
}
]
}
}
}
]
Creating Multiple Resources with Copy
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"name": "[concat(parameters('storagePrefix'), copyIndex('storageCopy', 1))]",
"location": "[parameters('location')]",
"copy": {
"name": "storageCopy",
"count": 3
},
"sku": {
"name": "[parameters('storageAccountType')]"
},
"kind": "StorageV2"
}
Conditional Deployment
{
"condition": "[equals(parameters('environment'), 'production')]",
"type": "Microsoft.Insights/components",
"apiVersion": "2020-02-02",
"name": "[parameters('appInsightsName')]",
"location": "[parameters('location')]",
"kind": "web",
"properties": {
"Application_Type": "web"
}
}
Template Functions
String Functions
- concat: Combines multiple strings
- toLower / toUpper: Converts to lowercase/uppercase
- substring: Returns part of a string
- replace: Replaces occurrences of a string
- trim: Removes whitespace
- padLeft / padRight: Pads a string
- split: Splits a string into an array
Numeric Functions
- add / sub / mul / div: Basic arithmetic
- mod: Modulo operation
- min / max: Minimum/maximum value
Array Functions
- array: Converts value to array
- concat: Combines arrays
- contains: Checks if array contains a value
- first / last: Gets first/last element
- length: Returns number of elements
Resource Functions
- resourceId: Gets resource ID
- resourceGroup: Gets resource group properties
- subscription: Gets subscription properties
- reference: Gets property values of a resource
- listKeys / listSecrets: Gets secure values
Deployment Functions
- deployment: Gets deployment properties
- parameters: Gets parameter value
- variables: Gets variable value
Logical Functions
- equals / not / and / or: Logical operations
- if: Conditional expression
- coalesce: Returns first non-null value
Sample Usage
"variables": {
"storageName": "[toLower(concat(parameters('storagePrefix'), uniqueString(resourceGroup().id)))]",
"currentTime": "[utcNow('yyyy-MM-dd')]",
"isProduction": "[equals(parameters('environment'), 'production')]"
},
"resources": [
{
"condition": "[variables('isProduction')]",
"name": "[variables('storageName')]",
"type": "Microsoft.Storage/storageAccounts",
"location": "[if(empty(parameters('location')), resourceGroup().location, parameters('location'))]"
}
]
Outputs
Output Definition
"outputs": {
"storageEndpoint": {
"type": "string",
"value": "[reference(parameters('storageAccountName')).primaryEndpoints.blob]"
},
"appServiceUrl": {
"type": "string",
"value": "[concat('https://', reference(parameters('webAppName')).defaultHostName)]"
},
"deployedResources": {
"type": "array",
"value": [
{
"name": "[parameters('storageAccountName')]",
"type": "Storage Account"
},
{
"name": "[parameters('webAppName')]",
"type": "Web App"
}
]
}
}
Common Resource Types
Storage Account
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('storageAccountType')]"
},
"kind": "StorageV2",
"properties": {
"accessTier": "Hot",
"supportsHttpsTrafficOnly": true,
"minimumTlsVersion": "TLS1_2",
"networkAcls": {
"defaultAction": "Deny",
"virtualNetworkRules": [
{
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName'))]",
"action": "Allow"
}
],
"ipRules": [
{
"value": "203.0.113.0/24",
"action": "Allow"
}
]
}
}
}
Virtual Network
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2021-02-01",
"name": "[parameters('vnetName')]",
"location": "[parameters('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('vnetAddressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnet1Name')]",
"properties": {
"addressPrefix": "[variables('subnet1Prefix')]"
}
},
{
"name": "[variables('subnet2Name')]",
"properties": {
"addressPrefix": "[variables('subnet2Prefix')]"
}
}
]
}
}
Virtual Machine
{
"type": "Microsoft.Network/networkInterfaces",
"apiVersion": "2021-02-01",
"name": "[variables('nicName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), variables('subnet1Name'))]"
}
}
}
]
}
},
{
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2021-03-01",
"name": "[parameters('vmName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"osProfile": {
"computerName": "[parameters('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPasswordOrKey')]"
},
"storageProfile": {
"imageReference": {
"publisher": "Canonical",
"offer": "UbuntuServer",
"sku": "18.04-LTS",
"version": "latest"
},
"osDisk": {
"createOption": "FromImage",
"managedDisk": {
"storageAccountType": "Premium_LRS"
}
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
}
}
}
Web App
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2020-12-01",
"name": "[parameters('appServicePlanName')]",
"location": "[parameters('location')]",
"sku": {
"name": "S1",
"tier": "Standard",
"capacity": 1
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2020-12-01",
"name": "[parameters('webAppName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]"
],
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]",
"httpsOnly": true,
"siteConfig": {
"minTlsVersion": "1.2",
"ftpsState": "Disabled",
"appSettings": [
{
"name": "WEBSITE_RUN_FROM_PACKAGE",
"value": "1"
},
{
"name": "ENVIRONMENT",
"value": "[parameters('environment')]"
}
]
}
}
}
SQL Database
{
"type": "Microsoft.Sql/servers",
"apiVersion": "2021-02-01-preview",
"name": "[parameters('sqlServerName')]",
"location": "[parameters('location')]",
"properties": {
"administratorLogin": "[parameters('administratorLogin')]",
"administratorLoginPassword": "[parameters('administratorLoginPassword')]",
"version": "12.0"
},
"resources": [
{
"type": "firewallRules",
"apiVersion": "2021-02-01-preview",
"name": "AllowAllAzureIPs",
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', parameters('sqlServerName'))]"
],
"properties": {
"startIpAddress": "0.0.0.0",
"endIpAddress": "0.0.0.0"
}
},
{
"type": "databases",
"apiVersion": "2021-02-01-preview",
"name": "[parameters('sqlDatabaseName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', parameters('sqlServerName'))]"
],
"sku": {
"name": "Basic",
"tier": "Basic"
},
"properties": {
"collation": "SQL_Latin1_General_CP1_CI_AS",
"maxSizeBytes": 1073741824
}
}
]
}
Deployment Commands
Azure CLI
# Validate a template and parameter file
az deployment group validate \
--resource-group MyResourceGroup \
--template-file azuredeploy.json \
--parameters @azuredeploy.parameters.json
# Deploy a template with a parameter file
az deployment group create \
--resource-group MyResourceGroup \
--template-file azuredeploy.json \
--parameters @azuredeploy.parameters.json
# Deploy a template with inline parameters
az deployment group create \
--resource-group MyResourceGroup \
--template-file azuredeploy.json \
--parameters storageAccountName=mystorageacct \
storageAccountType=Standard_LRS
# Deploy a template from a URL
az deployment group create \
--resource-group MyResourceGroup \
--template-uri https://raw.githubusercontent.com/user/repo/master/azuredeploy.json \
--parameters storageAccountName=mystorageacct
# Get information about a deployment
az deployment group show \
--resource-group MyResourceGroup \
--name DeploymentName
# List deployments in a resource group
az deployment group list \
--resource-group MyResourceGroup
# Export a template used in a deployment
az deployment group export \
--resource-group MyResourceGroup \
--name DeploymentName
Azure PowerShell
# Validate a template and parameter file
Test-AzResourceGroupDeployment `
-ResourceGroupName "MyResourceGroup" `
-TemplateFile "azuredeploy.json" `
-TemplateParameterFile "azuredeploy.parameters.json"
# Deploy a template with a parameter file
New-AzResourceGroupDeployment `
-ResourceGroupName "MyResourceGroup" `
-TemplateFile "azuredeploy.json" `
-TemplateParameterFile "azuredeploy.parameters.json"
# Deploy a template with inline parameters
New-AzResourceGroupDeployment `
-ResourceGroupName "MyResourceGroup" `
-TemplateFile "azuredeploy.json" `
-storageAccountName "mystorageacct" `
-storageAccountType "Standard_LRS"
# Deploy a template from a URL
New-AzResourceGroupDeployment `
-ResourceGroupName "MyResourceGroup" `
-TemplateUri "https://raw.githubusercontent.com/user/repo/master/azuredeploy.json" `
-storageAccountName "mystorageacct"
# Get information about a deployment
Get-AzResourceGroupDeployment `
-ResourceGroupName "MyResourceGroup" `
-Name "DeploymentName"
# List deployments in a resource group
Get-AzResourceGroupDeployment `
-ResourceGroupName "MyResourceGroup"
# Export a template used in a deployment
Save-AzResourceGroupDeploymentTemplate `
-ResourceGroupName "MyResourceGroup" `
-DeploymentName "DeploymentName" `
-OutputFile "template.json"
Subscription and Management Group Deployments
Subscription Deployment Template
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceGroupName": {
"type": "string",
"metadata": {
"description": "Name of the Resource Group to create"
}
},
"resourceGroupLocation": {
"type": "string",
"metadata": {
"description": "Location of the Resource Group"
}
}
},
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2021-04-01",
"name": "[parameters('resourceGroupName')]",
"location": "[parameters('resourceGroupLocation')]",
"properties": {}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"name": "nestedDeployment",
"resourceGroup": "[parameters('resourceGroupName')]",
"dependsOn": [
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('resourceGroupName'))]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"name": "[concat('storage', uniqueString(subscription().subscriptionId))]",
"location": "[parameters('resourceGroupLocation')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2"
}
]
}
}
}
]
}
Deploy to Subscription (CLI)
az deployment sub create \
--location "eastus" \
--template-file "azuredeploy.json" \
--parameters resourceGroupName="MyNewRG" \
resourceGroupLocation="eastus"
Deploy to Subscription (PowerShell)
New-AzDeployment `
-Location "eastus" `
-TemplateFile "azuredeploy.json" `
-resourceGroupName "MyNewRG" `
-resourceGroupLocation "eastus"
Nested Templates
Using a Linked Template
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"name": "linkedTemplate",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('storageTemplateUri')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"storageAccountName": {
"value": "[parameters('storageAccountName')]"
},
"location": {
"value": "[parameters('location')]"
}
}
}
}
Using an Inline Nested Template
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"name": "nestedTemplate",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"type": "string"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"name": "[parameters('storageAccountName')]",
"location": "[resourceGroup().location]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2"
}
],
"outputs": {
"storageEndpoint": {
"type": "string",
"value": "[reference(parameters('storageAccountName')).primaryEndpoints.blob]"
}
}
},
"parameters": {
"storageAccountName": {
"value": "[parameters('storageAccountName')]"
}
}
}
}
Deployment Modes
Mode | Description |
---|
Incremental (default) | Adds or updates resources, leaves existing untouched resources |
Complete | Removes resources not in template, adds new resources, updates existing resources |
# Deploy in complete mode (CLI)
az deployment group create \
--resource-group MyResourceGroup \
--template-file azuredeploy.json \
--mode Complete
# Deploy in complete mode (PowerShell)
New-AzResourceGroupDeployment `
-ResourceGroupName "MyResourceGroup" `
-TemplateFile "azuredeploy.json" `
-Mode Complete
Best Practices
Structure and Organization
- Use parameters for values that vary between deployments
- Use variables for values used multiple times
- Organize complex templates into nested or linked templates
- Use descriptive names for resources, parameters, and variables
- Include parameter validations (allowedValues, minValue, maxValue, etc.)
- Group related resources together
- Comment complex sections
Security
- Use secureString for passwords and secrets
- Avoid hardcoding sensitive data
- Use Key Vault references for sensitive values
{
"adminPassword": {
"reference": {
"keyVault": {
"id": "/subscriptions/{subscription-id}/resourceGroups/{rg-name}/providers/Microsoft.KeyVault/vaults/{vault-name}"
},
"secretName": "adminPassword"
}
}
}
Resource Tagging
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"tags": {
"Environment": "[parameters('environment')]",
"Project": "[parameters('projectName')]",
"Owner": "[parameters('owner')]",
"CostCenter": "[parameters('costCenter')]",
"DeploymentDate": "[utcNow('yyyy-MM-dd')]"
},
// ...
}
]
Resource Locks
{
"type": "Microsoft.Authorization/locks",
"apiVersion": "2017-04-01",
"name": "[concat(parameters('storageAccountName'), '-lock')]",
"scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
],
"properties": {
"level": "CanNotDelete",
"notes": "Prevents accidental deletion of the storage account."
}
}
Troubleshooting Common Issues
Issue | Solution |
---|
Invalid resource reference | Ensure resourceId() has correct parameters |
Circular dependency | Review dependsOn arrays and nested resource references |
Name resolution error | Check parameter references and name length limits |
Authorization failed | Verify deployment user has proper RBAC permissions |
Invalid template language expression | Check syntax of functions and expressions |
API version conflicts | Use consistent API versions for resource types |
Quota limits | Check subscription limits for resource types |
Lock conflicts | Remove locks before deployment or use incremental mode |
Nested deployment issues | Check parameter passing between templates |
Tools and Resources
Development Tools
- Visual Studio Code with Azure Resource Manager Tools extension
- Azure Resource Manager (ARM) Template Viewer
- ARM Template Toolkit
- What-If Operation: Preview changes before deployment
Template Examples and Gallery
ARM Template Testing
# Run ARM-TTK (ARM Template Test Toolkit)
Test-AzTemplate -TemplatePath .\azuredeploy.json
# Preview changes using What-If operation (CLI)
az deployment group what-if \
--resource-group MyResourceGroup \
--template-file azuredeploy.json \
--parameters storageAccountName=mystorageacct
# Preview changes using What-If operation (PowerShell)
Get-AzResourceGroupDeploymentWhatIfResult `
-ResourceGroupName "MyResourceGroup" `
-TemplateFile "azuredeploy.json" `
-storageAccountName "mystorageacct"
ARM Template Evolution
- Bicep: Domain-specific language for ARM templates
- Deployment Stacks: Manage collections of resource groups as a single unit
- Template Specs: Save templates as a resource type
Resources for Further Learning
This comprehensive Azure Resource Manager Templates cheat sheet provides a quick reference for creating and managing infrastructure as code in Azure. Use these patterns and best practices to streamline your deployment process and ensure consistent resource provisioning across your Azure environment.