Deploy multiple resources by using loops
Often, you need to deploy multiple resources that are very similar. By adding loops to your Bicep files, you can avoid having to repeat resource definitions. Instead, you can dynamically set the number of instances of a resource you want to deploy. You can even customize the properties for each instance.
For your toy company, you need to deploy back-end infrastructure, including some Azure SQL logical servers, to support the launch of the new smart teddy bear. You need to deploy a dedicated logical server to each country/region where the toy will be available, so that you're in compliance with each country/region's data-protection laws.
Apart from their locations, all logical servers will be configured in the same way. You want to use Bicep code to deploy your logical servers, and a parameter should allow you to specify the regions into which the logical servers should be deployed.
In this unit, you learn how to deploy multiple instances of resources by using copy loops.
Note
The commands in this unit are shown to illustrate concepts. Don't run the commands yet. You'll practice what you learn here soon.
Use copy loops
When you define a resource or a module in a Bicep template, you can use the for
keyword to create a loop. Place the for
keyword in the resource declaration, then specify how you want Bicep to identify each item in the loop. Typically, you loop over an array of objects to create multiple instances of a resource. The following example deploys multiple storage accounts, and their names are specified as parameter values:
param storageAccountNames array = [
'saauditus'
'saauditeurope'
'saauditapac'
]
resource storageAccountResources 'Microsoft.Storage/storageAccounts@2023-05-01' = [for storageAccountName in storageAccountNames: {
name: storageAccountName
location: resourceGroup().location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
}]
In this example, the loop iterates through each item in the storageAccountNames
array. Each time Bicep goes through the loop, it puts the current value into a special variable called storageAccountName
, and it's used as the value of the name
property. Notice that Bicep requires you put an opening bracket ([
) character before the for
keyword, and a closing bracket (]
) character after the resource definition.
If you deployed this Bicep file, you'd see that three storage accounts were created, with their names specified by the corresponding items in the storageAccountNames
array.
Loop based on a count
You might sometimes need to loop to create a specific number of resources, and not use an array as the source. Bicep provides the range()
function, which creates an array of numbers. For example, if you need to create four storage accounts called sa1
through sa4
, you could use a resource definition like this:
resource storageAccountResources 'Microsoft.Storage/storageAccounts@2023-05-01' = [for i in range(1,4): {
name: 'sa${i}'
location: resourceGroup().location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
}]
When you use the range()
function, you specify its start value and the number of values you want to create. For example, if you want to create storage accounts with the names sa0
, sa1
, and sa2
, you'd use the function range(0,3)
.
Note
When you use the range()
function, you provide two arguments. The first specifies the starting value, and the second tells Bicep the number of values you want. For example, if you use range(3,4)
then Bicep returns the values 3
, 4
, 5
, and 6
. Make sure you request the right number of values, especially when you use a starting value of 0.
Access the iteration index
With Bicep, you can iterate through arrays and retrieve the index of the current element in the array. For example, let's say you want to create a logical server in each location that's specified by an array, and you want the names of the servers to be sqlserver-1
, sqlserver-2
, and so on. You could achieve this by using the following Bicep code:
param locations array = [
'westeurope'
'eastus2'
'eastasia'
]
resource sqlServers 'Microsoft.Sql/servers@2023-08-01-preview' = [for (location, i) in locations: {
name: 'sqlserver-${i+1}'
location: location
properties: {
administratorLogin: administratorLogin
administratorLoginPassword: administratorLoginPassword
}
}]
Notice that the name
property includes the expression i+1
. The first value of the i
index variable is zero, so you need to add +1
to it if you want your server names to start with 1
.
Tip
In this example, we've named the index variable i
. This is the standard convention in Bicep. However, you can use any name you want.
Filter items with loops
In some situations, you might want to deploy resources by using copy loops combined with conditions. You can do this by combining the if
and for
keywords.
In the following example, the code uses an array parameter to define a set of logical servers. A condition is used with the copy loop to deploy the servers only when the environmentName
property of the loop object equals Production
:
param sqlServerDetails array = [
{
name: 'sqlserver-we'
location: 'westeurope'
environmentName: 'Production'
}
{
name: 'sqlserver-eus2'
location: 'eastus2'
environmentName: 'Development'
}
{
name: 'sqlserver-eas'
location: 'eastasia'
environmentName: 'Production'
}
]
resource sqlServers 'Microsoft.Sql/servers@2023-08-01-preview' = [for sqlServer in sqlServerDetails: if (sqlServer.environmentName == 'Production') {
name: sqlServer.name
location: sqlServer.location
properties: {
administratorLogin: administratorLogin
administratorLoginPassword: administratorLoginPassword
}
tags: {
environment: sqlServer.environmentName
}
}]
If you deployed the preceding example, you'd see two logical servers, sqlserver-we
and sqlserver-eas
, but not sqlserver-eus2
, because that object's environmentName
property doesn't match Production
.