Configuration set pattern

Rather than define lots of individual parameters, create predefined sets of values. During deployment, select the set of values to use.

Context and problem

A single Bicep file often defines many resources. Each resource might need to use a different configuration depending on the environment you're deploying it to. For example, you might build a Bicep file that deploys an App Service plan and app, and a storage account. Each of these resources has multiple options that affect its cost, availability, and resiliency. For production environments, you want to use one set of configuration that prioritize high availability and resiliency. For non-production environments, you want to use a different set of configuration that prioritizes cost reduction.

You could create parameters for each configuration setting, but this has some drawbacks:

  • This approach creates a burden on your template users, since they need to understand the values to use for each resource, and the impact of setting each parameter.
  • The number of parameters in your template increases with each new resource you define.
  • Users may select combinations of parameter values that haven't been tested or that won't work correctly.

Solution

Create a single parameter to specify the environment type. Use a variable to automatically select the configuration for each resource based on the value of the parameter.

Note

This approach is sometimes called t-shirt sizing. When you buy a t-shirt, you don't get lots of options for its length, width, sleeves, and so forth. You simply choose between small, medium, and large sizes, and the t-shirt designer has predefined those measurements based on that size.

Example

Suppose you have a template that can be deployed to two types of environment: non-production and production. Depending on the environment type, the configuration you need is different:

Property Non-production environments Production environments
App Service plan
SKU name S2 P2V3
Capacity (number of instances) 1 3
App Service app
Always On Disabled Enabled
Storage account
SKU name Standard_LRS Standard_GRS

You could use the configuration set pattern for this template.

Accept a single parameter that indicates the environment type, such as production or non-production. Use the @allowedValues parameter decorator to ensure that your template's users only provide values that you expect:

@allowed([
  'Production'
  'NonProduction'
])
param environmentType string = 'NonProduction'

Then create a map variable, which is an object that defines the specific configuration depending on the environment type. Notice that the variable has two objects named Production and NonProduction. These names match the allowed values for the parameter in the preceding example:

var environmentConfigurationMap = {
  Production: {
    appServicePlan: {
      sku: {
        name: 'P2V3'
        capacity: 3
      }
    }
    appServiceApp: {
      alwaysOn: false
    }
    storageAccount: {
      sku: {
        name: 'Standard_GRS'
      }
    }
  }
  NonProduction: {
    appServicePlan: {
      sku: {
        name: 'S2'
        capacity: 1
      }
    }
    appServiceApp: {
      alwaysOn: false
    }
    storageAccount: {
      sku: {
        name: 'Standard_LRS'
      }
    }
  }
}

When you define the resources, use the configuration map to define the resource properties:

resource appServicePlan 'Microsoft.Web/serverfarms@2020-06-01' = {
  name: appServicePlanName
  location: location
  sku: environmentConfigurationMap[environmentType].appServicePlan.sku
}

resource appServiceApp 'Microsoft.Web/sites@2020-06-01' = {
  name: appServiceAppName
  location: location
  properties: {
    serverFarmId: appServicePlan.id
    httpsOnly: true
    siteConfig: {
      alwaysOn: environmentConfigurationMap[environmentType].appServiceApp.alwaysOn
    }
  }
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = {
  name: storageAccountName
  location: location
  kind: 'StorageV2'
  sku: environmentConfigurationMap[environmentType].storageAccount.sku
}

Considerations

  • In your map variable, consider grouping the properties by resource to simplify their definition.
  • In your map variable, you can define both individual property values (like the alwaysOn property in the example), or object variables that set an object property (like the SKU properties in the example).
  • Consider using a configuration set with resource conditions. This enables your Bicep code to deploy certain resources for specific environments, and not in others.

Next steps

Learn about the shared variable file pattern.