Azure / bicep

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

optional enforcement of User Defined Type 'Allowed' (support suggested values/open enums) #10432

Open dciborow opened 1 year ago

dciborow commented 1 year ago

Is your feature request related to a problem? Please describe.

Users should be able to pass in values even if their are not 'allowed'.

The allowed has many benefits, such as improving the user experience when writing a Bicep template. But there are cases where all of the allowed values cannot be known. It may be that some allowed values are secrets that can't be initially shared. (I ran into this issue for GameDev VM, where we had to remove our allowed values to let some users try "win11-unreal-engine").

vm.bicep

This module is an example, and is not configurable by the user. It could be in a public registry.

@allowed['win10']
param os string

main.bicep

This module is written by the end-user, who is provided a option which has been made to them before the module was updated.

module vm 'br:bicep-module-registry:vm@1.0.0' = {
  name: 'myVM'
  params: {
    // Surpress Error
    os: 'win11'
  }
}

This will help to enable advanced use cases for user-defined types, like a Location type which is difficult to fully capture in a allowed Values field, but could be created that at least covers a majority of use cases.

Describe the solution you'd like

I should be able to deploy the above example. This should still initially raise errors, and the user should have to perform some action to suppress this. This should then produce a warning. The warning should explain that the error is being suppressed, and that the user is trying to use a value that is not allowed. That could also be suppressed by the user.

This could be done entirely in bicep, where the error suppression is detected and casts instances of os to simple strings without any restrictions on the values.

The main.json would either no longer contain the allowedValues, or they would be commented out explaining they have been suppressed.

main.json

    "os": {
      "type": "string"/*, (start comment here in case its last item in object)
      If we could simply comment this out in the final json
      and explain that this allowedValue is being suppressed, that its ideal, other drop this section)
      "allowedValues": [
        "new",
        "existing"
      ]*/, 
      "metadata": {
        "description": "Create new or use existing Public IP resource"
      }
    },
dciborow commented 1 year ago

@jeskew fyi

jeskew commented 1 year ago

Azure resource types can distinguish between 'open' and 'closed' enums, so the Bicep type system would be able to handle this change pretty easily. For example, in a Microsoft.KeyVault/vaults's sku property, family is an 'open' enum (in that it will autocomplete the known value of 'A' but will also permit any string), and name is a 'closed' enum (in that only 'standard' or 'premium' is accepted; all other strings are considered a type mismatch).

The Bicep syntax for this should be pretty straightforward. We can either allow non-literal members in unions:

param os 'win10' | string

or have a special decorator:

@openEnum()
param os 'win10'

The bigger question would be how to represent this in an ARM template. An open enum by definition won't have any runtime validation behavior, so it should probably be metadata, e.g.,

{
  "os": {
    "type": "string",
    "metadata": {
      "description": "Create new or use existing Public IP resource",
      "_bicep_autocomplete_suggestions": [
        "new",
        "existing"
      ]
    }
  }
}
dciborow commented 1 year ago

The bigger question would be how to represent this in an ARM template. An open enum by definition won't have any runtime validation behavior, so it should probably be metadata, e.g.,

Could we put it in the ARM template similar to how the custom types are done? Using a similar notation to required, perhaps something like this, where these both compile to the same ARM template.

@recommend(['win10', 'linux'])
type osRecommend string

param os osRecommend
@recommended(['win10', 'linux'])
param os string
dciborow commented 1 year ago
          can we add 'Allowed' annotation here with the list of "kinds". Even though the kinds are not defined in the public swagger.

(totally optional, but perhaps a good place to create a user-defined type)

@allowed(['CognitiveServices', 'ComputerVision', 'CustomVision.Prediction', 'CustomVision.Training', 'Face', 'FormRecognizer', 'SpeechServices', 'LUIS', 'QnAMaker', 'TextAnalytics', 'TextTranslation', 'AnomalyDetector', 'ContentModerator', 'Personalizer', 'OpenAI'])
param kind string

_Originally posted by @dciborow in https://github.com/Azure/bicep-registry-modules/pull/404#discussion_r1237284170_

richardsjoberg commented 1 year ago

This would be an really nice feature. It's a super common use case that platform teams want to provide recommendations for developers without enforcing them.

Is there a workaround for this?