adaptive.run TECH BLOG

Cloud can be tricky sometimes. Find out what scenarios we've ran into that are worth being mentioned and explained.

Mastering Loops in Azure Bicep 

Level: 200
Publishing date: 07-Mar-2025
Author: Catalin Popa


Azure Bicep’s looping functionality allows for cleaner, more efficient infrastructure deployments. By using loops, you can dynamically deploy multiple resources, iterate over arrays, and simplify parameterized configurations.

In this guide, we’ll cover:

✔ Basic looping syntax and structure

✔ Using loops for resources, modules, and outputs

✔ Filtering loops with conditions

✔ Using loop indices for structured naming

✔ Nested loops for complex deployments

✔ Batch size control for sequential execution


1. Basic Loops: Deploying Multiple Resources

The for-loop expression iterates over arrays, allowing multiple resources to be created dynamically.

Example: Creating Multiple Log Analytics Workspaces

Bicep

var logAnalyticsWorkspaces = [
{
name: 'monitoring-east'
location: 'eastus'
}
{
name: 'monitoring-west'
location: 'westus'
}
]

resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-06-01' = [for workspace in logAnalyticsWorkspaces: {
name: workspace.name
location: workspace.location
properties: {}
}] 

✔ Deploys multiple Log Analytics Workspaces dynamically based on array values.
✔ No need to duplicate resource definitions for each region.

2. Using Loops in Modules

Modules can also take advantage of loops, allowing dynamic deployments while keeping the main Bicep template clean and modular.

Example: Deploying Multiple Application Gateways via a Module

Bicep

var gateways = [
{
name: 'app-gateway-east'
location: 'eastus'
}
{
name: 'app-gateway-west'
location: 'westus'
  }
]

module appGateway 'modules/applicationGateway.bicep' = [for gateway in gateways: {
name: '${gateway.name}-deploy'
params: {
gatewayName: gateway.name
location: gateway.location
   }
}] 

✔ Modules enable clean separation of infrastructure components.
✔ The loop automates multiple deployments without cluttering the main template.

3. Filtering Loops: Conditional Deployments
Loops can be filtered using the if statement to deploy resources only if they meet specific conditions.

Example: Only Deploying Gateways Marked as Enabled

Bicep

var gateways = [
{
name: 'app-gateway-east'
location: 'eastus'
enabled: true
}
{
name: 'app-gateway-west'
location: 'westus'
enabled: false
  }
]

resource applicationGateways 'Microsoft.Network/applicationGateways@2021-06-01' = [for gateway in gateways: if (gateway.enabled) {
name: gateway.name
location: gateway.location
properties: {}
}] 

✔ Excludes resources that are not marked as enabled.
✔ Reduces unnecessary deployments, saving costs and resources.

4. Using Loop Indices for Dynamic Naming

The range() function allows for structured naming and indexed resource creation.

Example: Creating Indexed Public IP Addresses

Bicep

resource publicIPs 'Microsoft.Network/publicIPAddresses@2021-06-01' = [for i in range(1, 4): {
name: 'public-ip-${i}'
location: resourceGroup().location
properties: {
publicIPAllocationMethod: 'Static'
   }
}] 

✔ Generates sequentially numbered public IPs without manually defining them.

5. Nested Loops: Deploying Resources with Sub-Resources

Example: Deploying Storage Accounts with Containers

Bicep

var storageAccounts = [
{
name: 'storageeast'
location: 'eastus'
containers: [
{ name: 'logs' }
{ name: 'data' }
  ]
}
{
name: 'storagewest'
location: 'westus'
containers: [
{ name: 'backups' }
    ]
  }
]

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = [for account in storageAccounts: {
name: account.name
location: account.location
properties: {}

resource containers 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = [for container in account.containers: {
name: container.name
   }]
}] 

✔ Deploys multiple storage accounts, each with a set of containers.
✔ Uses nested loops to associate containers with the correct storage account.

6. Batch Size: Controlling Deployment Order

By default, resources in loops deploy in parallel, but you can control the execution order with @batchSize.

Example: Deploying Databases in Batches of 2

Bicep

@batchSize(2)
resource sqlDatabases 'Microsoft.Sql/servers/databases@2021-05-01' = [for i in range(1, 6): {
name: 'sqldb-${i}'
location: resourceGroup().location
properties: {
collation: 'SQL_Latin1_General_CP1_CI_AS'
}
}] 

✔ Deploys two databases at a time, preventing resource throttling.

7. Looping Outputs
Looping can also be used to collect and return resource information dynamically.

Example: Returning Deployed Storage Account IDs

Bicep

output storageAccountIds array = [for (account, i) in storageAccounts: {
name: storageAccount[i].name
id: storageAccount[i].id
}] 

✔ Generates an array of storage account IDs for use in other automation workflows.

8. Using Loops for Variables

Loops can be used to construct structured variable arrays dynamically.

Example: Generating a List of VM Names

Bicep

var vmPrefixes = ['web', 'api', 'db']

var vmNames = [for (prefix, i) in vmPrefixes: {
id: i+1
fullName: '${prefix}-vm-${i+1}'
}] 

✔ Creates a structured list of virtual machine names with an ID field.

Conclusion

Looping in Azure Bicep enables dynamic, flexible infrastructure deployments while keeping templates concise and readable.

Key Takeaways:

✔ Loops simplify bulk deployments of resources, modules, and outputs.
✔ Filtering conditions reduce unnecessary deployments.
✔ Nested loops allow complex relationships between parent and child resources.
✔ Batch size control helps optimize execution order.
✔ Loops work in variables, outputs, and resource definitions.

By leveraging loops in Azure Bicep, you can make your infrastructure more scalable, modular, and manageable. 🚀


Mobirise
adaptive.run

Transform your business.
Run adaptive.

Contact

Phone: +40 73 523 0005
Email: hello@adaptive.run

Mobirise Website Builder
Mobirise Website Builder

© Copyright  2019-2025 adaptive.run- All Rights Reserved