`loadCompiledBicep()` function
- Dominant language
- Bicep
- Stars
- 3.6k
- Forks
- 830
- Avg merge
- 1d 21m
- Merged PRs (30d)
- 79
Description
# Problem
There are several resource types in Azure that consume ARM template JSON in order to perform a deployment at a later time:
* DeployIfNotExists policy or initiative definitions (`Microsoft.Authorization/policyDefinitions` and `Microsoft.Authorization/policySetDefinitions`)
* Template Specs (`Microsoft.Resources/templateSpecs/versions`)
* etc.
Declaring such resources in Bicep can be accomplished but it requires either specifying the template as a Bicep-ified JSON or using the `loadJsonContent()` function to inject the JSON contents into the resource.
In the former case, the user has to manually convert the JSON into a Bicep equivalent or use paste as Bicep. Regardless of how the conversion is done, the ARM tools extension will not help with the authoring of the template and the Bicep-ified JSON content has readability and maintainability issues.
In the latter case, the `loadJsonContent()` function allows the inner template to exist as a separate file, which allows any tooling such as the ARM Tools extensions to be leveraged.
If the user wants to use Bicep in the inner template, the user has to sequence the build so that the template is compiled into JSON first using either a script, CI/CD, or manually running the command (and optionally checking in the resulting JSON file into the repo).
## Non-Goals
Azure Policy supports expressions that are evaluated in a deferred fashion w.r.t. the ARM deployment engine. Enhancing the experience with such expressions in Bicep is out of scope of this proposal. (We do need to ensure that there are no blockers for the proposed end-to-end experience, however.)
## loadJsonContent()
```bicep
targetScope = 'subscription' // or MG
resource policy 'Microsoft.Authorization/policyDefinitions@2021-06-01' = {
name: 'policy'
properties: {
// this is not a complete policy definition
parameters: {
myPolicyParam: {
type: 'string'
}
}
policyRule: {
if: {
// ...
}
then: {
effect: 'DeployIfNotExists'
details: {
// ...
deployment: {
properties: {
mode: 'incremental'
template: loadJsonContent('path-to-template.json')
parameters: {
// policy parameters are assigned to deployment parameters here
myTemplateParam: {
value: '[parameters(\'myPolicyParam\')]'
}
templateLocationParam: {
value: '[field(\'location\')]'
}
}
}
}
}
}
}
}
}
```
## Bicep-ified JSON
```bicep
targetScope = 'subscription' // or MG
resource policy 'Microsoft.Authorization/policyDefinitions@2021-06-01' = {
name: 'policy'
properties: {
// this is not a complete policy definition
parameters: {
myPolicyParam: {
type: 'string'
}
}
policyRule: {
if: {
// ...
}
then: {
effect: 'DeployIfNotExists'
details: {
// ...
deployment: {
properties: {
mode: 'incremental'
template: {
// Bicep-ified ARM template starts here
'$schema': 'https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#'
contentVersion: '1.0.0.0'
parameters: {
// ...
}
resources: [
// ...
]
}
parameters: {
// policy parameters are assigned to deployment parameters here
myTemplateParam: {
value: '[parameters(\'myPolicyParam\')]'
}
templateLocationParam: {
value: '[field(\'location\')]'
}
}
}
}
}
}
}
}
}
```
# Proposal
We can make the experience of deploying resources that accept an ARM Template JSON over the wire much more seamless for the users. I'm proposing a new compile-time function called `loadCompiledBicep()`. The function would be used as follows:
```bicep
targetScope = 'subscription' // or MG
resource policy 'Microsoft.Authorization/policyDefinitions@2021-06-01' = {
name: 'policy'
properties: {
// this is not a complete policy definition
parameters: {
myPolicyParam: {
type: 'string'
}
}
policyRule: {
if: {
// ...
}
then: {
effect: 'DeployIfNotExists'
details: {
// ...
deployment: {
properties: {
mode: 'incremental'
template: loadCompiledBicep('path-to-bicep.bicep')
parameters: {
// policy parameters are assigned to deployment parameters here
myTemplateParam: {
value: '[parameters(\'myPolicyParam\')]'
}
templateLocationParam: {
value: '[field(\'location\')]'
}
}
}
}
}
}
}
}
}
```
## Semantics
The new `loadCompiledBicep()` function would be a compile-time function that accepts a single parameter with the relative path to a Bicep file. When the containing Bicep file is built, the return value of the function would be the ARM Template JSON to which the Bicep file compiled if there were no errors. In cases of errors in the file referenced by the `loadCompiledBicep()` function, the compilation of the containing Bicep file would fail (exactly like our module semantics).
Additional notes:
* The file would be compiled in-memory, so no JSON file would be emitted to the file system.
* User can put in cycles using this function, so we need to add this to our cycle detection.
* Go to def should work for this function just like for all other `load*()` functions and open the file that is referenced.
## Naming Considerations
Considered calling this `compileBicep()` but stuck to the `load*()` naming convention to match the other functions.
Contributor guide
Assessment
This issue has not been assessed yet.