Managing Multiple Environments with Bicep Templates: A Practical Guide

If you’ve ever had to manage multiple cloud environments—whether it’s development, testing, or production—you know how important consistency and simplicity are.

You want everything to behave the same across environments, but with just enough flexibility to account for differences like resource sizes or locations. This is where Bicep, Azure’s Infrastructure as Code (IaC) tool, comes in handy. And if you’re using Bicep templates, you can easily handle these differences with parameter files, allowing you to reuse the same template across all your environments.

In this guide, I’ll show you how you can manage multiple environments with a single Bicep template, and how to use parameter files (including a neat trick with VS Code) to make your deployments smoother.

Why Use Bicep?

First, a quick refresher on why Bicep is such a great tool:

  • Clean and Simple: Compared to the JSON-based Azure Resource Manager (ARM) templates, Bicep is much easier to read and write.
  • Modular: You can break down your templates into reusable components.
  • Azure-Integrated: Bicep works natively with Azure Resource Manager, so no extra tools are needed to deploy.

But one of the best things about Bicep is that you can manage multiple environments with the same template and just change a few parameters using files. This is where parameter files come in.

What Are Parameter Files?

Let’s say you have different environments like development, testing, and production. While the core infrastructure might be the same, the configurations—like resource sizes, regions, or names—may differ.

Parameter files let you store these environment-specific settings outside your main template. This way, you can reuse the same template and just swap out the parameter file depending on where you’re deploying.

Why Parameter Files Make Life Easier

  • Clarity: Your template logic stays clean and is separate from the environment settings.
  • Efficiency: You don’t need to copy and paste the same template with minor edits—just switch the parameters.
  • Consistency: By using the same template, you reduce the chance of error, and ensure each environment is set up consistently.

Let’s Get Hands-On: Using Bicep with Parameter Files

Here’s a quick example to show how you can use one Bicep template across multiple environments with different parameter files.

Step 1: Create the Bicep Template

Here’s a basic Bicep template (main.bicep) that sets up a storage account:

param storageAccountName string
param location string = resourceGroup().location
param skuName string = 'Standard_LRS'

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: skuName
  }
  kind: 'StorageV2'
}

This template has three parameters:

  • storageAccountName (name of your storage account)
  • location (where it’s deployed—defaults to the resource group’s location)
  • skuName (the type of storage, which defaults to Standard_LRS)

Step 2: Create Environment-Specific Parameter Files

Now, you need to create a parameter file for each environment. These files will contain the specific values for each parameter depending on where you’re deploying.

Development (dev.parameters.json):

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "value": "devstorageacct"
    },
    "location": {
      "value": "eastus"
    },
    "skuName": {
      "value": "Standard_LRS"
    }
  }
}

Testing (test.parameters.json):

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "value": "teststorageacct"
    },
    "location": {
      "value": "centralus"
    },
    "skuName": {
      "value": "Standard_GRS"
    }
  }
}

Production (prod.parameters.json):

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "value": "prodstorageacct"
    },
    "location": {
      "value": "westus2"
    },
    "skuName": {
      "value": "Standard_ZRS"
    }
  }
}

Each of these files holds the storageAccountName, location, and skuName for the respective environment.

Step 3: Deploy Using Parameter Files

Now that you have your parameter files, you can easily deploy to different environments by referencing the appropriate file.

For development:

az deployment group create \
  --resource-group devResourceGroup \
  --template-file main.bicep \
  --parameters dev.parameters.json

For testing:

az deployment group create \
  --resource-group testResourceGroup \
  --template-file main.bicep \
  --parameters test.parameters.json

For production:

az deployment group create \
  --resource-group prodResourceGroup \
  --template-file main.bicep \
  --parameters prod.parameters.json

Use .bicepparam Files to Make Things Even Easier

You can simplify this even further by using .bicepparam files, which are like a simpler version of the JSON parameter files, but more developer-friendly. These .bicepparam files can later be converted into JSON using VS Code.

Here’s how a .bicepparam file might look:

@description('Storage account name')
param storageAccountName string = 'devstorageacct'

@description('Location of the resource')
param location string = 'eastus'

@description('SKU for the storage account')
param skuName string = 'Standard_LRS'

The cool thing is that you can convert this file into a JSON parameter file directly in VS Code:

  • Install the Bicep Extension: If you don’t have it already, install the Bicep extension for VS Code.
  • Right-click to Build: Once you have your .bicepparam file, right-click on the file and choose “Build Bicep File”. VS Code will automatically generate the JSON parameter file for you.

This approach allows you to keep your workflow within the Bicep ecosystem, without worrying about the complexity of manually writing JSON files.

Best Practices for Managing Multiple Environments

  • Version Control Your Parameter Files: Track your changes in version control (like Git) to keep consistency across deployments.
  • Use Defaults Carefully: Set reasonable default values for parameters that don’t vary much, like resource location.
  • Automate Deployments: Integrate your deployments with CI/CD pipelines (Azure DevOps, GitHub Actions) to automate environment rollouts.
  • Organize Your Files: Keep things tidy by organizing your templates and parameters in folders (/dev, /test, /prod) to easily switch between environments.

Conclusion

Managing multiple environments with Bicep templates and parameter files makes cloud deployments so much easier. It helps you maintain clean infrastructure code, reuse templates across environments, and reduce errors by keeping things consistent.

On top of that, by using .bicepparam files and leveraging the Bicep extension in VS Code, you can streamline the process even further, converting these files into JSON with just a few clicks.

Give it a try, and you’ll quickly see how Bicep can simplify the way you manage infrastructure across different environments!