Stage 4 · Provision
Bicep & ARM Templates for Azure
Bicep Basics
Syntax, parameters, variables, modules, and loops — the modern way to write Azure infrastructure.
What Is Bicep?
Bicep is a domain-specific language for deploying Azure resources. It transpiles to ARM (Azure Resource Manager) templates but offers a cleaner syntax, better authoring experience, and模块化支持。Bicep is Microsoft's recommended way to write Azure IaC.
Bicep files have the .bicep extension. The az bicep build command converts them to ARM JSON. The az deployment group create command can deploy Bicep files directly — the transpilation happens behind the scenes.
Bicep Syntax
Bicep syntax is significantly cleaner than ARM JSON. Resources are declared with named blocks, properties are assignments rather than nested objects, and comments use // instead of // or /* */.
@description('Azure region for all resources')
param location string = resourceGroup().location
@description('Environment name')
@allowed(['dev', 'staging', 'prod'])
param environment string
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'stg${environment}${uniqueString(resourceGroup().id)}'
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
}The param keyword declares parameters. The resource keyword declares a resource with its type and API version. String interpolation uses ${} syntax. The @allowed decorator restricts parameter values.
Parameters
Parameters are the input mechanism for Bicep files. They support types, defaults, decorators for validation, and descriptions. Parameters replace the complex parameter schema of ARM templates.
@description('Number of VM instances')
@minValue(1)
@maxValue(10)
param instanceCount int = 2
@description('Admin username')
@secure()
param adminPassword string
@description('Subnet configuration')
param subnets array = [
{ name: 'web', cidr: '10.0.1.0/24' }
{ name: 'data', cidr: '10.0.2.0/24' }
]
@description('Enable monitoring')
param enableMonitoring bool = trueThe @minValue and @maxValue decorators constrain numeric parameters. The @secure decorator marks sensitive parameters — they are masked in deployment logs. Array and object types allow structured inputs.
Variables
Variables store computed values for reuse within a Bicep file. They cannot be marked as secure — use parameters for sensitive values. Variables are evaluated at deployment time.
param environment string
param location string
var storageAccountName = 'stg${environment}${uniqueString(resourceGroup().id)}'
var isProduction = environment == 'prod'
var commonTags = {
Environment: environment
ManagedBy: 'bicep'
DeployedAt: utcNow()
}
var storageSku = isProduction ? 'Standard_GRS' : 'Standard_LRS'Variables use the var keyword. String interpolation combines literal text with expressions. Ternary expressions (? :) provide conditional logic. Variables keep resource definitions clean and readable.
Modules in Bicep
Modules in Bicep are separate .bicep files that encapsulate related resources. They provide the same benefits as Terraform modules — reuse, composition, and separation of concerns.
param environment string
param location string
module storage 'modules/storage.bicep' = {
name: 'storageDeploy'
params: {
storageAccountName: 'stg${environment}'
location: location
skuName: environment == 'prod' ? 'Standard_GRS' : 'Standard_LRS'
}
}
output storageId string = storage.outputs.storageAccountIdThe module keyword calls another Bicep file. The name parameter is a deployment name for tracking in the Azure portal. Outputs from the module are accessible via the module name. The module file must exist at the specified path.
Loops and Conditions
Bicep supports loops for creating multiple instances of a resource and conditions for optional resources. These features replace the copy and condition mechanisms of ARM templates.
param subnetConfig array
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' = [for config in subnetConfig: {
name: config.name
parent: vnet
properties: {
addressPrefix: config.cidr
serviceEndpoints: config.serviceEndpoints ?? []
}
}]
param enableFirewall bool
resource firewall 'Microsoft.AzureFirewall/azureFirewalls@2023-04-01' = if(enableFirewall) {
name: 'azureFirewall'
location: location
sku: {
name: 'AZFW_Vnet'
tier: 'Standard'
}
}The [for item in collection: {}] syntax creates multiple resource instances. The if() function makes a resource conditional. The ?? operator provides null-coalescing for optional properties.
resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' existing = {
name: 'myVnet'
}
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' = {
name: 'mySubnet'
parent: vnet
properties: {
addressPrefix: '10.0.1.0/24'
}
}The existing keyword references a resource that already exists in Azure without creating or managing it. This is useful for referencing resources in other resource groups or subscriptions.
The Bicep extension provides IntelliSense, validation, and deployment integration. It auto-completes resource types, property names, and API versions. It also shows ARM template equivalent output in real time.
Unlike Terraform, Bicep only works with Azure resources. If you need multi-cloud support, use Terraform. If you are Azure-only, Bicep offers a superior authoring experience with deeper Azure integration.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.