Skip to content
Get Started for Free

Bicep

Bicep is a domain-specific language for declaring Azure resources. It is the recommended authoring format for Azure Resource Manager: the Azure CLI transpiles Bicep to ARM JSON and submits that to Resource Manager, so a Bicep deployment is an ARM deployment underneath.

Bicep needs no special configuration for the emulator. Once lstk az start-interception redirects the Azure CLI to the emulator, az deployment group create deploys Bicep files to the emulator. Most of the Azure samples provide a Bicep deployment, next to Azure CLI scripts and a Terraform configuration for the same stack.

This guide shows you how to deploy a storage account and a blob container with a Bicep file and a parameter file, and how to inspect the deployment.

  • The Azure CLI with the Bicep CLI. Run az bicep install, or let the Azure CLI install Bicep the first time that you use it.
  • LocalStack for Azure and the lstk CLI, with the emulator running. For more information, see Installation.

Redirect the az commands to the emulator, and check that the Bicep CLI is installed:

Terminal window
lstk az start-interception
az bicep version

lstk az start-interception changes your global Azure CLI configuration to use the LocalStack cloud until you run lstk az stop-interception. For more information, see Azure CLI.

Create a Bicep file that declares a storage account, its blob service, and a blob container, and returns the resource ID of the storage account as an output:

main.bicep
@minLength(3)
param storageAccountName string
param location string = resourceGroup().location
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
}
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
parent: storageAccount
name: 'default'
}
resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
parent: blobService
name: 'activities'
properties: {
publicAccess: 'None'
}
}
output storageAccountId string = storageAccount.id

A parameter file keeps the values out of the template:

main.bicepparam
using 'main.bicep'
param storageAccountName = 'lsdocsbicep001'

Create the resource group that the template deploys to:

Terminal window
az group create --name MyResourceGroup --location westeurope

az bicep build transpiles the Bicep file to ARM JSON without contacting Azure or the emulator, which makes it a fast syntax and type check:

Terminal window
az bicep build --file main.bicep --stdout

az deployment group validate also runs against the emulator, but the emulator doesn’t validate the template: it returns an empty result for every request.

Terminal window
az deployment group validate \
--resource-group MyResourceGroup \
--template-file main.bicep \
--parameters main.bicepparam
Output
{
"error": null,
"id": null,
"name": null,
"properties": null,
"type": null
}

Before it calls the emulator, the Azure CLI compiles the Bicep file and checks that every parameter has a value. A zero exit code therefore rules out syntax errors and missing parameters, but not the values that Azure rejects during validation, such as a parameter value that violates a @minLength constraint.

Deploy the Bicep file with the parameter file:

Terminal window
az deployment group create \
--resource-group MyResourceGroup \
--name my-deployment \
--template-file main.bicep \
--parameters main.bicepparam \
--query "{state:properties.provisioningState, outputs:properties.outputs}"

The --query argument reduces the deployment to its provisioning state and the template outputs:

Output
{
"outputs": {
"storageAccountId": {
"type": "String",
"value": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MyResourceGroup/providers/Microsoft.Storage/storageAccounts/lsdocsbicep001"
}
},
"state": "Succeeded"
}

List the resources of the resource group:

Terminal window
az resource list --resource-group MyResourceGroup --output table
Output
Name ResourceGroup Location Type Status
-------------- --------------- ---------- --------------------------------- ---------
lsdocsbicep001 MyResourceGroup westeurope Microsoft.Storage/storageAccounts Succeeded

The list contains only top-level resources: the blob service and the container are child resources of the storage account.

When a deployment reports Failed, list the deployment operations to see which resource failed and why, rather than reading the whole response:

Terminal window
az deployment operation group list \
--resource-group MyResourceGroup --name my-deployment \
--query "[].{resource:properties.targetResource.resourceType, \
state:properties.provisioningState, \
message:properties.statusMessage.error.message}" -o table

Delete the resource group and the resources that it contains:

Terminal window
az group delete --name MyResourceGroup --yes

To make the az commands target Azure again, stop the interception:

Terminal window
lstk az stop-interception

Bicep modules work as they do against Azure. The Function App with Service Bus sample splits its deployment into modules for the virtual network and its NAT gateway, the storage account, Service Bus, the private endpoints and private DNS zones, Log Analytics and Application Insights, and the Function App. The modules pass values to each other through their outputs. For example, each private DNS zone links to the virtual network that the network module creates:

module blobStoragePrivateDnsZone 'modules/private-dns-zone.bicep' = {
name: 'blobStoragePrivateDnsZone'
params: {
name: 'privatelink.blob.core.windows.net'
vnetId: network.outputs.virtualNetworkId
tags: tags
}
}

The following samples deploy their stack with Bicep, and also provide Azure CLI scripts and a Terraform configuration for the same stack:

The AKS cluster with tags, labels and taints tutorial deploys a full AKS stack with Bicep.

  • az deployment group what-if isn’t supported: the emulator doesn’t implement the What-If operation, so the command fails.
  • az deployment group validate doesn’t validate the template. For more information, see Check the template.
  • The emulator doesn’t delete resources in Complete mode. For more information, see Deployment modes.

For the other limitations of the Resource Manager emulator, see Resource Manager.

  • Resource types with a global name scope, such as storage accounts, key vaults, and container registries, collide across deployments as they do in Azure. Give each stack a distinct prefix or suffix, or reset the emulator state between runs.
  • Creating a key vault can take up to two minutes on the emulator. Allow for that in pipeline timeouts.
  • A template that references a container image expects the image to exist in the registry. The samples build and push the image before they deploy the template, and a template-only deployment leaves the container resource in a failed state.
Was this page helpful?