Bicep Tips and Tricks | #4 | Shared Variables

Overview

This week is a simple one, but works wonders in maintainability and consistency.

There are often cases where you will need to define static values that don’t change frequently, if at all, and more importantly, you seem to be setting these up frequently for multiple templates. Here are some examples that I have seen:

  1. Multi-Environment Deployments

    Different environments (dev, staging, prod) that share common configuration but need environment-specific values:

    var environmentConfig = {
      dev: {
        sku: 'Basic'
      }
      staging: {
        sku: 'Standard'
      }
      prod: {
        sku: 'Premium'
      }
    }
    
  2. Organisational Standards

    When you need to enforce company-wide conventions and policies.

    var mandatoryTags = {
      costCenter: 'IT-001'
      dataClassification: 'internal'
      businessUnit: 'engineering'
      version: deployment().properties.template.contentVersion
    }
    
  3. Object or Resource Configurations

    When you have configurations that would be repeated across multiple templates.

    var subnetConfigurations = [
      {
        name: 'web-subnet'
        addressPrefix: '10.0.1.0/24'
        serviceEndpoints: ['Microsoft.Storage', 'Microsoft.KeyVault']
      }
      {
        name: 'api-subnet'
        addressPrefix: '10.0.2.0/24'
        serviceEndpoints: ['Microsoft.Sql', 'Microsoft.KeyVault']
      }
    ]
    
    // OR
    
    var keyVaultSecretsUserRoleDefId = '4633458b-17de-408a-b874-0445c86b69e6'
    

You might think, “Hey, the values don’t change very often, so what’s the big deal? So what if I set them up many times across my templates?” Not to be a pessimist, but it’s usually at this point that Murphy’s Law comes into effect and the values will need to change! Would you not rather there be a single point where you can update these values and they ripple through your templates like a stone hitting water?

Well, you can. Simply put, Shared Variables.

My recommended approach is to leverage Bicep Imports. Create a Common folder within your IaC directory, and then a variables.bicep file within:

IaC
  ↳ Common
    ↳ variables.bicep
  ↳ main.bicep

This will form the basis of your shared variables that can be used across your deployment templates.

Then find all the relevant candidates and move these across to the shared variables template. Include the @export() decorator to each variable; this is used to allow the variable to be imported into other Bicep files.

For Example

/**********************************
  Bicep Template: Shared Variables
  Author: Andrew Wilson
***********************************/

@export()
@description('Shared Variable for environment configurations')
var environmentConfig = {
  dev: {
    sku: 'Basic'
  }
  staging: {
    sku: 'Standard'
  }
  prod: {
    sku: 'Premium'
  }
}

@export()
@description('Shared Variable for tagging resources')
var mandatoryTags = {
  costCenter: 'IT-001'
  dataClassification: 'internal'
  businessUnit: 'engineering'
  version: deployment().properties.template.contentVersion
}

@export()
@description('Shared Variable for resource configuration')
var subnetConfigurations = [
  {
    name: 'web-subnet'
    addressPrefix: '10.0.1.0/24'
    serviceEndpoints: ['Microsoft.Storage', 'Microsoft.KeyVault']
  }
  {
    name: 'api-subnet'
    addressPrefix: '10.0.2.0/24'
    serviceEndpoints: ['Microsoft.Sql', 'Microsoft.KeyVault']
  }
]

@export()
@description('Shared Variable for Key Vault Secrets User Role Definition ID')
var keyVaultSecretsUserRoleDefId = '4633458b-17de-408a-b874-0445c86b69e6'

These can then be used in your deployment templates as follows:

/***************************************
Bicep Template: Main Deploy
Author: Andrew Wilson
****************************************/

targetScope = 'resourceGroup'

// ** Shared Imports **
// ********************

import { keyVaultSecretsUserRoleDefId } from './Common/variables.bicep'

// ** Parameters **
// ****************

...

// ** Variables **
// ***************

...

// ** Resources **
// ***************

resource roleassignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(logicAppName, keyVaultSecretsUserRoleDefId)
  properties: {
    roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', keyVaultSecretsUserRoleDefId)
    ...
  }
}

Best Practices for Using Shared Variables

  1. Keep it Simple: Only share variables that are common and stable.
  2. Tight Control: Treat shared variables as critical infrastructure code.
  3. Documentation: Clearly document and describe what each variable represents or is used for.
  4. Selective Imports: Don’t import everything with *; be selective about what needs to be imported into deployment templates.

When NOT to Use Shared Variables

  • For values that change frequently or are deployment-specific
  • For simple, one-off configurations
  • For sensitive values (use parameters or Key Vault references instead)

Summary

Shared variables help eliminate duplication and improve consistency across your Bicep templates. This simple approach gives you a single point of truth for common configurations, making updates easier and reducing the risk of inconsistencies across environments. Hope this helps, and happy Bicep-ing!

For the original version of this post see Andrew Wilson's personal blog at Bicep Tips and Tricks | #4 | Shared Variables