Bicep abstraction for mixed-scope templates
- 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 setting up complex deployments that span multiple scopes it can become a bit messy when you need to create new Bicep-files (modules) in order to switch scope.
I'll go through a small simplified example of what I mean.
The following `main.bicep` is deployed to the subscription scope, it creates a resource group and a module:
```bicep
// main.bicep
targetScope = 'subscription'
param resourceGroupName string
param storageAccountName string
param location string
resource rg 'Microsoft.Resources/resourceGroups@2023-07-01' = {
name: resourceGroupName
location: location
}
module storage 'modules/storage.bicep' = {
scope: rg
name: 'storage-module'
params: {
location: location
storageAccountName: storageAccountName
}
}
```
The module in `modules/storage.bicep` is deployed to the resource group scope, it creates a storage account:
```bicep
// modules/storage.bicep
param storageAccountName string
param location string
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
}
```
This example only has one of these scope-jumps, but there could be more if you deploy at a higher scope.
_(**Note**: There could be other situations where you need to use a module, even though you might not like to do it. I don't have a simple example to illustrate this but it involves using values that are not known at compile-time, then you need to create a module and send the values (that are unknown at compile-time) into this module.)_
For the simple example above it would be convenient to have Bicep allow me to define everything in the same template, and then handle the creation of the required ARM-structure with sub-deployments for me underneath. I.e. Bicep should be able to abstract this for me.
**Describe the solution you'd like**
My idea is to instead create the example above as in the following `main.bicep`:
```bicep
// main.bicep
targetScope = 'subscription' // we still set the main scope, where we create the initial deployment
param resourceGroupName string
param storageAccountName string
param location string
resource rg 'Microsoft.Resources/resourceGroups@2023-07-01' = {
name: resourceGroupName
location: location
}
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
scope: rg
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
}
```
The new thing here is that I can set a `scope` on the storage account resource. If I run `az bicep build -f main.bicep` for this file I should end up with:
```json
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.23.1.45101",
"templateHash": "7528206918474232928"
}
},
"parameters": {
"resourceGroupName": {
"type": "string"
},
"storageAccountName": {
"type": "string"
},
"location": {
"type": "string"
}
},
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2023-07-01",
"name": "[parameters('resourceGroupName')]",
"location": "[parameters('location')]"
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2022-09-01",
"name": "storage-module",
"resourceGroup": "[parameters('resourceGroupName')]",
"properties": {
"expressionEvaluationOptions": {
"scope": "inner"
},
"mode": "Incremental",
"parameters": {
"location": {
"value": "[parameters('location')]"
},
"storageAccountName": {
"value": "[parameters('storageAccountName')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.23.1.45101",
"templateHash": "16210097254981877065"
}
},
"parameters": {
"storageAccountName": {
"type": "string"
},
"location": {
"type": "string"
}
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2"
}
]
}
},
"dependsOn": [
"[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]"
]
}
]
}
```
This is the same output that is generated for the current working solution presented above.
In this case Bicep has noticed that I am creating a mixed-scope deployment and created a nested deployment automatically.
I can't think of any obvious blockers for a feature like this, but please let me know if there are any!
Contributor guide
Assessment
This issue has not been assessed yet.