Bicep Essentials: Looping Like a Pro in Bicep: Deploying Dynamic Arrays of Azure Resources

Loops in Bicep can feel intimidating at first, especially when you’re trying to dynamically deploy several Azure resources—like multiple subnets, virtual machines, or storage accounts. But once you grasp the basics, they unlock huge flexibility and help you avoid repetitive code.

In this post, we’ll look at how to loop through arrays in Bicep, create indexed names, and define interdependent resources using the for syntax.

Why Use Loops in Bicep?

Azure environments often follow repeatable patterns: a cluster of VMs, subnets for different tiers, or multiple storage containers. Rather than copy-pasting blocks of code, Bicep lets you define a resource once and repeat it across an array.

Benefits:

  • Fewer lines of code
  • Less chance of mistakes
  • Better maintainability

Basic Loop Syntax

Let’s start with a simple example: creating 3 virtual networks.


param location string = resourceGroup().location

var vnets = [
  {
    name: 'vnet-core'
    cidr: '10.10.0.0/16'
  }
  {
    name: 'vnet-app'
    cidr: '10.20.0.0/16'
  }
  {
    name: 'vnet-db'
    cidr: '10.30.0.0/16'
  }
]

resource vnetResources 'Microsoft.Network/virtualNetworks@2023-02-01' = [for v in vnets: {
  name: v.name
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        v.cidr
      ]
    }
  }
}]

Each item in the vnetNames array is used to deploy a separate VNet.

Indexed Names in Loops

Want your resource names to be numbered (like vm1, vm2, vm3)? Use an indexed loop:


param location string = resourceGroup().location
param vmCount int = 3

resource vms 'Microsoft.Compute/virtualMachines@2023-03-01' = [for i in range(1, vmCount): {
  name: 'vm-${i}'
  location: location
  properties: {
    hardwareProfile: {
      vmSize: 'Standard_B1s'
    }
    osProfile: {
      computerName: 'vm-${i}'
      adminUsername: 'azureuser'
      adminPassword: 'SecureP@ssw0rd!'
    }
    ...
}]

Using range(start, count) creates a numbered index array starting at start with count items.

Looping with Dependencies

If you’re deploying subnets into VNets, you might need one resource to depend on another. Bicep manages this via automatic dependency chaining, but you can still be explicit using dependsOn.


param location string = resourceGroup().location

resource vnet 'Microsoft.Network/virtualNetworks@2023-02-01' = {
  name: 'vnet-looped'
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.1.0.0/16'
      ]
    }
  }
}

var subnetNames = [
  'subnet-web'
  'subnet-app'
  'subnet-db'
]

resource subnets 'Microsoft.Network/virtualNetworks/subnets@2023-02-01' = [for name in subnetNames: {
  name: name
  parent: vnet
  properties: {
    addressPrefix: '10.1.${indexOf(subnetNames, name)}.0/24'
  }
}]

Each subnet uses its index in the array to get a unique CIDR block. indexOf(array, value) retrieves the current loop index.

Tips for Mastering Loops

  • Use range() for numbered loops
  • Use indexOf() for working with the array index of a value
  • Parent-child relationships (like subnets inside VNets) work naturally via parent: keyword
  • Use modules to abstract looped deployments for even cleaner structure

Loops make your Bicep code scalable, repeatable, and DRY (Don’t Repeat Yourself). Once you get the hang of the for syntax and range functions, you’ll rarely need to copy-paste resource blocks again.

This entry was posted in Azure, Bicep and tagged , . Bookmark the permalink.