Upcoming book:

Modern .NET Development with Azure and DevOps

Reusing Azure Bicep modules

Introduction

I wanted to share something quick I've been doing quite a lot lately to simplify Bicep components reusability.

The problem

When you're building a Bicep project, you are likely going to deploy it to more than one environment. For example, you might have development, staging, and production environments.

It's also likely that your resources may vary and either not be deployed or have different properties depending on the environment. For example, you might to deploy High Availability (HA) resources in production, but not in development.

You might be tempted to create a Bicep file for each environment, but this is not a good idea as it leads to a lot of duplication.

A common thing I've seen is to parametrize everything or almost everything in a Bicep file, but then you don't really know at a quick glance what you are deploying. For example:

param location string = resourceGroup().location
param appServicePlanName string
param appServicePlanSkuName string
param appServicePlanCapacity int
param appServiceUseZoneRedundancy bool

resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: appServicePlanSkuName
    capacity: appServicePlanCapacity
  }
  properties: {
    zoneRedundant: appServiceUseZoneRedundancy
  }
  kind: 'app'
}

Simplified solution

The example above can work when you are in a CI/CD environment where you want to make sure that different Bicep projects use the same configuration for the environments and so they are kept in sync through things like Azure DevOps or Octopus variable groups. In other cases, it can be easier and more developer friendly to keep it to one variable and use conditionals.

For example:

param location string = resourceGroup().location
param appServicePlanName string
param isProduction bool

resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: isProduction ? 'P2v3' : 'S1'
    capacity: isProduction ? 2 : 1
  }
  properties: {
    zoneRedundant: isProduction ? true : false
  }
  kind: 'app'
}

While it may seem like an obvious solution, I've seen a people using a lot of parameters, which makes it harder to understand what hardware and configuration is being deployed at a quick glance.

Conclusion

Hopefully this brings you some ideas on how to simplify your Bicep projects and make them easier to maintain. Happy coding and let me know if you have any questions or comments.