Description
Proposal - Better validation for conditional resources/modules
Problem statement
Dealing with conditional resources/modules today can be error prone as you must remember to propagate conditions correctly, and the type system will not alert you to this being done incorrectly - leading to deploy-time errors.
In the following sample, there are no Bicep errors although the output statement may be invalid if someCond
is false and the resource does not exist.
param someCond bool
resource resWithCond 'Microsoft.Compute/virtualMachines@2021-03-01' existing = if (someCond) {
name: 'abc'
}
output myValue string = resWithCond.properties.licenseType
To fix this, the output statement needs to be changed to the following, to propagate the conditional check:
output myValue string = someCond ? resWithCond.properties.licenseType : ''
I've mentioned this idea on a few other issues, but thought it would be a good idea to write it up formally for tracking.
Potential Solutions
Note - these solutions all build on-top of each other - 3 depends on 1 & 2, and 2 depends on 1.
1. Treat conditional references as resource | null
In the first example, if resWithCond
has type resource | null
, accessing properties
directly will raise an error, so the user will be forced to handle the null-ness and write something like the following:
output myValue string = someCond ? resWithCond.properties.licenseType : ''
There are still problems with this approach - it is a little verbose, and Bicep needs to validate that the condition in the ternary is exactly the same as the condition on the resource.
2. Introduce 'null-conditional' operator .?
This builds upon the previous solution, but also adds a new operator which removes the need to propate the someCond
condition - so the type error would be fixable with:
output myValue string = resWithCond?.properties.licenseType ?? ''
In the generated JSON output, Bicep would generate a ternary if()
statement, bringing the condition for the nullable reference forward:
[if(<someCond>, <resWithCond reference statement>, '')]
This is essentially syntactic sugar for 1., but means that lengthy conditions don't need to be copy-pasted.
3. Going deeper with conditional flow analysis
Again, this builds on 1. & 2., giving Bicep the ability to skip null-checks if the condition has already been checked:
param someCond bool
resource resWithCond 'Microsoft.Compute/virtualMachines@2021-03-01' existing = if (someCond) {
name: 'abc'
}
resource someOtherRes 'My.Rp/madeUpResource@2021-03-01' = if (someCond) {
name: 'someOtherRes '
properties: {
// the .? and ?? may be skipped because we're inside a block which already has a `if (someCond)` check on it
someProp: resWithCond.properties.licenseType
}
}
output myObj object = someCond ? {
// again, we don't need to handle nullability here because we've already checked for someCond
someProp: resWithCond.properties.licenseType
} : {}
output myOtherObj object = {
// we do need to handle nullability here, because we haven't already checked for someCond
someProp: resWithCond?.properties.licenseType ?? ''
}
Other Notes
- Any solution for stacking conditions will need to be robust to handle binary bool operations - e.g. inside a block which checks
someCond && otherCond
- our flow analysis will need to understand that a nullable reference gated onsomeCond
can be used safely, and inside a block which checkssomeCond || otherCond
it cannot be. - A solution will also need to be robust enough to perform analysis on assignment through variables - e.g.
var combinedCond = someCond && otherCond
Metadata
Metadata
Assignees
Labels
Type
Projects
Status