3 min read
Azure Bicep: Infrastructure as Code Simplified
Azure Bicep is a domain-specific language for deploying Azure resources. Cleaner syntax than ARM templates, same deployment engine underneath.
Why Bicep?
| ARM JSON | Bicep |
|---|---|
| Verbose | Concise |
| Complex syntax | Natural syntax |
| Hard to read | Easy to read |
| Reference functions | Simple references |
Basic Syntax
// main.bicep
param location string = resourceGroup().location
param storageAccountName string
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
accessTier: 'Hot'
}
}
output storageAccountId string = storageAccount.id
output primaryEndpoint string = storageAccount.properties.primaryEndpoints.blob
Deploy Bicep
# Deploy directly
az deployment group create \
--resource-group myRG \
--template-file main.bicep \
--parameters storageAccountName=mystorage123
# Compile to ARM (optional)
az bicep build --file main.bicep
Variables and Expressions
var baseName = 'myapp'
var uniqueSuffix = uniqueString(resourceGroup().id)
var storageAccountName = '${baseName}${uniqueSuffix}'
// String interpolation
var connectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}'
Parameters with Decorators
@description('The environment name')
@allowed([
'dev'
'test'
'prod'
])
param environment string
@minLength(3)
@maxLength(24)
param storageAccountName string
@secure()
param adminPassword string
@minValue(1)
@maxValue(10)
param instanceCount int = 2
Conditional Deployment
param deployStorage bool = true
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = if (deployStorage) {
name: storageAccountName
location: location
// ...
}
Loops
param storageAccounts array = [
'storage1'
'storage2'
'storage3'
]
resource accounts 'Microsoft.Storage/storageAccounts@2021-02-01' = [for name in storageAccounts: {
name: name
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
}]
// Loop with index
resource indexedAccounts 'Microsoft.Storage/storageAccounts@2021-02-01' = [for (name, i) in storageAccounts: {
name: '${name}${i}'
// ...
}]
Modules
// modules/storage.bicep
param name string
param location string
resource storage 'Microsoft.Storage/storageAccounts@2021-02-01' = {
name: name
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}
output id string = storage.id
// main.bicep
module storageModule 'modules/storage.bicep' = {
name: 'storageDeployment'
params: {
name: 'mystorage'
location: location
}
}
output storageId string = storageModule.outputs.id
Existing Resources
// Reference existing resource
resource existingVnet 'Microsoft.Network/virtualNetworks@2021-02-01' existing = {
name: 'my-existing-vnet'
}
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' = {
parent: existingVnet
name: 'new-subnet'
properties: {
addressPrefix: '10.0.1.0/24'
}
}
Complete Example
// Deploy web app with SQL
param appName string
param sqlAdminPassword string
resource sqlServer 'Microsoft.Sql/servers@2021-02-01-preview' = {
name: '${appName}-sql'
location: location
properties: {
administratorLogin: 'sqladmin'
administratorLoginPassword: sqlAdminPassword
}
}
resource sqlDb 'Microsoft.Sql/servers/databases@2021-02-01-preview' = {
parent: sqlServer
name: '${appName}-db'
location: location
sku: {
name: 'Basic'
}
}
resource appServicePlan 'Microsoft.Web/serverfarms@2021-01-01' = {
name: '${appName}-plan'
location: location
sku: {
name: 'S1'
}
}
resource webApp 'Microsoft.Web/sites@2021-01-01' = {
name: appName
location: location
properties: {
serverFarmId: appServicePlan.id
}
}
Bicep: ARM templates you can actually read.