Bicep Loops

Loops in Bicep create multiple copies of a resource from a single declaration. Without loops, deploying five storage accounts means writing five separate resource blocks — 50 lines that look almost identical. With loops, the same result comes from a single resource block and an array of values.

Think of loops like a stamp. Instead of painting every brick on a wall individually, a brick stamp presses the same pattern repeatedly across the surface. Bicep loops stamp out multiple resources from one template.

The for Loop – Core Syntax

Bicep uses a for expression inside square brackets to loop over an array and produce multiple resources.

resource <symbolicName> '<ResourceType>@<ApiVersion>' = [for <item> in <array>: {
  name: '<name using item>'
  location: '<region>'
  <properties>
}]

The resource block is wrapped in square brackets [ ]. The for item in array expression iterates over each element in the array, making its value available as item inside the block.

Example 1 – Loop Over a String Array

param location string = 'eastus'

var storageNames = [
  'storagedev001'
  'storagestaging001'
  'storageprod001'
]

// Create three storage accounts — one per name in the array
resource storageAccounts 'Microsoft.Storage/storageAccounts@2023-01-01' = [for name in storageNames: {
  name: name
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}]
Loop Execution Diagram

storageNames = ['storagedev001', 'storagestaging001', 'storageprod001']
                      │                  │                    │
             ┌────────┘          ┌───────┘           ┌───────┘
             ▼                   ▼                    ▼
     storageAccounts[0]  storageAccounts[1]  storageAccounts[2]
     name: storagedev001  name: storagestaging001  name: storageprod001

Example 2 – Loop with Index Using range()

The range(start, count) function generates a sequence of integers. This is useful when creating numbered resources.

param location string = 'eastus'
param instanceCount int = 3

// Create 3 storage accounts numbered 001, 002, 003
resource storageAccounts 'Microsoft.Storage/storageAccounts@2023-01-01' = [for i in range(0, instanceCount): {
  name: 'storage${padLeft(i + 1, 3, '0')}'
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}]
range(0, 3) produces: [0, 1, 2]

i=0 → name: 'storage001'
i=1 → name: 'storage002'
i=2 → name: 'storage003'

Example 3 – Loop Over an Array of Objects

Looping over an array of objects is the most powerful and common pattern. Each object in the array holds all the properties needed for one resource.

param location string = 'eastus'

var environments = [
  {
    name: 'dev'
    sku: 'Standard_LRS'
    httpsOnly: false
  }
  {
    name: 'staging'
    sku: 'Standard_LRS'
    httpsOnly: true
  }
  {
    name: 'prod'
    sku: 'Standard_GRS'
    httpsOnly: true
  }
]

resource storageAccounts 'Microsoft.Storage/storageAccounts@2023-01-01' = [for env in environments: {
  name: 'storage${env.name}001'
  location: location
  sku: {
    name: env.sku
  }
  kind: 'StorageV2'
  properties: {
    supportsHttpsTrafficOnly: env.httpsOnly
  }
}]
Object Array Loop

environments[0]: { name:'dev',     sku:'Standard_LRS', httpsOnly:false }
                         │                  │                  │
                         ▼                  ▼                  ▼
                 name:'storagedev001'  Standard_LRS       false

environments[1]: { name:'staging', sku:'Standard_LRS', httpsOnly:true  }
                         │                  │                  │
                         ▼                  ▼                  ▼
               name:'storagestaging001' Standard_LRS       true

environments[2]: { name:'prod',    sku:'Standard_GRS', httpsOnly:true  }
                         │                  │                  │
                         ▼                  ▼                  ▼
               name:'storageprod001'  Standard_GRS        true

Accessing Loop Results – Symbolic Name with Index

Resources created by a loop form an array. Access individual items using the symbolic name with a numeric index.

// Access the first storage account from the loop
var firstStorageId = storageAccounts[0].id

// Access the third storage account's name
var thirdStorageName = storageAccounts[2].name

Loop Outputs – Collecting Values from All Resources

Outputs from looped resources return arrays of values, one per resource created.

param location string = 'eastus'

var regionList = ['eastus', 'westus', 'centralus']

