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.

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

ConceptDetail
Incremental modeDefault; adds/updates resources; doesn't delete extras
Complete modeDeletes resources in RG not in template — use with care
existing keywordReference an already-deployed resource
@secure()Hides param in logs; required for passwords/keys
uniqueString()Deterministic 13-char hash; same input = same output
Bicep vs ARMBicep transpiles to ARM JSON; same deployment engine
ModulesReusable Bicep files; scope can differ from parent
What-ifShows adds/modifies/deletes before deploying

More in Microsoft Azure