Azure / Azure/bicep

Improve loop code generation when using a complex expression as loop source

Open
#12,960 0 comments 0 reactions 0 assignees View on GitHub
enhancement good first issue
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.