resource storageAccounts 'Microsoft.Storage/storageAccounts@2023-01-01' = [for region in regionList: {
  name: 'storage${uniqueString(region)}'
  location: region
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}]

// Output an array of all storage account IDs
output storageAccountIds array = [for i in range(0, length(regionList)): storageAccounts[i].id]

// Output an array of all storage account names
output storageAccountNames array = [for i in range(0, length(regionList)): storageAccounts[i].name]

Nested Loops – Loop Inside a Loop

Bicep supports loops inside resource property arrays. This is useful for creating resources with multiple sub-items like subnets inside a virtual network.

param location string = 'eastus'

var subnetConfig = [
  {
    name: 'frontend-subnet'
    addressPrefix: '10.0.1.0/24'
  }
  {
    name: 'backend-subnet'
    addressPrefix: '10.0.2.0/24'
  }
  {
    name: 'database-subnet'
    addressPrefix: '10.0.3.0/24'
  }
]

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-09-01' = {
  name: 'vnet-myapp'
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/16'
      ]
    }
    // Loop inside the subnets array property
    subnets: [for subnet in subnetConfig: {
      name: subnet.name
      properties: {
        addressPrefix: subnet.addressPrefix
      }
    }]
  }
}
Virtual Network Structure After Deployment

vnet-myapp (10.0.0.0/16)
├── frontend-subnet  (10.0.1.0/24)
├── backend-subnet   (10.0.2.0/24)
└── database-subnet  (10.0.3.0/24)

Loop with Condition – Filtering Items

A condition can combine with a loop to selectively create resources from an array.

param location string = 'eastus'
param environment string = 'prod'

var storageList = [
  { name: 'storageapp',  deployToProd: true  }
  { name: 'storagelog',  deployToProd: true  }
  { name: 'storagetest', deployToProd: false }
]

// Deploy only items where deployToProd is true (when in prod)
resource storageAccounts 'Microsoft.Storage/storageAccounts@2023-01-01' = [for store in storageList: if (environment != 'prod' || store.deployToProd) {
  name: '${store.name}${environment}'
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}]

Loop Variable Naming – Using Index with Item

Use for item, index in array when both the item value and its position in the array are needed.

var appNames = ['frontend', 'backend', 'admin']

resource appServicePlans 'Microsoft.Web/serverfarms@2023-01-01' = [for (appName, index) in appNames: {
  name: 'asp-${appName}-${index}'   // index: 0, 1, 2
  location: 'eastus'
  sku: {
    name: 'B1'
  }
}]
Result:
asp-frontend-0
asp-backend-1
asp-admin-2

Complete Example – Looping to Create a Multi-Region Deployment

param appName string = 'globalapp'

var regions = [
  { location: 'eastus',      suffix: 'eus' }
  { location: 'westeurope',  suffix: 'weu' }
  { location: 'southeastasia', suffix: 'sea' }
]

var commonTags = {
  application: appName
  managedBy: 'Bicep'
}

// Create an App Service Plan in each region
resource appServicePlans 'Microsoft.Web/serverfarms@2023-01-01' = [for region in regions: {
  name: 'asp-${appName}-${region.suffix}'
  location: region.location
  tags: commonTags
  sku: {
    name: 'B1'
  }
}]

// Create a Web App in each region, linked to the local App Service Plan
resource webApps 'Microsoft.Web/sites@2023-01-01' = [for (region, i) in regions: {
  name: 'app-${appName}-${region.suffix}'
  location: region.location
  tags: commonTags
  kind: 'app'
  properties: {
    serverFarmId: appServicePlans[i].id    // link to same-region plan
  }
}]

// Output all web app hostnames
output webAppHostnames array = [for i in range(0, length(regions)): webApps[i].properties.defaultHostName]

Summary

Loops in Bicep use the for item in array syntax to create multiple resource instances from a single declaration. Loops work with string arrays, integer sequences from range(), and arrays of objects. Results from a loop form an array accessible by index. Loops combine with conditions to filter which items deploy. They also work inside resource property arrays for sub-resources like subnets. Mastering loops eliminates repetitive resource blocks and enables scalable, data-driven templates.

Leave a Comment