You’ve probably heard about Azure Resource Manager (ARM) – the deployment and management service/layer of Azure, which enables you to manage (create, configure, delete) your Azure resources. Also, you are probably aware that ARM uses so called ARM templates – basically, JSON files that actually define the infrastructure and configuration you want to deploy to Azure (think Infrastructure as Code, IaC).
So, if you have dealt with ARM/JSON in the past, you may have been finding it difficult to start with, and somewhat complex.
Bicep is here to help.
Here is a short overview of Bicep – basically, it’s a language which enables you easier deployment of Azure resources, without messing around (too much) with JSON. To be frank, it somehow reminds of Terraform, but it’s also different. It has many cool features, immediately supports all new Azure features and APIs, can be built (converted) into .json and deployed as such or it can be deployed straight away as .bicep, doesn’t require state file, it’s open and free, has great support in Visual Studio Code and much more. And it’s still in active development!
If you’re dealing with IaC and Azure, try it.
To show you the power (and simplicity) of Bicep, here is a short example of deploying Linux virtual machine in Azure (together with a resource group, virtual network, virtual network subnet, virtual NIC and network security group), done “the old way” (in JSON, which was actually converted from Bicep… it’s easier than writing JSON from the scratch) and then done via Bicep (“the right way”? 😀).
Additionally, you’ll see that I’ve tried to break stuff into modules – with more or less sucess. 😀
The ARM/JSON way (could be done nicer/shorter, with parameters inside .parameters.json… if you know what you’re doing – this is converted from Bicep and serves just for illustrative purposes):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
{ "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", "version": "0.4.451.19169", "templateHash": "6031449369953332876" } }, "parameters": { "resourceGroupName": { "type": "string", "defaultValue": "Gym" }, "location": { "type": "string", "defaultValue": "westeurope" } }, "functions": [], "resources": [ { "type": "Microsoft.Resources/resourceGroups", "apiVersion": "2021-04-01", "name": "[parameters('resourceGroupName')]", "location": "[parameters('location')]" }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2019-10-01", "name": "vm-module", "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { "location": { "value": "[parameters('location')]" }, "adminName": { "value": "tomica" }, "adminSSHKey": { "value": "ssh-rsa <SOME SSH PUBLIC KEY>" }, "machineName": { "value": "tkVM" }, "machineSize": { "value": "Standard_A1_v2" }, "networkName": { "value": "tkNet" }, "networkSubnet": { "value": "default" }, "networkSG": { "value": "tkNetSG" } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", "version": "0.4.451.19169", "templateHash": "141276028935159012" } }, "parameters": { "location": { "type": "string" }, "adminName": { "type": "string" }, "adminSSHKey": { "type": "string" }, "machineName": { "type": "string" }, "machineSize": { "type": "string" }, "networkName": { "type": "string" }, "networkSubnet": { "type": "string" }, "networkSG": { "type": "string" } }, "functions": [], "variables": { "networkNIC": "[format('{0}-nic', parameters('machineName'))]", "networkSubnetID": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('networkName'), parameters('networkSubnet'))]", "osConfiguration": { "disablePasswordAuthentication": true, "ssh": { "publicKeys": [ { "path": "[format('/home/{0}/.ssh/authorized_keys', parameters('adminName'))]", "keyData": "[parameters('adminSSHKey')]" } ] } } }, "resources": [ { "type": "Microsoft.Network/networkSecurityGroups", "apiVersion": "2021-02-01", "name": "[parameters('networkSG')]", "location": "[parameters('location')]", "properties": { "securityRules": [ { "name": "allow-ssh", "properties": { "priority": 1000, "access": "Allow", "direction": "Inbound", "destinationPortRange": "22", "protocol": "Tcp", "sourceAddressPrefix": "*", "sourcePortRange": "*", "destinationAddressPrefix": "*" } } ] } }, { "type": "Microsoft.Network/virtualNetworks", "apiVersion": "2021-02-01", "name": "[parameters('networkName')]", "location": "[parameters('location')]", "properties": { "addressSpace": { "addressPrefixes": [ "10.0.0.0/16" ] }, "subnets": [ { "name": "[parameters('networkSubnet')]", "properties": { "addressPrefix": "10.0.3.0/24", "networkSecurityGroup": { "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSG'))]" } } } ] }, "dependsOn": [ "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSG'))]" ] }, { "type": "Microsoft.Network/networkInterfaces", "apiVersion": "2021-02-01", "name": "[variables('networkNIC')]", "location": "[parameters('location')]", "properties": { "ipConfigurations": [ { "name": "ipConfig", "properties": { "privateIPAllocationMethod": "Dynamic", "subnet": { "id": "[variables('networkSubnetID')]" } } } ] }, "dependsOn": [ "[resourceId('Microsoft.Network/virtualNetworks', parameters('networkName'))]" ] }, { "type": "Microsoft.Compute/virtualMachines", "apiVersion": "2021-03-01", "name": "[parameters('machineName')]", "location": "[parameters('location')]", "properties": { "hardwareProfile": { "vmSize": "[parameters('machineSize')]" }, "osProfile": { "computerName": "[parameters('machineName')]", "adminUsername": "[parameters('adminName')]", "adminPassword": "[parameters('adminSSHKey')]", "linuxConfiguration": "[variables('osConfiguration')]" }, "storageProfile": { "imageReference": { "publisher": "Canonical", "offer": "UbuntuServer", "sku": "18.04-LTS", "version": "latest" } }, "networkProfile": { "networkInterfaces": [ { "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkNIC'))]" } ] } }, "dependsOn": [ "[resourceId('Microsoft.Network/networkInterfaces', variables('networkNIC'))]" ] } ] } }, "dependsOn": [ "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" ] } ] } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", "version": "0.4.451.19169", "templateHash": "141276028935159012" } }, "parameters": { "location": { "type": "string" }, "adminName": { "type": "string" }, "adminSSHKey": { "type": "string" }, "machineName": { "type": "string" }, "machineSize": { "type": "string" }, "networkName": { "type": "string" }, "networkSubnet": { "type": "string" }, "networkSG": { "type": "string" } }, "functions": [], "variables": { "networkNIC": "[format('{0}-nic', parameters('machineName'))]", "networkSubnetID": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('networkName'), parameters('networkSubnet'))]", "osConfiguration": { "disablePasswordAuthentication": true, "ssh": { "publicKeys": [ { "path": "[format('/home/{0}/.ssh/authorized_keys', parameters('adminName'))]", "keyData": "[parameters('adminSSHKey')]" } ] } } }, "resources": [ { "type": "Microsoft.Network/networkSecurityGroups", "apiVersion": "2021-02-01", "name": "[parameters('networkSG')]", "location": "[parameters('location')]", "properties": { "securityRules": [ { "name": "allow-ssh", "properties": { "priority": 1000, "access": "Allow", "direction": "Inbound", "destinationPortRange": "22", "protocol": "Tcp", "sourceAddressPrefix": "*", "sourcePortRange": "*", "destinationAddressPrefix": "*" } } ] } }, { "type": "Microsoft.Network/virtualNetworks", "apiVersion": "2021-02-01", "name": "[parameters('networkName')]", "location": "[parameters('location')]", "properties": { "addressSpace": { "addressPrefixes": [ "10.0.0.0/16" ] }, "subnets": [ { "name": "[parameters('networkSubnet')]", "properties": { "addressPrefix": "10.0.3.0/24", "networkSecurityGroup": { "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSG'))]" } } } ] }, "dependsOn": [ "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSG'))]" ] }, { "type": "Microsoft.Network/networkInterfaces", "apiVersion": "2021-02-01", "name": "[variables('networkNIC')]", "location": "[parameters('location')]", "properties": { "ipConfigurations": [ { "name": "ipConfig", "properties": { "privateIPAllocationMethod": "Dynamic", "subnet": { "id": "[variables('networkSubnetID')]" } } } ] }, "dependsOn": [ "[resourceId('Microsoft.Network/virtualNetworks', parameters('networkName'))]" ] }, { "type": "Microsoft.Compute/virtualMachines", "apiVersion": "2021-03-01", "name": "[parameters('machineName')]", "location": "[parameters('location')]", "properties": { "hardwareProfile": { "vmSize": "[parameters('machineSize')]" }, "osProfile": { "computerName": "[parameters('machineName')]", "adminUsername": "[parameters('adminName')]", "adminPassword": "[parameters('adminSSHKey')]", "linuxConfiguration": "[variables('osConfiguration')]" }, "storageProfile": { "imageReference": { "publisher": "Canonical", "offer": "UbuntuServer", "sku": "18.04-LTS", "version": "latest" } }, "networkProfile": { "networkInterfaces": [ { "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkNIC'))]" } ] } }, "dependsOn": [ "[resourceId('Microsoft.Network/networkInterfaces', variables('networkNIC'))]" ] } ] } |
The Bicep way:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// main.bicep targetScope = 'subscription' param resourceGroupName string = 'Gym' param location string = 'westeurope' resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {   name: resourceGroupName   location: location } module vm 'vm.bicep' = {   name: 'vm-module'   scope: rg   params: {     location: location     adminName: 'tomica'     adminSSHKey: 'ssh-rsa <SOME SSH PUBLIC KEY>'     machineName: 'tkVM'     machineSize: 'Standard_A1_v2'     networkName: 'tkNet'     networkSubnet: 'default'     networkSG: 'tkNetSG'   } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
// vm.bicep targetScope = 'resourceGroup' param location string param adminName string param adminSSHKey string param machineName string param machineSize string param networkName string param networkSubnet string param networkSG string var networkNIC = '${machineName}-nic' var networkSubnetID = resourceId('Microsoft.Network/virtualNetworks/subnets', networkName, networkSubnet) var osConfiguration = { disablePasswordAuthentication: true ssh: { publicKeys: [ { path: '/home/${adminName}/.ssh/authorized_keys' keyData: adminSSHKey } ] } } resource networkSecurityGroupName 'Microsoft.Network/networkSecurityGroups@2021-02-01' = { name: networkSG location: location properties: { securityRules: [ { name: 'allow-ssh' properties: { priority: 1000 access: 'Allow' direction: 'Inbound' destinationPortRange: '22' protocol: 'Tcp' sourceAddressPrefix: '*' sourcePortRange: '*' destinationAddressPrefix: '*' } } ] } } resource virtualNetworkName 'Microsoft.Network/virtualNetworks@2021-02-01' = { name: networkName location: location properties: { addressSpace: { addressPrefixes: [ '10.0.0.0/16' ] } subnets: [ { name: networkSubnet properties: { addressPrefix: '10.0.3.0/24' networkSecurityGroup: { id: networkSecurityGroupName.id } } } ] } } resource nicName 'Microsoft.Network/networkInterfaces@2021-02-01' = { name: networkNIC location: location properties: { ipConfigurations: [ { name: 'ipConfig' properties: { privateIPAllocationMethod: 'Dynamic' subnet: { id: networkSubnetID } } } ] } dependsOn: [ virtualNetworkName ] } resource vmName 'Microsoft.Compute/virtualMachines@2021-03-01' = { name: machineName location: location properties: { hardwareProfile: { vmSize: machineSize } osProfile: { computerName: machineName adminUsername: adminName adminPassword: adminSSHKey linuxConfiguration: osConfiguration } storageProfile: { imageReference: { publisher: 'Canonical' offer: 'UbuntuServer' sku: '18.04-LTS' version: 'latest' } } networkProfile: { networkInterfaces: [ { id: nicName.id } ] } } } |
Bicep seems a bit easier to read and shorter, right (while still doing basically the same thing)? 😀
If we deploy the .bicep files above (note that I’m deploying the “raw” .bicep file directly – which is cool!):
So, where should you start if you’re new to Bicep?
I would certainly recommend starting with free and official Deploy and manage resources in Azure by using Bicep learning path on Microsoft Learn.
After that, you can probably pick up Freek Berson’s book Getting started with Bicep: Infrastructure as Code on Azure (first and only book on Bicep that I know of – really liked it because of the simple (yet effective) examples with storage accounts, it connects everything and flows naturally – building up “brick by brick” and not “jumping around”, just to show off what Bicep can do).
Another great resource are also the Bicep examples – there’s plenty to learn from them too!
Of course, you’ll also need to practice – install the Azure CLI or Azure PowerShell module, add Bicep and use Visual Studio Code for your first steps with creating, deleting, configuring and breaking stuff… powered by Bicep! 😀
Cheers!