Azure / Azure/bicep

Support parameter projections in order to simplify "root" dictionary configurations

Open
#6,788 6 comments 0 reactions 0 assignees View on GitHub
enhancement Needs: Upvote
Dominant language
Bicep
Stars
3.6k
Forks
830
Avg merge
1d 21m
Merged PRs (30d)
79

Description

I have an elaborate dictionary variable that describes a number of different properties of some Service Fabric applications and containing services. This dictionary describes several values known ahead of time - what's the name and port of each web service (to pass into Application Gateway for rule building), but there are also several values of this dictionary I'd like to populate as the script deploys (pulled from various output values).

Ideally I'd like to put all this into the single array of dictionaries so it's all in a single place and so I'm not dealing with ensuring that indexes match across sets of arrays, but if I were to do this in Bicep today (constant values in the dictionary + values set as the output of future modules), then pass this object into the modules that will produce said outputs, I get a circular dependency error, as you'd expect.

Today, this looks similar to the `apps` variable in my example below, but everything that comes from an output is maintained in a separate variable that is created to match the indexes of the first, but created only after the dependencies have run to use their outputs. Then both arrays are passed into still downstream dependencies and the value pulled with `key: constantArr[index].value ?? outputArr[index].value`. This is a real pain to maintain and tweak.

But this is because Bicep isn't smart enough to look down the usage tree of this parameter and determine that it's not actually circular after-all, because I don't use the values of any of these keys that are set with outputs as part of that module. They just happen to be there and will be set later on.

**Describe the solution you'd like**
What I'd like to see is Bicep get smarter about understanding how parameters are passed through the module graph and at build, apply projections to narrow the values actually passed to only those that are in use. Ideally this would also indicate true circular dependencies at development time.

Then at development time, I can maintain my large array of dictionaries variable at a root location and keep everything in one place, but know that when it is built, circular dependencies have simply been removed (so they're only included in the projection when their values are both used and set downstream).

This is a contrived example of what I'd like to see (grossly simplified from what I'd actually implement, but gets the point across):

```bicep
// root.bicep
var apps = [
{
name: 'AppA'
type: 'AppAType'
targetDomain: 'example.com' //Because no downstream modules ever reference this, it should be grayed out as unused and never actually sent to any modules
storageContainerName: AzDeployments.outputs.containerName
}
//In practice this spans a whole pile of additional properties, both initially known and derived from outputs
]

module AzDeployments './deployment.bicep' = {
name: 'deployments'
params: {
Apps: apps
}
}

module SfDeployments './sfDeployment.bicep' = {
name: 'sf-deployments'
params: {
Apps: apps
}
dependsOn: [
AzDeployments //Not really necessary since the projector would already know about the dependency from the module reference in the value, but it gives me a great opportunity to place this comment
]
}
```

```bicep
// deployments.bicep
param Apps array //Because the only values used from this are $.name and $.type, these would be the only two values provided - because $.storageContainerName isn't included, there's no circular dependency

module DeployStorage './storage.bicep' = [for app in Apps: {
name: 'storage-${app.name}'
params: {
name: app.name
type: app.type
}
}]

outputs containerName string = DeployStorage.outputs.containerName
```

```bicep
// sfDeployment.bicep
param Apps array // And because we use the storageContainerName value here, it's included in this projection along with name and type

module Sf './sf.bicep' = [for app in Apps: {
name: 'sf-${app.name}
params: {
name: app.name
type: app.type
containerName: app.storageContainerName
}
}]
```

Contributor guide

Open the contributing guide

Research direction

Start with the root.bicep, deployment.bicep, sfDeployment.bicep, storage.bicep, and sf.bicep examples to trace how Apps values flow through the module graph. Determine how projections would retain only referenced fields while avoiding false circular dependencies; done means unused properties are excluded and genuine dependencies remain detectable.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.