With infrastructure as code, we strive to parameterize, re-use as much as possible, and make our code as modular as possible. As your application infrastructure grows, it might become too much work to have everything decoupled and modular. But for ad-hoc deployments, “bread and butter” resources we can make things very agile. Azure Bicep modules, variables and parameters are here to help
At the moment I am collaborating on rewriting some of our infrastructures to use Azure Bicep. This infrastructure is set up multiple times for different environments like
production, but they are mostly identical when it comes to the resources used. Therefore all resources now exist as Azure Bicep Modules. The modules themselves handle the different configurations based on the environment being provisioned.
test environments using a smaller number of nodes in the AKS for example.
Bicep modules are here to help us abstract the complexity of our deployment templates. Make it easier to re-use and share the templates across environments, applications, and teams. It is totally up to you how you create and structure your modules, as all
.bicep files can be used as a module. You can also include as many individual resources you like. In the end, bicep will combine all module files and create a nested deployment.
If you have ever worked with nested deployments in ARM templates, you will be glad Bicep modules now exist.
Below I have added a fairly simple module that will provision a storage account with the inputted
storageName parameter. In my
main.bicep file I will ask for this parameter, and pass it down to my module. The main file is actually more complex than the module file, as its also provisioning a resource group that the storage account will be in.
Environment sizing with Bicep modules
With the above example in mind. How can we move from that to a more complex and re-usable scenario? First of all, we need to accept a few more inputs in our main file. But the complexity will need to be handled inside our storage account module. Our goal is to re-use the module regardless of the environment being provisioned. Therefore we need to handle scenarios like name, storage SKU, and so on. I use a storage account as an example here, but the concept is the same regardless of resource types.
To extend my module’s modularity (sorry) I am expanding the required input parameters, but I also added an object variable to hold my configuration settings for the different environments. Looking at the storage account module now, you can see the new variable, and also how I get the data from within the object variable by using the inputted name for
I also added more smartness to the module, by using the
toLower function and concatenating the storage account name with a unique string based on our resource group. This way we move the complexity of naming the storage account from the user to the code it self. Storage accounts only accepts lower case in its name, and it needs to be globally unique.
The main file changed slightly as well. As you can see, I added
environment parameter, with two allowed values,
test. I also have a
tags variable that I pass down to the module as input parameter together with the environment. The resource group name now has environment represented in the name as well.
Tags and naming conventions is of course very domain specific, but this example show how you can use the different functions available in Azure Bicep to create a more elastic code for your specific environments where resource types is shared.
In the above examples, I showed how you can use Bicep Modules, parameters and variables to re-use your templates cross multiple environments with different properties. If you like me, have multiple environments that on the technology side is identical, but properties like network addresses, virtual machine families, etc differs. You can use modules and custom variable to handle it.
Stay tuned for more Bicep posts in the near future.