bicep icon indicating copy to clipboard operation
bicep copied to clipboard

Format Function use with an array of Strings

Open dotnetcadet opened this issue 3 years ago • 2 comments

Trying to write a generic module where you can pass in an array of strings to format another string based on environment being deployed to, but keep getting input string not incorrect format.

Question:

  1. Does the format function take an array?
  2. Does the input string have to have formatting?

Here is the actual scenario:

  1. Parameter File would contain the following:
"appConfigurationKey": "Some config key",
"appConfigurationValue": "https://{est.com/{0}",
"appConfigurationValueContentType": null,
"appConfigurationName": "app config name",
"appConfigurationResourceGroup": "app config resource group",
"appConfigurationValueEnvReplacements": {
"dev": [
       "dev value"
],
"qa": [
     "qa value"
],
"uat": [
      "uat value"
 ],
 "prd": [
       "prd value"
 ]
}
@allowed([
  ''
  'dev'
  'qa'
  'uat'
  'prd'
])
@description('The environment in which the resource(s) will be deployed')
param environment string = ''

@description('The region prefix or suffix for the resource name, if applicable.')
param region string = ''

@description('The name of the app configuration')
param appConfigurationName string

@description('The name of the Key Vaule')
param appConfigurationKey string

@secure()
@description('The value of the App configuration to create or update')
param appConfigurationValue string

@description('Labels to append to App Configuration Key')
param appConfigurationLabels array = []

@description('The content type of the configuration value')
param appConfigurationContentType string = ''

@description('')
param appConfigurationValueEnvReplacements object = {}

// if empty then yes (env agnostic config value) or 
var appConfigReplacements = !empty(appConfigurationValueEnvReplacements) ? appConfigurationValueEnvReplacements : {
  dev: []
  qa: []
  uat: []
  prd: []
}
var appConfig = environment == 'dev' ? {
  value: format(appConfigurationValue, appConfigReplacements.dev)
} : environment == 'qa' ? {
  value: format(appConfigurationValue, appConfigReplacements.qa) 
} : environment == 'uat' ? {
  value: format(appConfigurationValue, appConfigReplacements.uat)
} : environment == 'prd' ? {
  value: format(appConfigurationValue, appConfigReplacements.dev)
} : {
  value: appConfigurationValue
}

resource azAppConfigurationKeyValuesDeployment 'Microsoft.AppConfiguration/configurationStores/keyValues@2022-05-01' = if (empty(appConfigurationLabels) || contains(appConfigurationLabels, 'default')) {
  name: replace(replace('${appConfigurationName}/${appConfigurationKey}', '@environment', environment), '@region', region)
  properties: {
    value: replace(replace(appConfig.value, '@environment', environment), '@region', region)
    contentType: empty(appConfigurationContentType) ? json('null') : appConfigurationContentType
  }
}

// Deploys the same configuration value with different labels
resource azAppConfigurationKeyValuesWithLabelsDeployment 'Microsoft.AppConfiguration/configurationStores/keyValues@2022-05-01' = [for label in appConfigurationLabels: if (label != 'default' && !empty(appConfigurationLabels)) {
  name: replace(replace('${appConfigurationName}/${appConfigurationKey}$${label}', '@environment', environment), '@region', region)
  properties: {
    value: replace(replace(appConfig.value, '@environment', environment), '@region', region)
    contentType: empty(appConfigurationContentType) ? json('null') : appConfigurationContentType
  }
}]


dotnetcadet avatar Aug 09 '22 00:08 dotnetcadet

After some testing I realized array of string is not supported. Is there a way to make this current issue into a request for enhancement?

dotnetcadet avatar Aug 09 '22 13:08 dotnetcadet

I believe this would need special syntax to implement in a backwards-compatible way. The format function does support passing an array as an argument, but the longstanding behavior of the ARM runtime is to convert the array to a string before injecting it into the format string as a single element. E.g.,

output foo string = format('{0}', ['fizz', 'buzz', 'pop'])

would compile to:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.9.1.41621",
      "templateHash": "8261008250483932764"
    }
  },
  "resources": [],
  "outputs": {
    "foo": {
      "type": "string",
      "value": "[format('{0}', createArray('fizz', 'buzz', 'pop'))]"
    }
  }
}

which, when deployed, would produce the following outputs:

image

In order to implement something close to the feature described, I think ARM would need an equivalent of JavaScript's function.prototype.apply() or PHP's call_user_func_array(), which would allow users to invoke a function by name with an array of arguments. For example, format('{0}', 'foo') might be equivalent to (and this is purely strawman syntax) apply('format', createArray('{0}', 'foo')) in ARM JSON or format.apply(['{0}', 'foo']) in Bicep.

jeskew avatar Aug 09 '22 19:08 jeskew