Type system reflection error
Bicep version Bicep CLI version 0.7.4 (5afc312467)
Describe the bug I use modules from the CARML and my own and they all use the following to generate managed identity objects to be used in the template:
@description('Optional. Enables system assigned managed identity on the resource.')
param systemAssignedIdentity bool = false
@description('Optional. The ID(s) to assign to the resource.')
param userAssignedIdentities object = {}
...
var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None')
var identity = identityType != 'None' ? {
type: identityType
userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null
} : null
If I have a submodule where I just want to hand over the generated identity object instead of copying the same logic again, I get the following error:
module xyz 'xyz' = [for element in elements: {
name: element.name
params: {
...
identity: !empty(identity) ? identity : {}
...
}
}]
// Error:
var identity: null | object
The property "identity" expected a value of type "object" but the provided value is of type "null | object | object".bicep(BCP036)
That error is correct, but when I update the statement to reflect the null state:
identity: ((identity != null) && !empty(identity)) ? identity : {}
It still throws the same error: The property "identity" expected a value of type "object" but the provided value is of type "null | object | object".bicep(BCP036)
To Reproduce Use the above code

@itpropro
If you are looking for a way to make this scenario work to pass to the Module, you can use an alternate default than null.
@description('Optional. Enables system assigned managed identity on the resource.')
param systemAssignedIdentity bool = false
@description('Optional. The ID(s) to assign to the resource.')
param userAssignedIdentities object = {}
var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None')
var identity = identityType != 'None' ? {
type: identityType
userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null
} : {} // <-- default to empty object instead
module xyz 'xyx.bicep' = {
name: 'myModule01'
params: {
identity: identity
location: resourceGroup().location
}
}
Then inside of the Module, have the default, plus if it's empty convert it back to null.
xyx.bicep
param identity object = {}
@description('Required. Name of the static site.')
@minLength(1)
@maxLength(40)
param name string = 'mystaticTest54'
@allowed([
'Free'
'Standard'
])
@description('Optional. Type of static site to deploy.')
param sku string = 'Free'
@description('Location to deploy static site. The following locations are supported: CentralUS, EastUS2, EastAsia, WestEurope, WestUS2.')
param location string
resource staticSite 'Microsoft.Web/staticSites@2021-03-01' = {
name: name
location: location
identity: identity == {} ? null : identity
sku: {
name: sku
tier: sku
}
properties: {}
}
Thanks @brwilkinson for the suggestion, I already use that workaround at many points.
But mostly it's just a workaround for missing features/bugs in the reflection system.
With the statement identity: ((identity != null) && !empty(identity)) ? identity : {}, the Bicep compiler should definitely know that the value can't be null and thereby adjusting it internal type representation, as e.g. TypeScript would do.
The particular scenario outlined will now emit a warning rather than an error, and there are a couple of ways to manually suppress it. You can use a non-null assertion to tell the compiler it's wrong and that the value won't be null:
module xyz 'xyz' = [for element in elements: {
name: element.name
params: {
...
identity: !empty(identity) ? identity! : {}
...
}
}]
or you can disable the type mismatch diagnostic altogether (NB: #disable-next-line will only work on warnings, not errors):
module xyz 'xyz' = [for element in elements: {
name: element.name
params: {
...
#disable-next-line BCP321
identity: !empty(identity) ? identity : {}
...
}
}]
The compiler could infer that identity won't be null from context, but this is not yet implemented. (tracked in #12121)