Azure / bicep

Bicep is a declarative language for describing and deploying Azure resources
MIT License
3.21k stars 746 forks source link

`bicep(BCP338)` when dynamically building inputs in `.bicepparam` files to reduce duplication #14722

Open DrEsteban opened 1 month ago

DrEsteban commented 1 month ago

Bicep version Bicep CLI version 0.29.47 (132ade51bc)

Describe the bug To reduce duplication, I attempted to dynamically build a parameter in a .bicepparam file using vars, [for]s, and array functions which appears to be valid according to documentation. However it resulted in error:

Failed to evaluate parameter "<foo>": Unhandled exception during evaluating template language function 'variables' is not handled. bicep(BCP338)

To Reproduce deploy.bicep

@export()
type FleetConfig = {
  namePrefix: string
  sku: string
  capacity: int
  clusteringPolicy: ClusteringPolicy
}

param testMatrix FleetConfig[]

output testMatrix

deploy.bicepparams

using './deploy.bicep'

var matrix = [
  {
    namePrefix: 'e10impactx4'
    sku: 'Enterprise_E10'
    capacity: 4
  }
  {
    namePrefix: 'e10impact'
    sku: 'Enterprise_E10'
    capacity: 2
  }
  {
    namePrefix: 'e5impactx4'
    sku: 'Enterprise_E5'
    capacity: 4
  }
  {
    namePrefix: 'e5impact'
    sku: 'Enterprise_E5'
    capacity: 2
  }
]
var type1 = [for item in matrix: {
  namePrefix: item.namePrefix
  sku: item.sku
  capacity: item.capacity
  clusteringPolicy: 'EnterpriseCluster'
}]
var type2 = [for item in matrix: {
  namePrefix: '${item.namePrefix}-ent'
  sku: item.sku
  capacity: item.capacity
  clusteringPolicy: 'OSSCluster'
}]
param testMatrix = concat(type1, type2) // <- doesn't work

Additional context Basically, I wanted to build a test matrix input where all items had the same properties except for clusteringPolicy. I tried restructuring the above lots of ways, but none of them worked 😢 In the end I had to manually copy/paste/update.

Note, though, that the following does work:

var matrix = [
  {
    namePrefix: 'e10impactx4'
    sku: 'Enterprise_E10'
    capacity: 4
  }
  {
    namePrefix: 'e10impact'
    sku: 'Enterprise_E10'
    capacity: 2
  }
  {
    namePrefix: 'e5impactx4'
    sku: 'Enterprise_E5'
    capacity: 4
  }
  {
    namePrefix: 'e5impact'
    sku: 'Enterprise_E5'
    capacity: 2
  }
]

param textMatrix = concat(matrix, matrix)

So it doesn't seem to be a fundamental limitation of .bicepparam files. (The ability to concatenate arrays.) The system just doesn't like when I try to dynamically build the different sub-arrays with a [for] loop.

anthony-c-martin commented 1 month ago

FWIW you could probably use the map function instead as a workaround:

using './deploy.bicep'

var matrix = [
  {
    namePrefix: 'e10impactx4'
    sku: 'Enterprise_E10'
    capacity: 4
  }
  {
    namePrefix: 'e10impact'
    sku: 'Enterprise_E10'
    capacity: 2
  }
  {
    namePrefix: 'e5impactx4'
    sku: 'Enterprise_E5'
    capacity: 4
  }
  {
    namePrefix: 'e5impact'
    sku: 'Enterprise_E5'
    capacity: 2
  }
]
var type1 = map(matrix, item => {
  namePrefix: item.namePrefix
  sku: item.sku
  capacity: item.capacity
  clusteringPolicy: 'EnterpriseCluster'
})
var type2 = map(matrix, item => {
  namePrefix: '${item.namePrefix}-ent'
  sku: item.sku
  capacity: item.capacity
  clusteringPolicy: 'OSSCluster'
})

param testMatrix = concat(type1, type2)
DrEsteban commented 1 month ago

That worked!! Thanks @anthony-c-martin! I'll be keeping this option in mind in the future.

Do you agree my original approach should be valid, intuitively? Or at least have a better error message to direct the user?

anthony-c-martin commented 1 month ago

Do you agree my original approach should be valid, intuitively? Or at least have a better error message to direct the user?

Yes, completely agree that intuitively it should work - we either need to fix the underlying problem, or improve the error message. I just wanted to unblock you with a workaround while we investigate further!