Azure Bicep has become my preferred tool for writing infrastructure-as-code for Azure, thanks to its modularity and effortless adaptability. This repository serves as a handy cheat sheet, offering code snippets and references for Azure Bicep.
Note
Community contributions are welcome :-)! If you have content that you want to add, please add it to the cheat sheet using a pull request.
A cheat sheet is a concise set of notes or a reference guide used for quick retrieval of essential information. It"s often a single page that contains summaries, commands, formulas, or procedures that someone might need to reference frequently, especially when learning a new topic or skill.
Azure Bicep is a domain-specific language (also known as DSL) designed by Microsoft for defining and deploying Azure resources in a declarative manner. It"s the next generation of Azure Resource Manager (ARM) templates, offering a cleaner syntax, improved type safety, and better support for modularization. While ARM templates use JSON syntax, Bicep uses a more concise syntax that aims to make it easier for developers to author and maintain Azure deployments.
Note
Click the arrow next to a topic to expand its content.
Basics
Declarations of new and existing resources, variables, parameters and outputs, etcetera.
resource resourceName "ResourceType@version" = {
name: "exampleResourceName"
properties: {
// resource properties here
}
}
resource resVnet "Microsoft.Network/virtualNetworks@2022-01-01" = {
name: "my-vnet"
}
resource resChildSubnet "Microsoft.Network/virtualNetworks/subnets@2022-01-01" = {
name: "${resVnet}/my-subnet"
}
resource resVnet "Microsoft.Network/virtualNetworks@2022-01-01" = {
name: "my-vnet"
}
resource resChildSubnet "Microsoft.Network/virtualNetworks/subnets@2022-01-01" = {
name: "my-subnet"
parent: resVnet
}
resource resVnet "Microsoft.Network/virtualNetworks@2022-01-01" = {
name: "my-vnet"
resource resChildSubnet "subnets" = {
name: "my-subnet"
}
}
resource resKeyVaultRef "Microsoft.KeyVault/vaults@2019-09-01" = existing {
name: "myExistingKeyVaultName"
}
resource resVnet "Microsoft.Network/virtualNetworks@2022-01-01" existing = {
name: "my-vnet"
resource resChildSubnet "subnets" existing = {
name: "my-subnet"
}
}
// access child resource
output outChildSubnetId string = resVnet::resChildSubnet.id
var varEnvironment = "dev"
There is no need to declare a datatype for a variable, because the type is inferred from the value.
param parStorageAccountName string
param parLocation string = resourceGroup().location
Available datatypes are: string
, bool
, int
, object
, array
and custom (user defined type)
.
@secure()
param parSecureParameter string
resource resPublicIp "Microsoft.Network/publicIPAddresses@2023-02-01" ={
name: parPublicIpName
tags: parTags
location: parLocation
zones: parAvailabilityZones
sku: parPublicIpSku
properties: parPublicIpProperties
}
output outPublicIpId string = resPublicIp.id
output outMyString string = "Hello!"
Available datatypes are: string
, bool
, int
, object
, array
and custom (user defined type)
.
var varGreeting = "Hello"
output outResult string = "${varGreeting} World"
var varMultiLineString = """
This is a
Muli-line string
variable.
"""
Modules
Split your deployment into smaller, reusable components.
module modVirtualNetwork "./network.bicep" = {
name: "networkModule"
params: {
parLocation: "westeurope"
parVnetName: "my-vnet-name"
}
}
module modBicepRegistryReference "br/<bicep registry name>:<file path>:<tag>" = {
name: "deployment-name"
params: {}
}
Conditions
Resource definitions based on conditions.
param parDeployResource bool
resource resDnsZone "Microsoft.Network/dnszones@2018-05-01" = if (parDeployResource) {
name: "myZone"
location: "global"
}
param parEnvironment string
var varSku = parEnvironment == "prod" ? "premium" : "standard"
Loops
Loop constructions.
param parStorageAccountNames array = [
"storageaccount1"
"storageaccount2"
"storageaccount3"
]
resource resStorageAccounts "Microsoft.Storage/storageAccounts@2021-04-01" = [for name in parStorageAccountNames: {
name: name
location: "westeurope"
kind: "StorageV2"
sku: {
name: "Standard_LRS"
}
}]
param parStorageAccountNames array = [
{
name: "storageaccount1"
kind: "StorageV2"
sku: {
name: "Standard_LRS"
}
}
{
name: "storageaccount2"
kind: "StorageV2"
sku: {
name: "Standard_LRS"
}
}
]
resource resStorageAccounts "Microsoft.Storage/storageAccounts@2021-04-01" = [for storageAccount in parStorageAccountNames: {
name: storageAccount.name
location: "westeurope"
kind: storageAccount.kind
sku: {
name: storageAccount.sku
}
}]
Data manipulation
Functions used to manipulate data.
var varGroceryStore = [
{
productName: "Icecream"
productPrice: 2
productCharacteristics: [
"Vegan"
"Seasonal"
]
}
{
productName: "Banana"
productPrice: 4
productCharacteristics: [
"Bio"
]
}
]
output outProducts array = filter(varGroceryStore, item => item.productPrice >= 4)
[
{
"productName": "Banana",
"productPrice": 4,
"productCharacteristics": [
"Bio"
]
}
]
output outDiscount array = map(range(0, length(varGroceryStore)), item => {
productNumber: item
productName: varGroceryStore[item].productName
discountedPrice: "The item ${varGroceryStore[item].productName} is on sale. Sale price: ${(varGroceryStore[item].productPrice / 2)}"
})
[
{
"productNumber": 0,
"productName": "Icecream",
"discountedPrice": "The item Icecream is on sale. Sale price: 1"
},
{
"productNumber": 1,
"productName": "Banana",
"discountedPrice": "The item Banana is on sale. Sale price: 2"
}
]
output outUsingSort array = sort(varGroceryStore, (a, b) => a.productPrice <= b.productPrice)
[
{
"productName": "Icecream",
"productPrice": 2,
"productCharacteristics": [
"Vegan"
"Seasonal"
]
},
{
"productName": "Banana",
"productPrice": 4,
"productCharacteristics": [
"Bio"
]
}
]
User Defined Types
Define custom complex data structures.
// a string type with two allowed strings ("Standard_LRS" or "Standard_GRS")
type skuType = "Standard_LRS" | "Standard_GRS"
// an integer type with one allowed value (1337)
type integerType = 1337
// an boolean type with one allowed value (true)
type booleanType = true
// Reference the type
param parMyStringType skuType
param parMyIntType integerType
param parMyBoolType booleanType
type arrayWithObjectsType = {
name: string
age: int
}[]
param parCustomArray arrayWithObjectsType = [
{
name: "John"
age: 30
}
]
type arrayWithObjectsType = {
name: string
age: int
hasChildren: bool?
hasPets: bool?
}[]
param parCustomArray arrayWithObjectsType = [
{
name: "John"
age: 30
}
{
name: "Jane"
age: 31
hasPets: true
}
{
name: "Jack"
age: 45
hasChildren: true
hasPets: true
}
]
User Defined Functions
Define custom complex expressions.
func <function-name> (<parameter-name> <data-type>) <return-type> => <expression>
func funcSayHelloTo() string => "Hello and welcome, John Doe"
func funcSayHelloTo(name string) string => "Hello and welcome, ${name}"
With multiple parameters:
func funcPersonNameAndAge(name string, age int) string => "My name is ${name} and my age is ${age}"
func funcReturnTypeArray() array => [1, 2, 3, 4, 5]
func funcReturnTypeObject() object => {name: "John Doe", age: 31}
func funcReturnTypeInt() int => 1337
func funcReturnTypeBool(key string) bool => contains({}, key)
func funcReturnTypeUserDefinedType() customTypeUsedAsReturnType => {
hello: "world"
}
type customTypeUsedAsReturnType = {
hello: string
}
Compile-time imports
Import and export() enable reuse of user-defined types variables, functions.
Supported in Bicep and Bicepparam files.
Supported in Bicep and Bicepparam files.
@export()
var region = "we"
@export()
type tagsType = {
Environment: "Prod" | "Dev" | "QA" | "Stage" | "Test"
CostCenter: string
Owner: string
BusinessUnit: string
*: string
}
import { region, tagsType } from "shared.bicep"
output outRegion string = region
output outTags tagsType = {
Environment: "Dev"
CostCenter: "12345"
BusinessUnit: "IT"
Owner: "John Lokerse"
}
using "keyVault.bicep"
import { region as importRegion } from "shared.bicep"
param parKeyVaultName = "kv-${importRegion}-${uniqueString(importRegion)}"
import * as shared from "shared.bicep"
output outRegion string = shared.region
output outTags shared.tagsType = {
Environment: "Dev"
CostCenter: "12345"
BusinessUnit: "IT"
Owner: "John Lokerse"
}
Networking
CIDR functions to make subnetting easier.
output outParseCidrInformation object = parseCidr("192.168.1.0/24")
"outParseCidrInformation": {
"type": "Object",
"value": {
"broadcast": "192.168.1.255",
"cidr": 24,
"firstUsable": "192.168.1.1",
"lastUsable": "192.168.1.254",
"netmask": "255.255.255.0",
"network": "192.168.1.0"
}
}
output outCidrSubnet string = cidrSubnet("192.168.1.0/24", 25, 0)
"outCidrSubnet": {
"type": "String",
"value": "192.168.1.0/25"
}
output outCidrHost array = [for i in range(0, 10): cidrHost("192.168.1.0/24", i)]
"outCidrHost": {
"type": "Array",
"value": [
"192.168.1.1",
"192.168.1.2",
"192.168.1.3",
"192.168.1.4",
"192.168.1.5",
"192.168.1.6",
"192.168.1.7",
"192.168.1.8",
"192.168.1.9",
"192.168.1.10"
]
}
Bicepconfig
Customize your Bicep development experience.
{
"moduleAliases": {
"br": {
"<bicep registry name>": {
"registry": "<url to registry>",
"modulePath": "<module path of the alias>"
}
}
}
}
Dependencies
Implicit and explicit dependencies.
resource resNetworkSecurityGroup "Microsoft.Network/networkSecurityGroups@2019-11-01" = {
name: "my-networkSecurityGroup"
location: resourceGroup().location
}
resource nsgRule "Microsoft.Network/networkSecurityGroups/securityRules@2019-11-01" = {
name: "${resNetworkSecurityGroup}/AllowAllRule"
properties: {
// resource properties here
}
}
resource resDnsZone "Microsoft.Network/dnsZones@2018-05-01" = {
name: "contoso.com"
location: "global"
}
module modVirtualNetwork "./network.bicep" = {
name: "networkModule"
params: {
parLocation: "westeurope"
parVnetName: "my-vnet-name"
}
dependsOn: [
resDnsZone
]
}
Deployment
Orchestration commands to deploy Azure Bicep to your Azure Environment.
Scope | Command |
---|---|
resourceGroup | az deployment group create --resource-group ResourceGroupName --template-file template.bicep --parameters parameters.bicepparam |
subscription | az deployment sub create --location location --template-file template.bicep --parameters parameters.bicepparam |
managementGroup | az deployment mg create --management-group-id ManagementGroupId --template-file template.bicep --parameters parameters.bicepparam |
tenant | az deployment tenant create --location location --template-file template.bicep --parameters parameters.bicepparam |
Scope | Command |
---|---|
resourceGroup | New-AzResourceGroupDeployment -ResourceGroupName "ResourceGroupName" -TemplateFile "template.bicep" -TemplateParameterFile "parameters.bicepparam |
subscription | New-AzDeployment -Location "Location" -TemplateFile "template.bicep" -TemplateParameterFile "parameters.bicepparam" |
managementGroup | New-AzManagementGroupDeployment -ManagementGroupId "ManagementGroupId" -Location "location" -TemplateFile "template.bicep" -TemplateParameterFile "parameters.bicepparam" |
tenant | New-AzTenantDeployment -Location "Location" -TemplateFile "template.bicep" -TemplateParameterFile "parameters.bicepparam" |
Target Scopes
Deployment scope definitions.
The targetScope
directive in Azure Bicep determines the level at which the Bicep template will be deployed within Azure. The default is targetScope = "resourceGroup"
.
Azure Bicep supports multiple levels of targetScope
:
Scope | Description |
---|---|
resourceGroup | The Bicep file is intended to be deployed at the Resource Group level. |
subscription | The Bicep file targets a Subscription, allowing you to manage resources or configurations across an entire subscription. |
managementGroup | For managing resources or configurations across multiple subscriptions under a specific Management Group. |
tenant | The highest scope, targeting the entire Azure tenant. This is useful for certain global resources or policies. |
targetScope = "resourceGroup"
resource resKeyVault "Microsoft.KeyVault/vaults@2019-09-01" = {
// key vault properties here
}
Use the scope property on modules to deploy on a different scope than the target scope:
// Uses the targetScope
module modStorageModule1 "storage.bicep" = {
name: "storageModule1"
}
// Uses the scope of the module
module modStorageModule2 "storage.bicep" = {
name: "storageModule2"
scope: resourceGroup("other-subscription-id", "other-resource-group-name")
// module properties here
}