Infrastructure as code gives consistent, repeatable deployments for Dev/QA/Prod, fewer manual steps, code review, reuse and modules.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageName": { "type": "string", "minLength": 3, "maxLength": 24 },
"adminPassword": { "type": "securestring" }
},
"variables": { "skuName": "Standard_LRS" },
"resources": [{
"type": "Microsoft.Storage/storageAccounts", "apiVersion": "2023-05-01",
"name": "[parameters('storageName')]", "location": "[resourceGroup().location]",
"sku": { "name": "[variables('skuName')]" }, "kind": "StorageV2"
}],
"outputs": { "storageId": { "type": "string", "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]" } }
}
| Block | Role |
|---|---|
parameters |
Values passed in at deploy time (--parameters k=v or @file.json) |
variables |
Values computed inside the template |
resources |
Resources to create |
outputs |
Values returned after the deployment |
Secrets use securestring (hidden from logs and deployment history), not string.
flowchart LR B["main.bicep"] -- "az bicep build" --> J["main.json (ARM)"] Q["QuickStart template<br/>(GitHub)"] --> J J --> W["az deployment group what-if"] J --> D["az deployment group create"] D --> RG["rg-devops-lab"]
- The default mode is
Incremental(keeps resources not in the template);Completedeletes RG resources that are not in the template. - QuickStart templates (repo
Azure/azure-quickstart-templates) are community samples to use as-is or as a starting point. - Bicep: a shorter syntax;
az deployment group createaccepts.bicepfiles directly.
| Command | Meaning |
|---|---|
az deployment group what-if -g rg-devops-lab -f azuredeploy.json -p storageName=stdevopslab01 |
Preview the changes |
az deployment group create -g rg-devops-lab -f azuredeploy.json -p storageName=stdevopslab01 |
Deploy |
az group delete -n rg-devops-lab --yes --no-wait |
Clean up the lab |
The same storage account in Bicep
param storageName string
param location string = resourceGroup().location
resource st 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: storageName
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}
output storageId string = st.id
Bicep compiles to the same ARM JSON, but it infers dependencies from references (here st.id), supports modules, and is much shorter. Microsoft recommends Bicep for new work.
Useful template features
| Feature | Purpose |
|---|---|
uniqueString(resourceGroup().id) |
Deterministic suffix for globally unique names |
dependsOn |
Explicit order in JSON when no reference links two resources |
Parameter file (azuredeploy.parameters.json or .bicepparam) |
Keep per-environment values out of the template |
copy loop |
Create several similar resources |
condition |
Deploy a resource only when an expression is true |
Deployment scopes
Templates can target more than a resource group: az deployment sub create deploys at subscription scope (for example to create resource groups or policy assignments), and az deployment mg create and az deployment tenant create go higher still. A subscription deployment needs --location to store its metadata.
Reuse and lifecycle
- Template specs store a versioned template in Azure so teams deploy approved designs without copying files.
- Deployment stacks manage a group of resources as one unit and can delete or lock resources that are removed from the template.
- The portal's Export template turns existing resources into a starting point, though the output usually needs cleanup.
Each deployment is recorded in the resource group's deployment history, which keeps the most recent 800 entries.