Exploring Modules in Azure Bicep

Somebody who likes to code
Search for a command to run...

Somebody who likes to code
No comments yet. Be the first to comment.
Google processes tens of thousands of code changes every day across a codebase of hundreds of millions of lines. The secret isn't magic tooling — it's a documented, principled approach to code review

Amazon Simple Notification Service (SNS) looks straightforward on the surface — publish a message, deliver it to subscribers — but the gap between a topic that "works in dev" and one that is productio

Amazon Simple Queue Service (SQS) is deceptively simple to get started with, but surprisingly easy to misconfigure in ways that cause silent message loss, runaway costs, duplicate processing storms, o

HTTP API is the right default choice for the majority of .NET serverless workloads. At 70% less cost than REST API, lower latency, and native JWT authorization, it covers most production requirements

AWS Lambda has become a cornerstone of modern serverless architectures, but writing a function that "just works" is very different from writing one that is performant, cost-effective, secure, and read

In the article Getting Started with Azure Bicep, we discussed the fundamental concepts required to start using it. Today, we will explore the topic of modules.
Modules are reusable building blocks of code that enable us to create, manage, and deploy Azure resources in a more organized and efficient manner. A module is a regular Bicep file that enables us to bundle multiple resources, parameters, variables, and outputs into a single package, which can then be invoked from other Bicep files.
These modules help to reduce code duplication, enhance maintainability, and simplify complex infrastructure deployments by encapsulating resource configurations.
The syntax for defining a module is:
module <symbolic-name> '<path-to-file>' = {
name: '<linked-deployment-name>'
params: {
<parameter-names-and-values>
}
}
The name property is required. It becomes the name of the nested deployment resource in the generated template.
When creating a module, the most crucial aspect is defining the interface, the inputs it requires, and what outputs it provides:
Inputs: The params specified in the module definition must correspond to the parameters in the Bicep file.
Outputs: We can get values from a module and use them in the main Bicep file using the syntax <symbolic-name>.outputs.<output-name>.
To illustrate the usage and benefits, we will refactor the following script (main.bicep) using modules:
param prefix string = 'myapp'
param location string = resourceGroup().location
var appServicePlanName = '${toLower(prefix)}-app-${uniqueString(resourceGroup().id)}'
var webSiteName = '${toLower(prefix)}-web-${uniqueString(resourceGroup().id)}'
var cpuMetricAlertName = '${appServicePlanName}-cpu-alert'
var actionGroupName = '${toLower(prefix)}-action-group-${uniqueString(resourceGroup().id)}'
resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
name: appServicePlanName
location: location
properties: {
reserved: true
}
sku: {
name: 'B1'
capacity: 1
}
kind: 'linux'
}
resource appService 'Microsoft.Web/sites@2020-06-01' = {
name: webSiteName
location: location
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: 'DOTNETCORE|7.0'
alwaysOn: true
}
}
}
resource developmentTeamActionGroup 'Microsoft.Insights/actionGroups@2022-06-01' = {
name: actionGroupName
location: 'Global'
properties: {
groupShortName: 'devteam'
enabled: true
emailReceivers:[
{
name:'developer1'
emailAddress:'raulnq@gmail.com'
}
]
}
}
resource cpuMetricAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
location: 'Global'
name: cpuMetricAlertName
properties:{
severity: 2
enabled: true
evaluationFrequency: 'PT1H'
windowSize: 'PT1H'
autoMitigate: true
targetResourceType: 'Microsoft.Web/serverFarms'
targetResourceRegion: location
scopes: [
appServicePlan.id
]
criteria: {
allOf: [
{
threshold: 75
name: 'Metric1'
metricNamespace: 'Microsoft.Web/serverFarms'
metricName: 'CpuPercentage'
operator: 'GreaterThan'
timeAggregation: 'Average'
criterionType: 'StaticThresholdCriterion'
}
]
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
}
actions: [
{
actionGroupId: developmentTeamActionGroup.id
}
]
}
}
The script creates an App Service and an Alert associated with its CPU usage. Based on this, we will create two modules: one for the App Service and another for the Alert. Let's create an appService.bicep file with the following content:
param appServicePlanName string
param location string
param webSiteName string
resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
name: appServicePlanName
location: location
properties: {
reserved: true
}
sku: {
name: 'B1'
capacity: 1
}
kind: 'linux'
}
resource appService 'Microsoft.Web/sites@2020-06-01' = {
name: webSiteName
location: location
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: 'DOTNETCORE|7.0'
alwaysOn: true
}
}
}
output appServicePlanId string = appServicePlan.id
And an alert.bicep file as follows:
param cpuMetricAlertName string
param location string
param threshold int
param appServicePlanId string
param actionGroupId string
resource cpuMetricAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
location: 'Global'
name: cpuMetricAlertName
properties:{
severity: 2
enabled: true
evaluationFrequency: 'PT1H'
windowSize: 'PT1H'
autoMitigate: true
targetResourceType: 'Microsoft.Web/serverFarms'
targetResourceRegion: location
scopes: [
appServicePlanId
]
criteria: {
allOf: [
{
threshold: threshold
name: 'Metric1'
metricNamespace: 'Microsoft.Web/serverFarms'
metricName: 'CpuPercentage'
operator: 'GreaterThan'
timeAggregation: 'Average'
criterionType: 'StaticThresholdCriterion'
}
]
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
}
actions: [
{
actionGroupId: actionGroupId
}
]
}
}
Then modify the original main.bicep file as follows:
param prefix string = 'myapp'
param location string = resourceGroup().location
var appServicePlanName = '${toLower(prefix)}-app-${uniqueString(resourceGroup().id)}'
var webSiteName = '${toLower(prefix)}-web-${uniqueString(resourceGroup().id)}'
var cpuMetricAlertName = '${appServicePlanName}-cpu-alert'
var actionGroupName = '${toLower(prefix)}-action-group-${uniqueString(resourceGroup().id)}'
module webApp './appService.bicep' = {
name: '${webSiteName}-deployment'
params: {
appServicePlanName: appServicePlanName
location : location
webSiteName : webSiteName
}
}
resource developmentTeamActionGroup 'Microsoft.Insights/actionGroups@2022-06-01' = {
name: actionGroupName
location: 'Global'
properties: {
groupShortName: 'devteam'
enabled: true
emailReceivers:[
{
name:'developer1'
emailAddress:'raulnq@gmail.com'
}
]
}
}
module cpuMetricAlert './alert.bicep' = {
name: '${cpuMetricAlertName}-deployment'
params: {
appServicePlanId: webApp.outputs.appServicePlanId
cpuMetricAlertName: cpuMetricAlertName
location : location
actionGroupId : developmentTeamActionGroup.id
threshold: 75
}
}
This new main.bicep file will generate the same resources, but it will be much easier to add a new App Service and its corresponding Alert. We hope this article helps in your Azure Bicep journey! Thanks, and happy coding.