Bicep | Conditional Iterative Deployment
Background
I have recently been looking at creating multiple of the same resource using Bicep. There is however a condition where I would wish for the set of resources not to be deployed. The following stages show my work through of this particular problem (using a storage account resource as an example):
Conditional Deployment
Conditional Deployment is used where you may or may not wish to deploy a given resource depending on the outcome of a given condition (if statement).
/**********************************
Bicep Template: Conditional Storage
***********************************/
targetScope = 'resourceGroup'
// ** Parameters **
// ****************
param name string
param location string
param deployStorage bool
// ** Variables **
// ***************
// ** Resources **
// ***************
resource storageAccountDeployment 'Microsoft.Storage/storageAccounts@2021-02-01' = if (deployStorage) {
name: name
location: location
kind: 'StorageV2'
sku: {
name: 'Premium_LRS'
}
}
// ** Outputs **
// *************
In the instance shown above, if the deployStorage
parameter is True
then the storageAccountDeployment will be deployed, if False
then the storageAccountDeployment will not be deployed. As an ARM template the instance above will appear as follows:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"name": {
"type": "string"
},
"location": {
"type": "string"
},
"deployStorage": {
"type": "bool"
}
},
"resources": [
{
"condition": "[parameters('deployStorage')]",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "[parameters('name')]",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "Premium_LRS"
}
}
]
}
Looping Resources
Iterative loops in Bicep utilise the For
syntax as shown below:
/**********************************
Bicep Template: Iterative Storage
***********************************/
targetScope = 'resourceGroup'
// ** Parameters **
// ****************
param location string
param storageAccounts array
// ** Variables **
// ***************
// ** Resources **
// ***************
resource storageAccountDeployment 'Microsoft.Storage/storageAccounts@2021-02-01' = [for storage in storageAccounts: {
name: storage.name
location: location
kind: 'StorageV2'
sku: {
name: 'Premium_LRS'
}
}]
// ** Outputs **
// *************
The syntax above will deploy a number of storage accounts based on the number of storage items in the parameter array. In ARM this appears as follows:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"name": {
"type": "string"
},
"location": {
"type": "string"
},
"storageAccounts": {
"type": "array"
}
},
"resources": [
{
"copy": {
"name": "storageaccountDeployment",
"count": "[length(parameters('storageAccounts'))]"
},
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "[parameters('name')]",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "Premium_LRS"
}
}
]
}
Conditional Looping Resources
Iterative Conditional Deployments are used in the instance where you would like to create multiple instances of a resource, but on each iteration you would like to check if the resource should be deployed based on a condition. Such as:
/********************************************
Bicep Template: Iterative Conditional Storage
*********************************************/
targetScope = 'resourceGroup'
// ** Parameters **
// ****************
param location string
param storageAccounts array = [
{
name: 'st1'
kind: 'StorageV2'
}
]
// ** Variables **
// ***************
// ** Resources **
// ***************
resource storageAccountDeployment 'Microsoft.Storage/storageAccounts@2021-02-01' = [for storage in storageAccounts: if(storage.kind != 'BlobStorage') {
name: storage.name
location: location
kind: storage.kind
sku: {
name: 'Premium_LRS'
}
}]
// ** Outputs **
// *************
The example above will loop through each storage item in the storageAccounts
array. At each point of iteration, the conditional is evaluated. In this example only deployment of an item will occur if it is not a 'BlobStorage'
account type. The ARM for this example appears as follows:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string"
},
"storageAccounts": {
"type": "array",
"defaultValue": [
{
"name": "st1",
"kind": "StorageV2"
}
]
}
},
"resources": [
{
"copy": {
"name": "storageAccountDeployment",
"count": "[length(parameters('storageAccounts'))]"
},
"condition": "[not(equals(parameters('storageAccounts')[copyIndex()].kind, 'BlobStorage'))]",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "[parameters('storageAccounts')[copyIndex()].name]",
"location": "[parameters('location')]",
"kind": "[parameters('storageAccounts')[copyIndex()].kind]",
"sku": {
"name": "Premium_LRS"
}
}
]
}
Problem Space
The previous case conditionally deploys each resource in the looped array
. Although this appears to be what we are looking for, it’s not. We would like to conditionally loop through the array
which is a step higher than what is provided to us.
At this point, we are not limited by the Bicep language rather that ARM conditionals and loops do not support this behaviour.
Solution | Conditional Module with Looping Resource
To solve this problem, we need to rely on Bicep Modules or in ARM this is nested resource deployments. Essentially we need to split the problem into two. The calling template has the conditional module whilst the nested template (module) handles the looping of storage account creation. For example:
Calling Template:
/********************************************
Bicep Template: Conditional Storage Module
*********************************************/
targetScope = 'resourceGroup'
// ** Parameters **
// ****************
param location string
param StorageDeploy bool
param storageAccounts array = [
{
name: 'st1'
kind: 'StorageV2'
}
]
// ** Variables **
// ***************
// ** Resources **
// ***************
module storageDeploy 'storageDeploy.bicep' = if (StorageDeploy) {
name: 'storageDeploy'
params:{
location: location
storageAccounts: storageAccounts
}
}
// ** Outputs **
// *************
Nested Template (Module)
/*********************************
Bicep Template: Iterative Storage
**********************************/
targetScope = 'resourceGroup'
// ** Parameters **
// ****************
param location string = resourceGroup().location
param storageAccounts array
// ** Variables **
// ***************
// ** Resources **
// ***************
resource storageAccountDeployment 'Microsoft.Storage/storageAccounts@2021-02-01' = [for storage in storageAccounts: {
name: storage.name
location: location
kind: storage.kind
sku: {
name: 'Premium_LRS'
}
}]
// ** Outputs **
// *************
ARM representation
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string"
},
"StorageDeploy": {
"type": "bool"
},
"storageAccounts": {
"type": "array",
"defaultValue": [
{
"name": "st1",
"kind": "StorageV2"
}
]
}
},
"resources": [
{
"condition": "[parameters('StorageDeploy')]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2022-09-01",
"name": "storageDeploy",
"properties": {
"expressionEvaluationOptions": {
"scope": "inner"
},
"mode": "Incremental",
"parameters": {
"location": {
"value": "[parameters('location')]"
},
"storageAccounts": {
"value": "[parameters('storageAccounts')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"storageAccounts": {
"type": "array"
}
},
"resources": [
{
"copy": {
"name": "storageAccountDeployment",
"count": "[length(parameters('storageAccounts'))]"
},
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "[parameters('storageAccounts')[copyIndex()].name]",
"location": "[parameters('location')]",
"kind": "[parameters('storageAccounts')[copyIndex()].kind]",
"sku": {
"name": "Premium_LRS"
}
}
]
}
}
}
]
}
We have now constructed a template (or two) which will only conduct looping of the resource deployment if we have said so through our StorageDeploy
module conditional parameter.
For the original version of this post see Andrew Wilson's personal blog at Bicep | Conditional Iterative Deployment