Azure Bicep & ARM Templates — Cheat Sheet
IntermediateCheat Sheet15 min5 min read20 Jan 2025Azure
Bicep and ARM template syntax quick reference — parameters, variables, resources, modules, outputs, functions, and CLI deployment commands.
Prerequisites
Bicep Basics
// main.bicep — minimal example
param location string = resourceGroup().location
param storageAccountName string
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
accessTier: 'Hot'
supportsHttpsTrafficOnly: true
}
}
output storageAccountId string = storageAccount.id
output blobEndpoint string = storageAccount.properties.primaryEndpoints.blob
Parameters
// Simple param with default
param location string = 'eastus'
// Required param (no default)
param adminUsername string
// Param with allowed values
@allowed(['Standard_LRS', 'Standard_GRS', 'Premium_LRS'])
param skuName string = 'Standard_LRS'
// Param with constraints
@minLength(3)
@maxLength(24)
@description('Storage account name (3-24 chars, lowercase letters and numbers only)')
param storageAccountName string
// Secure param (not logged, not stored in history)
@secure()
param adminPassword string
// Object param
param tags object = {
environment: 'production'
owner: 'ops-team'
}
// Array param
param allowedIPs array = ['10.0.0.0/16', '192.168.1.0/24']
Variables
var storagePrefix = 'myapp'
var uniqueSuffix = uniqueString(resourceGroup().id)
var storageAccountName = '${storagePrefix}${uniqueSuffix}'
// Computed from params
var isProduction = environment == 'production'
var skuSize = isProduction ? 'Standard_GRS' : 'Standard_LRS'
// Object aggregation
var commonTags = union(tags, {
deployedBy: 'Bicep'
})
Resources
// Resource declaration
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'mystorageacct'
location: resourceGroup().location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}
// Child resource (blob service inside storage account)
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
parent: storageAccount // preferred: parent reference
name: 'default'
properties: {
deleteRetentionPolicy: {
enabled: true
days: 7
}
}
}
// Conditional resource
resource devStorageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = if (environment == 'dev') {
name: 'devstorageacct'
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}
// Existing resource (reference without deploying)
resource existingVNet 'Microsoft.Network/virtualNetworks@2023-05-01' existing = {
name: 'myExistingVNet'
scope: resourceGroup('otherRG') // can cross resource groups
}
// Reference existing resource property
var subnetId = existingVNet.properties.subnets[0].id
Loops (for)
// Create multiple storage accounts
param storageNames array = ['storageA', 'storageB', 'storageC']
resource storageAccounts 'Microsoft.Storage/storageAccounts@2023-01-01' = [for name in storageNames: {
name: name
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}]
// Loop with index
resource subnets 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' = [for (subnet, i) in subnets: {
parent: vnet
name: subnet.name
properties: {
addressPrefix: '10.0.${i}.0/24'
}
}]
// Output array from loop
output storageIds array = [for i in range(0, length(storageNames)): storageAccounts[i].id]
Modules
// storageModule.bicep (child module)
param name string
param location string = resourceGroup().location
resource sa 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: name
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}
output id string = sa.id
// main.bicep — consuming a module
module storage './storageModule.bicep' = {
name: 'storageDeployment'
params: {
name: 'mystorageacct'
location: location
}
}
// Reference module output
output storageId string = storage.outputs.id
Built-in Functions
// String functions
var lower = toLower('MyString') // 'mystring'
var upper = toUpper('MyString') // 'MYSTRING'
var trimmed = trim(' hello ') // 'hello'
var replaced = replace('hello world', 'world', 'Azure') // 'hello Azure'
var joined = '${prefix}-${suffix}' // string interpolation
var split = split('a,b,c', ',') // ['a','b','c']
var contains = contains('hello', 'ell') // true
// Resource functions
var rgName = resourceGroup().name
var rgLocation = resourceGroup().location
var subId = subscription().subscriptionId
var tenantId = tenant().tenantId
// Unique string (deterministic hash)
var uniqueName = uniqueString(resourceGroup().id)
var storageAccountName = 'sa${uniqueString(resourceGroup().id)}'
// ARM resource ID
var vnetId = resourceId('Microsoft.Network/virtualNetworks', 'myVNet')
var subnetId = resourceId('Microsoft.Network/virtualNetworks/subnets', 'myVNet', 'mySubnet')
// Array & object functions
var merged = union({a: 1}, {b: 2}) // {a: 1, b: 2}
var arr = concat([1,2], [3,4]) // [1,2,3,4]
var len = length(['a','b','c']) // 3
var range = range(0, 5) // [0,1,2,3,4]
var empty = empty([]) // true
Outputs
output storageAccountName string = storageAccount.name
output storageAccountId string = storageAccount.id
output blobEndpoint string = storageAccount.properties.primaryEndpoints.blob
// Sensitive output
@description('Storage account key — treat as secret')
output storageKey string = storageAccount.listKeys().keys[0].value
// Object output
output storageInfo object = {
id: storageAccount.id
name: storageAccount.name
endpoint: storageAccount.properties.primaryEndpoints.blob
}
CLI Deployment Commands
# Validate template
az deployment group validate \
--resource-group myRG \
--template-file main.bicep \
--parameters @params.json
# What-if (preview changes)
az deployment group what-if \
--resource-group myRG \
--template-file main.bicep \
--parameters @params.json
# Deploy to resource group
az deployment group create \
--resource-group myRG \
--template-file main.bicep \
--parameters @params.json \
--name myDeployment
# Deploy with inline params
az deployment group create \
--resource-group myRG \
--template-file main.bicep \
--parameters storageAccountName=mystorageacct adminUsername=azureuser
# Deploy to subscription level
az deployment sub create \
--location eastus \
--template-file sub-template.bicep
# Deploy to management group
az deployment mg create \
--management-group-id myMG \
--location eastus \
--template-file mg-template.bicep
# Get deployment outputs
az deployment group show \
--resource-group myRG \
--name myDeployment \
--query properties.outputs
# List deployments
az deployment group list --resource-group myRG --output table
# Delete deployment (not resources)
az deployment group delete --resource-group myRG --name myDeployment
Bicep Build & Decompile
# Build Bicep → ARM JSON
az bicep build --file main.bicep
# Decompile ARM JSON → Bicep
az bicep decompile --file template.json
# Install/update Bicep CLI
az bicep install
az bicep upgrade
az bicep version
Parameters File
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"value": "mystorageacct"
},
"adminPassword": {
"reference": {
"keyVault": {
"id": "/subscriptions/<sub>/resourceGroups/myRG/providers/Microsoft.KeyVault/vaults/myKV"
},
"secretName": "adminPassword"
}
}
}
}
Key Facts for AZ-104
| Concept | Detail |
|---|---|
| Incremental mode | Default; adds/updates resources; doesn't delete extras |
| Complete mode | Deletes resources in RG not in template — use with care |
existing keyword | Reference an already-deployed resource |
@secure() | Hides param in logs; required for passwords/keys |
uniqueString() | Deterministic 13-char hash; same input = same output |
| Bicep vs ARM | Bicep transpiles to ARM JSON; same deployment engine |
| Modules | Reusable Bicep files; scope can differ from parent |
| What-if | Shows adds/modifies/deletes before deploying |
