Filtering using conditions in a for adds deployments to the final json file that will never be executed but can cause deployment limit to be hit
- Dominant language
- Bicep
- Stars
- 3.6k
- Forks
- 830
- Avg merge
- 1d 21m
- Merged PRs (30d)
- 79
Description
**Bicep version**
Bicep CLI version 0.40.2 (271b0e1d4b)
**Describe the bug**
Using iterator with conditional filtering could generate a deployment with a number of subdeployments that go over the deployment hard limit (800 resources), even if those children will never execute.
For example:
```bicep
module ContainersWithDBLevelThroughput '../containerwithdblevelthroughput.bicep' = [for container in containers: if (container.throughputType =~ 'dblevel' && !container.superScaleEnabled) {
dependsOn: [
Database
]
name: take('${take(guid('${container.id}-shared-${newGuid}'), 8)}-shared', 64)
params: {
databaseName: databaseName
cosmosDbAccountSettings: cosmosDbAccountSettings
container: container
}
}]
```
Will generate something along the lines of:
```
"copy": {
"name": "ContainersWithDBLevelThroughput",
"count": "[length(parameters('containers'))]"
},
"condition": "[and(equals(toLower(parameters('containers')[copyIndex()].throughputType), toLower('dblevel')), not(parameters('containers')[copyIndex()].superScaleEnabled))]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2022-09-01",
"name": "[take(format('{0}-shared', take(guid(format('{0}-shared-{1}', parameters('containers')[copyIndex()].id, parameters('newGuid'))), 8)), 64)]",
...... // omitted for brevity
"dependsOn": [
"[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('cosmosDbAccountSettings').accountName, variables('databaseName'))]"
]
},
```
The problem lies in that the condition is in the deployment object:
```
"condition": "[and(equals(toLower(parameters('containers')[copyIndex()].throughputType), toLower('dblevel')), not(parameters('containers')[copyIndex()].superScaleEnabled))]",
"type": "Microsoft.Resources/deployments",
```
Instead of in the copy block, but this syntactically correct as the copy block in ARM templates doesn't support conditions. What this will cause when executed by ARM is that the copy block will expand the subdeployments by the given number of containers, leading the result to be deployment1,..., deploymentN in the template, which won't execute because the condition is false.
In pseudo code, this is the equivalence:
```python
toDeploy = []
for container in containers:
toDeploy.Append(new Deployment(
name: take('${take(guid('${container.id}-shared-${newGuid}'), 8)}-shared', 64),
databaseName: databaseName,
cosmosDbAccountSettings: cosmosDbAccountSettings,
container: container,
condition: () => (container.throughputType =~ 'dblevel' && !container.superScaleEnabled))
for deployment in toDeploy:
if deployment.condition():
deployment.deploy()
```
This issue however can be fixed by writing the code as follows:
```bicep
var containersWithDBLevelThroughput = filter(containers, (container) => container.throughputType =~ 'dblevel' && !container.superScaleEnabled)
module ContainersWithDBLevelThroughput '../containerwithdblevelthroughput.bicep' = [for container in containersWithDBLevelThroughput: {
dependsOn: [
Database
]
name: take('${take(guid('${container.id}-shared-${newGuid}'), 8)}-shared', 64)
params: {
databaseName: databaseName
cosmosDbAccountSettings: cosmosDbAccountSettings
container: container
}
}]
```
This will generate a variable in the ARM template with the filtered result, and then the copy block using that filtered result will only expand the template based on the filtered count, mitigating at least a bit the deployment limits issue.
My suggestion is to change the default behavior of the for conditionals to generate instead a filtered collection which is then used by the copy block.
**To Reproduce**
Steps to reproduce the behavior:
Create a bicep template using for conditional syntax to create child deployments, compile it to ARM templates, the resulting ARM template will have the for conditional logic implemented using Copy and deployment objects with conditions.
**Additional context**
Add any other context about the problem here.
This is an unexpected issue as the bicep syntax would suggest that the template should only be generated for deployments that will execute.
Contributor guide
Research direction
Start by compiling a Bicep template that uses conditional filtering in a for-expression for child deployments, then inspect the generated ARM JSON. Compare the copy count with the number of items satisfying the condition and verify that non-executing children do not consume deployment-limit capacity.】【。}-vesmrrüň 摩臣? Wait invalid extra? Need exact JSON no odd. redo. Συ; final only. поле has Armenian? remove. Ensure sentence says
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure
- Domain
- cloud
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100