Resource iteration in Bicep

This article shows you how to create more than one instance of a resource in your Bicep file. You can add a loop to a resource declaration and dynamically set the number of resources to deploy. You avoid repeating syntax in your Bicep file.

You can also use a loop with modules, properties, variables, and outputs.

If you need to specify whether a resource is deployed at all, see condition element.

Syntax

Loops can be used declare multiple resources by:

  • Using a loop index.

    resource <resource-symbolic-name> '<resource-type>@<api-version>' = [for <index> in range(<start>, <stop>): {
        <resource-properties>
    }]
    

    For more information, see Loop index.

  • Iterating over an array.

    resource <resource-symbolic-name> '<resource-type>@<api-version>' = [for <item> in <collection>: {
        <resource-properties>
    }]
    

    For more information, see Loop array.

  • Iterating over an array and index.

    resource <resource-symbolic-name> '<resource-type>@<api-version>' = [for (<item>, <index>) in <collection>: {
        <resource-properties>
    }]
    

    For more information, see Loop array and index.

Loop limits

Bicep loop has these limitations:

  • Can't loop a resource with nested child resources. You must change the child resources to top-level resources. See Iteration for a child resource.
  • Can't loop on multiple levels of properties. See Property iteration in Bicep.
  • Loop iterations can't be a negative number or exceed 800 iterations.

Loop index

The following example creates the number of storage accounts specified in the storageCount parameter.

param rgLocation string = resourceGroup().location
param storageCount int = 2

resource storageAcct 'Microsoft.Storage/storageAccounts@2021-02-01' = [for i in range(0, storageCount): {
  name: '${i}storage${uniqueString(resourceGroup().id)}'
  location: rgLocation
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'Storage'
}]

Notice the index i is used in creating the storage account resource name.

Loop array

The following example creates one storage account for each name provided in the storageNames parameter.

param rgLocation string = resourceGroup().location
param storageNames array = [
  'contoso'
  'fabrikam'
  'coho'
]

resource storageAcct 'Microsoft.Storage/storageAccounts@2021-02-01' = [for name in storageNames: {
  name: '${name}${uniqueString(resourceGroup().id)}'
  location: rgLocation
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'Storage'
}]

If you want to return values from the deployed resources, you can use a loop in the output section.

Loop array and index

The following example uses both the array element and index value when defining the storage account.

param storageAccountNamePrefix string

var storageConfigurations = [
  {
    suffix: 'local'
    sku: 'Standard_LRS'
  }
  {
    suffix: 'geo'
    sku: 'Standard_GRS'
  }
]

resource storageAccountResources 'Microsoft.Storage/storageAccounts@2021-02-01' = [for (config, i) in storageConfigurations: {
  name: '${storageAccountNamePrefix}${config.suffix}${i}'
  location: resourceGroup().location
  properties: {
    supportsHttpsTrafficOnly: true
    accessTier: 'Hot'
    encryption: {
      keySource: 'Microsoft.Storage'
      services: {
        blob: {
          enabled: true
        }
        file: {
          enabled: true
        }
      }
    }
  }
  kind: 'StorageV2'
  sku: {
    name: config.sku
  }
}]

Resource iteration with condition

The following example shows a nested loop combined with a filtered resource loop. Filters must be expressions that evaluate to a boolean value.

resource parentResources 'Microsoft.Example/examples@2020-06-06' = [for parent in parents: if(parent.enabled) {
  name: parent.name
  properties: {
    children: [for child in parent.children: {
      name: child.name
      setting: child.settingValue
    }]
  }
}]

Filters are also supported with module loops.

Deploy in batches

By default, Resource Manager creates resources in parallel. When you use a loop to create multiple instances of a resource type, those instances are all deployed at the same time. The order in which they're created isn't guaranteed. There's no limit to the number of resources deployed in parallel, other than the total limit of 800 resources in the Bicep file.

You might not want to update all instances of a resource type at the same time. For example, when updating a production environment, you may want to stagger the updates so only a certain number are updated at any one time. You can specify that a subset of the instances be batched together and deployed at the same time. The other instances wait for that batch to complete.

To serially deploy instances of a resource, add the batchSize decorator. Set its value to the number of instances to deploy concurrently. A dependency is created on earlier instances in the loop, so it doesn't start one batch until the previous batch completes.

param rgLocation string = resourceGroup().location

@batchSize(2)
resource storageAcct 'Microsoft.Storage/storageAccounts@2021-02-01' = [for i in range(0, 4): {
  name: '${i}storage${uniqueString(resourceGroup().id)}'
  location: rgLocation
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'Storage'
}]

For purely sequential deployment, set the batch size to 1.

The batchSize decorator is in the sys namespace. If you need to differentiate this decorator from another item with the same name, preface the decorator with sys: @sys.batchSize(2)

Iteration for a child resource

You can't use a loop for a nested child resource. To create more than one instance of a child resource, change the child resource to a top-level resource.

For example, suppose you typically define a file service and file share as nested resources for a storage account.

resource stg 'Microsoft.Storage/storageAccounts@2021-02-01' = {
  name: 'examplestorage'
  location: resourceGroup().location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
  resource service 'fileServices' = {
    name: 'default'
    resource share 'shares' = {
      name: 'exampleshare'
    }
  }
}

To create more than one file share, move it outside of the storage account. You define the relationship with the parent resource through the parent property.

The following example shows how to create a storage account, file service, and more than one file share:

resource stg 'Microsoft.Storage/storageAccounts@2021-02-01' = {
  name: 'examplestorage'
  location: resourceGroup().location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

resource service 'Microsoft.Storage/storageAccounts/fileServices@2021-02-01' = {
  name: 'default'
  parent: stg
}

resource share 'Microsoft.Storage/storageAccounts/fileServices/shares@2021-02-01' = [for i in range(0, 3): {
  name: 'exampleshare${i}'
  parent: service
}]

Next steps