3 min read

Bicep: Dynamic naming technique

Bicep: Dynamic naming technique
Photo by Glen Carrie / Unsplash

Introduction

When designing IaC modules finding the correct syntax to deploy a certain resource type is often not the hardest thing to do. What I found in 3 years of writing Bicep code, is that defining a dynamic way to name your resources which is also easy to use, seems to pose quite the challenge. This article won't define the best way to get this but a way that seems to work for me and the customers I have worked with in the past.

Challenges

First, we have to analyse how the naming of resources happens to be able to shape our code to those requirements. A good place to start is the Cloud Adoption Framework, which contains a list of items you can incorporate in your naming ,. However, through experience I've noticed it's not always the best solution to name all resources in an equal way, to give you some examples:

  • An Azure Front Door is a global resource and doesn't need a region in its name
  • In most cases, there is only one Hub/VWAN and one Identity spoke, so an environment (DEV/TEST/UAT/PRD) might be unnecessary in the vnet name
  • An index might be unnecessary since you'll seldom deploy 2 VWAN hubs in the same region
  • For child and extension resources the naming shows cannot be followed because they can't exist alone (not viewable in the portal in a regular Resource Group view). An example might be subnet, which when referenced almost always shows the vnet in which they can be found

The conclusion is that we have to account for this in our naming strategy and make it possible to overwrite our default naming strategy defined for a certain customer. Abbreviations are something which most customers will use from the CAF (I hope) but there will always be exceptions (looking at you LAW or LA instead of LOG for Log Analytics Workspace). A way to add default values and override this function would be great.

Another neat feature we should support is Bicepparam, at this time I don't think that they are production-ready. I'm especially waiting on extendable parameters (the former modular parameters) however the first release seems to contain several bugs. So we want to implement support for this in our naming strategy.

Techniques

To solve our challenges above we need some new-ish features that have been recently introduced into Bicep. The simplest feature is user-defined types, the naming will be defined as a custom type and then, using user-defined type imports, imported into each resource module, and be a part of a type of each resource. We will form the resource names in each resource module using custom functions which are centrally stored and imported in each file where required.

Solution

On GitHub, you can find a way to structure a module library and how the naming is integrated into this. Below we will take a short look at each component of this structure.

shared - main.bicep

Below is the main focus of this article, the naming type:

  • Param1-3 are custom parameters the customer may require (fixed customer name for example)
  • Function is the only required property, however format is also required for this to work
		
Code loading...
      	
    
Show on Github

functions - main.bicep

The custom functions that are used in each resource module are centrally stored. They define the different flows based on the parameters:

  1. Check if forceFunctionAsFullName == true, use full name
  2. Check if forceDefaultNaming == true, use CAF naming
  3. Otherwise use customName, if not available, use CAF naming
		
Code loading...
      	
    
Show on Github

resource-group - types.bicep

Below are the resource-group specific types, in this case only resourceGroup which contains:

  • sharedNaming: for the whole customer
  • naming: specific to the resource
  • location: resource deployment location
		
Code loading...
      	
    
Show on Github

resource-group - main.bicep

The file that deploys the resource group itself and uses the functions and types imported from the other files. You can see we right-merge the sharedNaming and naming, meaning naming will have priority over sharedNaming properties (overwrite). Abbreviation is retrieved from a central file, but can be overwritten using naming parameter.

		
Code loading...
      	
    
Show on Github

customer - main.bicep

Last but not least is the customer side, here we define the sharedNaming constructed specifically for the customer. Next we define the overloaded properties.

		
Code loading...
      	
    
Show on Github

Conclusion

The file structure shown above is how we organize our modules in a single repository. We then deploy all the main files to a Template Specs across all customer tenants. The deployment process is a topic for another article, but this naming strategy will help you customize your code for your customer while still keeping it DRY (Don't Repeat Yourself).