1 min read
Azure Bicep in 2022: The New Standard for Infrastructure as Code
I wrote “Azure Bicep in 2022: The New Standard for Infrastructure as Code” to share practical, production-minded guidance on this topic.
Why Bicep?
Bicep offers:
- Cleaner syntax than ARM JSON
- First-class tooling in VS Code
- No state management (unlike Terraform)
- Direct integration with Azure
- Module support for reusability
Getting Started
# Install Bicep CLI
az bicep install
az bicep upgrade
# Check version
az bicep version
Modern Bicep Patterns
Resource Organization
// main.bicep
targetScope = 'subscription'
param environment string
param location string = 'australiaeast'
// Resource group
resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: 'rg-myapp-${environment}'
location: location
tags: {
environment: environment
managedBy: 'bicep'
}
}
// Deploy resources to the resource group
module resources 'modules/resources.bicep' = {
name: 'resources-deployment'
scope: rg
params: {
environment: environment
location: location
}
}
output resourceGroupId string = rg.id
output appServiceUrl string = resources.outputs.appServiceUrl
Loops and Conditions
param storageAccounts array = [
{ name: 'logs', sku: 'Standard_LRS' }
{ name: 'data', sku: 'Standard_GRS' }
{ name: 'backup', sku: 'Standard_RAGRS' }
]
param deployRedis bool = true
resource storage 'Microsoft.Storage/storageAccounts@2021-08-01' = [for account in storageAccounts: {
name: '${account.name}${uniqueString(resourceGroup().id)}'
location: location
sku: {
name: account.sku
}
kind: 'StorageV2'
properties: {
minimumTlsVersion: 'TLS1_2'
supportsHttpsTrafficOnly: true
}
}]
resource redis 'Microsoft.Cache/redis@2021-06-01' = if (deployRedis) {
name: 'redis-${uniqueString(resourceGroup().id)}'
location: location
properties: {
sku: {
name: 'Basic'
family: 'C'
capacity: 0
}
}
}
User-Defined Types (2022 Feature)
@description('Configuration for the web application')
type webAppConfig = {
name: string
@minValue(1)
@maxValue(10)
instanceCount: int
sku: 'F1' | 'B1' | 'S1' | 'P1v3'
customDomain: string?
}
param config webAppConfig
resource appServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: 'plan-${config.name}'
location: location
sku: {
name: config.sku
capacity: config.instanceCount
}
}
Decorators
@description('The name of the storage account')
@minLength(3)
@maxLength(24)
param storageAccountName string
@description('The SKU of the storage account')
@allowed([
'Standard_LRS'
'Standard_GRS'
'Standard_RAGRS'
'Premium_LRS'
])
param storageSku string = 'Standard_LRS'
@secure()
param adminPassword string
@metadata({
author: 'Michael John Pena'
version: '1.0.0'
})
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = {
name: storageAccountName
location: location
sku: {
name: storageSku
}
kind: 'StorageV2'
}
Modules
Create reusable modules:
// modules/appService.bicep
param name string
param location string = resourceGroup().location
param sku string = 'S1'
param linuxFxVersion string = 'DOTNETCORE|6.0'
resource appServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: 'plan-${name}'
location: location
kind: 'linux'
properties: {
reserved: true
}
sku: {
name: sku
}
}
resource appService 'Microsoft.Web/sites@2021-03-01' = {
name: name
location: location
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: linuxFxVersion
alwaysOn: sku != 'F1'
}
httpsOnly: true
}
}
output url string = 'https://${appService.properties.defaultHostName}'
output principalId string = appService.identity.principalId
CI/CD Integration
# azure-pipelines.yml
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: Validate
jobs:
- job: Validate
steps:
- task: AzureCLI@2
inputs:
azureSubscription: 'Azure-Connection'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az bicep build --file main.bicep
az deployment sub what-if \
--location australiaeast \
--template-file main.bicep \
--parameters environment=dev
- stage: Deploy
dependsOn: Validate
jobs:
- deployment: Deploy
environment: 'Production'
strategy:
runOnce:
deploy:
steps:
- task: AzureCLI@2
inputs:
azureSubscription: 'Azure-Connection'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az deployment sub create \
--location australiaeast \
--template-file main.bicep \
--parameters environment=prod
Bicep in 2022 is production-ready and the best choice for Azure infrastructure as code.\n\n## Takeaways\n\nAdd a concise, personal takeaway and recommended next steps here.\n