3 min read
Azure Bicep in 2022: The New Standard for Infrastructure as Code
Azure Bicep has matured significantly and is now the recommended way to author ARM templates. Let’s explore the latest features and best practices for 2022.
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.