Improve loop code generation when using a complex expression as loop source
- Dominant language
- Bicep
- Stars
- 3.6k
- Forks
- 830
- Avg merge
- 1d 21m
- Merged PRs (30d)
- 79
Description
**Is your feature request related to a problem? Please describe.**
When using any expression more complex than a simple variable access as the source of a loop of resources, the compiled ARM template can become incomprehensible whenever the loop variable is used, and can cause unnecessarily large template size.
The source of the loop values is copied verbatim everywhere the loop variable is used, then indexed with `[copyIndex()]`, e.g.
```bicep
param values { type: 'a' | 'b', location: string }[]
resource a 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' = [for v in filter(values, v => v.type == 'a'): {
name: guid(v.type, v.location)
location: v.location
}]
```
becomes
```json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"languageVersion": "2.0",
"contentVersion": "1.0.0.0",
"metadata": { ... },
"parameters": {
"values": { ... }
},
"resources": {
"a": {
"copy": {
"name": "a",
"count": "[length(filter(parameters('values'), lambda('v', equals(lambdaVariables('v').type, 'a'))))]"
},
"type": "Microsoft.ServiceBus/namespaces",
"apiVersion": "2022-10-01-preview",
"name": "[guid(filter(parameters('values'), lambda('v', equals(lambdaVariables('v').type, 'a')))[copyIndex()].type, filter(parameters('values'), lambda('v', equals(lambdaVariables('v').type, 'a')))[copyIndex()].location)]",
"location": "[filter(parameters('values'), lambda('v', equals(lambdaVariables('v').type, 'a')))[copyIndex()].location]"
}
}
}
```
The loop input `filter(parameters('values'), lambda('v', equals(lambdaVariables('v').type, 'a')))` gets inlined everywhere - making it difficult to evaluate the template for humans, increases compiled ARM template size, and (I assume) causes some extra work for the ARM backend having to recalculate the expression every time it is used.
**Describe the solution you'd like**
When the source for a loop is not a simple variable access, and in situations where it's possible, the loop source could be hoisted into a generated variable, simplifying the contents of the loop.
e.g.
```json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"languageVersion": "2.0",
"contentVersion": "1.0.0.0",
"metadata": { ... },
"parameters": {
"values": { ... }
},
"variables": {
"$loopVar#0": "[filter(parameters('values'), lambda('v', equals(lambdaVariables('v').type, 'a')))]"
},
"resources": {
"a": {
"copy": {
"name": "a",
"count": "[length(variables('$loopVar#0'))]"
},
"type": "Microsoft.ServiceBus/namespaces",
"apiVersion": "2022-10-01-preview",
"name": "[guid(variables('$loopVar#0')[copyIndex()].type, variables('$loopVar#0')[copyIndex()].location)]",
"location": "[variables('$loopVar#0')[copyIndex()].location]"
}
}
}
```
Contributor guide
Research direction
No source files or tests are named in the issue. Start by reproducing the Bicep example and inspecting the compiled ARM JSON; done means complex loop sources are hoisted where possible, loop references use the generated variable, and repeated source expressions are avoided.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure
- Domain
- cloud, infrastructure
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100