Azure / Azure/bicep

Using module outputs in variables used for loops

Open
#12,543 2 comments 4 reactions 1 assignee Claimed by @alex-frankel View on GitHub
enhancement Needs: Upvote
Dominant language
Bicep
Stars
3.6k
Forks
830
Avg merge
1d 2h
Merged PRs (30d)
79

Description

**Is your feature request related to a problem? Please describe.**
I spin up a bunch of resources in a Bicep deployment and would like to store the names of the resources and some of their children in an application configuration instance. Because I need a lot of the secrets associated with these resources, I save them to a Key Vault instance and would like to save those URLs as Key Vault references in that aforementioned app config instance. In other words, while there are some values I could know in advance and build out in an array, this isn't true of all values - some of them can only come from module outputs.

But I do know in advance just which values it is I want to put in the configuration store, so I'd like to build out an array of each of these and their module output values that I can pass into my separate module for populating these key/value pairs.

Here's where this becomes a problem. I would like to loop through this array to pass into the module, but I run headfirst into BCP178:
`This expression is being used in the for-expression, which requires a value that can be calculated at the start of the deployment. You are referencing a variable which cannot be calculated at the start ("myAppConfigurationSettings" -> referenced resource name). Properties of which can be calculated at the start include "name".`

In other words, this is not allowed:

```bicep
var adxAppConfigurationSettings = [
{
key: 'Settings:DataExplorer'
value: '{"ServiceUri": "https://${DataExplorer.outputs.ResourceName}.${LocationLookupValue}.kusto.windows.net", "UaiClientId": "${UAI.outputs.ClientId}"}'
contentType: 'application/json'
}
]

module DataExplorerAppConfigStore './resources/azureAppConfiguration/appConfiguration-keyValuePair.bicep' = [for (setting, index) in adxAppConfigurationSettings: {
name: 'appconfiguration-setting-dataexplorer'
params: {
AppConfigurationStoreName: AzureAppConfigurationStorageName
Key: setting.key
Value: setting.value
ContentType: setting.contentType
}
}]
```

**Describe the solution you'd like**
This doesn't make sense because while that isn't allowed, this is:

root.bicep
```bicep
var adxAppConfigurationSettings = [
{
key: 'Settings:DataExplorer'
value: '{"ServiceUri": "https://${DataExplorer.outputs.ResourceName}.${LocationLookupValue}.kusto.windows.net", "UaiClientId": "${UAI.outputs.ClientId}"}'
contentType: 'application/json'
}
]

module DataexplorerAppConfigStore './resources/azureAppConfiguration/appConfiguration-keyValuePairs.bicep' = {
name: 'appconfiguration-setting-dataexplorer'
params: {
AppConfigurationStoreName: AzureAppConfigurationStorageName
Settings: adxAppConfigurationSettings
}
}
```

types.bicep
```bicep
@export()
type AppConfigurationSetting = {
key: string
value: string
contentType: string
}
```

appConfiguration-keyValuePairs.bicep
```bicep
import * as types from '../../shared/types.bicep'

@description('The name of the application configuration store')
param AppConfigurationStoreName string

@description('The various settings to save in the configuration store')
param Settings types.AppConfigurationSetting[]

resource AppConfigStore 'Microsoft.AppConfiguration/configurationStores@2023-03-01' existing = {
name: AppConfigurationStoreName
}

resource ConfigurationKvPair 'Microsoft.AppConfiguration/configurationStores/keyValues@2023-03-01' = [for (kvp, index) in Settings: {
name: kvp.key
parent: AppConfigStore
properties: {
contentType: kvp.contentType
value: kvp.value
}
}]

```

In the latter version, the variable is passed into a module which has implicit dependencies on prior modules so it wouldn't be resolved until those dependencies are resolved. But in the former one, the loop doesn't do the same dependency check before assigning values, instead requiring that the values be constant and known upfront without any dependency requirements.

I've maintained that the Bicep DSL should be a tool that simplifies things as much as possible for the developers without necessarily putting load on the ARM team (who I'm sure also has an impressive backlog). As such, I propose the following:

Whenever Bicep sees a scenario that might result in a BCP178 error, it attempt to abstract out the loop to a module created on the fly (as in the second example above) and then do the same loop within that module. In short, the latter is fully understood and allowed by both Bicep and ARM without changes - this change requires mild code generation on Bicep's part, but would fully eliminate this error and remove one potential stumbling block for future developers implementing loops.

Yes, there's a workaround, so this isn't a priority, but it's an inconvenient workaround and not one that's necessarily obvious to new Bicep developers as to why it works and the former lop doesn't, detailed error message notwithstanding.

I appreciate the consideration!

This isn't quite a duplicate of #5009 or #5020 as this doesn't reference existing resource values so much as the outputs of modules.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.