Azure / bicep

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

Set or skip an object property based on a condition #15451

Open anthony-c-martin opened 1 week ago

anthony-c-martin commented 1 week ago

Problem statement

This issue exists generally to track the requirement of being able to conditionally set a property on an object. This is often necessary, because although when authoring in Bicep we don't distinguish between the absence of a value and explicitly setting a value to null, some of the services which handle resource deployment (resource providers) do.

For example, it might be desirable to conditionally not set a property:

param setFooProp bool

resource foo '<type>' = {
  name: 'foo'
  properties: {
    fooProp: setFooProp ? 'bar' : null
    barProp: 'baz'
  }
}

However, if setFooProp is false, this is equivalent to writing:

resource foo '<type>' = {
  name: 'foo'
  properties: {
    fooProp: null
    barProp: 'baz'
  }
}

Whereas the author may instead intend to have the following instead:

resource foo '<type>' = {
  name: 'foo'
  properties: {
    barProp: 'baz'
  }
}

To accomplish the latter option, a more significant refactor is needed. We support this with the spread operator:

resource foo '<type>' = {
  name: 'foo'
  properties: {
    ...(setFooProp ? { fooProp: 'bar' } : {})
    barProp: 'baz'
  }
}

Or by refactoring into a variable:

var fooProps = setFooProp ? {
  fooProp: 'bar'
} : {}

resource foo '<type>' = {
  name: 'foo'
  properties: {
    ...fooProps
    barProp: 'baz'
  }
}

Why is this a new issue?

The requirement for "set-or-skip" was originally being tracked by #387, which is now over 4 years old, and is one of our highest-upvoted issue with many comments. Since then, we've implemented the spread operator (usage docs).

The requirements of the original issue are satisfied with the spread operator, but we wanted to continue to collect feedback about set-or-skip, because of the popularity of the thread. We are having a hard time prioritizing this work because we're not sure whether:

As such we have closed #387 but intend to keep this issue open to track new feedback. Please comment on this issue if you'd like to help us answer the above!

Special-case syntax

Under #387, the original discussion was mostly around introducing a new dedicated syntax - here is a prototype of the syntax example for "set-or-skip" syntax that was being discussed. This is not implemented, and is not something we are currently considering without clear feedback supporting it:

var obj = {
 propNormal: value
 if (condition) {
   propConditional1: propConditional1Value
   propConditional2: propConditional2Value
 }
}

var arr = [
  element
  if (condition) { condElement1, condElement2 }
]
odegroot commented 1 week ago

So what is the goal of this issue - just to collect feedback?

My 2 cents:

Maybe the bicep spread docs can be modified to more explicitly showcase the "conditionally set property on object" / "conditionally add element to array" usage pattern? I.e. make it more discoverable via internet search (SEO). https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/operator-spread

Maybe add a note to the docs pages for if and ternary ? :, mentioning that you cannot use them for these use cases, that you need to use ... spread instead?

(After discovering the "set or skip" unresolved issue, I settled on a union() with a ternary ? : instead.)

anthony-c-martin commented 1 week ago

@odegroot - thank you for the detailed feedback! I've created #15499 to cover your comments on documentation, and have reworded the original issue text to try and explain the purpose more clearly.