Stage 5 · Platform
Infrastructure as Code on Azure
Bicep Language
Resources, parameters, variables, modules, and deployment stacks.
What Is Bicep?
Bicep is a domain-specific language (DSL) for deploying Azure resources. It is a declarative alternative to ARM templates that compiles to ARM JSON. Bicep simplifies authoring with cleaner syntax, modules, and reuse patterns.
// ARM JSON (verbose)
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-05-01",
"name": "stpaymentsprod",
"location": "eastus",
"sku": {"name": "Standard_LRS"},
"kind": "StorageV2"
}
// Bicep (concise)
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: 'stpaymentsprod'
location: 'eastus'
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}Bicep compiles to ARM JSON before deployment. The resource type and API version are explicit in the resource declaration, making it self-documenting.
Microsoft recommends Bicep over ARM JSON for new deployments. It has first-class Azure support, no state file management, and automatic resource ID references.
Bicep Syntax
// Resource with explicit dependencies
resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
name: 'vnet-prod'
location: resourceGroup().location
properties: {
addressSpace: {
addressPrefixes: ['10.0.0.0/16']
}
}
}
// Subnet resource referencing parent VNet
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' = {
parent: vnet
name: 'subnet-aks'
properties: {
addressPrefix: '10.0.1.0/24'
}
}The parent keyword creates an explicit relationship between a child resource (subnet) and its parent (VNet). Bicep resolves dependencies automatically.
Parameters
@description('Environment name')
@allowed([
'dev'
'staging'
'prod'
])
param environment string
@description('Resource location')
param location string = resourceGroup().location
@description('VM size')
@allowed([
'Standard_D2s_v3'
'Standard_D4s_v3'
'Standard_D8s_v3'
])
param vmSize string = 'Standard_D2s_v3'
@description('Number of VM instances')
@minValue(1)
@maxValue(10)
param instanceCount int = 2
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: 'st${environment}${uniqueString(resourceGroup().id)}'
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}Parameters are decorated with @description, @allowed, @minValue, and @maxValue for validation. The uniqueString function generates a unique storage account name.
Variables
param environment string
// Simple variable
var storageSku = environment == 'prod' ? 'Standard_GRS' : 'Standard_LRS'
// Variable with conditional logic
var commonTags = {
Environment: environment
ManagedBy: 'bicep'
CostCenter: 'CC-1234'
}
// Variable with string interpolation
var vnetName = 'vnet-${environment}-eastus'
output storageAccountName string = storageAccount.name
output vnetId string = vnet.idVariables are computed once during deployment. Use them for values that are referenced multiple times or require conditional logic.
Modules
// main.bicep
param environment string
param location string = resourceGroup().location
module networking './modules/networking.bicep' = {
name: 'networking-${environment}'
params: {
environment: environment
location: location
vnetAddressPrefix: '10.0.0.0/16'
}
}
module compute './modules/compute.bicep' = {
name: 'compute-${environment}'
params: {
environment: environment
location: location
vnetId: networking.outputs.vnetId
subnetName: networking.outputs.subnetName
}
}
output vnetId string = networking.outputs.vnetIdModules encapsulate reusable infrastructure components. They accept parameters and expose outputs. Module references create implicit dependency ordering.
// modules/networking.bicep
param environment string
param location string
param vnetAddressPrefix string
var subnetPrefixes = {
dev: '10.0.1.0/24'
staging: '10.0.2.0/24'
prod: '10.0.3.0/24'
}
resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
name: 'vnet-${environment}'
location: location
properties: {
addressSpace: { addressPrefixes: [vnetAddressPrefix] }
}
}
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' = {
parent: vnet
name: 'subnet-workload'
properties: { addressPrefix: subnetPrefixes[environment] }
}
output vnetId string = vnet.id
output subnetName string = subnet.nameModules are the primary reuse mechanism in Bicep. Store them in a shared repository and reference them with local paths or Bicep registry references.
Publish modules to an ACR Bicep registry for team-wide sharing: az bicep publish --file modules/networking.bicep --target acr://crbicepprod.azurecr.io/bicep/modules/networking:v1.0
Deployment Stacks
Deployment Stacks group resources and manage their lifecycle as a unit. They track which resources belong to a deployment, handle cleanup when resources are removed from the template, and provide deployment history.
# Create a deployment stack
az stack group create \
--name stack-payments-prod \
--resource-group rg-payments \
--template-file main.bicep \
--parameters environment=prod \
--action-on-unmanage delete \
--yes
# Update the deployment
az stack group update \
--name stack-payments-prod \
--resource-group rg-payments \
--template-file main.bicep \
--parameters environment=prodDeployment Stacks track all resources deployed through them. When a resource is removed from the template, --action-on-unmanage delete removes it from Azure.
Deployment Stacks are currently in preview. Test thoroughly before using in production. They require --action-on-unmanage to be specified for each deployment.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.