{"id":109,"date":"2026-02-02T12:00:00","date_gmt":"2026-02-02T12:00:00","guid":{"rendered":"https:\/\/rezachegini.com\/?p=109"},"modified":"2026-10-30T07:04:17","modified_gmt":"2026-10-30T07:04:17","slug":"from-arm-to-bicep-why-i-made-the-switch","status":"publish","type":"post","link":"https:\/\/rezachegini.com\/?p=109","title":{"rendered":"From ARM to Bicep: Why I Made the Switch"},"content":{"rendered":"<p class=\"wp-block-paragraph\">Hello everyone! After learning ARM templates, I started to learn Bicep. Let me show you why I made the switch from ARM JSON to Bicep.<\/p><p class=\"wp-block-paragraph\">While I was learning ARM templates, I heard people talking about Bicep. I was curious &#8211; what is Bicep? Why do people prefer it over ARM templates?<\/p><p class=\"wp-block-paragraph\">I tried it and immediately understood. Bicep is SO much easier to read and write!<\/p><h3 class=\"wp-block-heading\">What is Bicep?<\/h3><p class=\"wp-block-paragraph\">Bicep is a new language for deploying Azure resources. It does the same thing as ARM templates, but with cleaner syntax.<\/p><p class=\"wp-block-paragraph\">Think of it like this:<\/p><ul class=\"wp-block-list\"><li>ARM templates = Writing in formal business language<\/li><li>Bicep = Writing in normal, everyday language<\/li><\/ul><p class=\"wp-block-paragraph\">Both get the job done, but Bicep is much easier!<\/p><h3 class=\"wp-block-heading\">My First Comparison: Creating a Storage Account<\/h3><p class=\"wp-block-paragraph\">Let me show you the difference.<\/p><p class=\"wp-block-paragraph\">ARM Template (JSON):<\/p><pre class=\"wp-block-code\"><code>{\n  \"$schema\": \"https:\/\/schema.management.azure.com\/schemas\/2019-04-01\/deploymentTemplate.json#\",\n  \"contentVersion\": \"1.0.0.0\",\n  \"parameters\": {},\n  \"functions\": [],\n  \"variables\": {},\n  \"resources\": [\n    {\n      \"name\": \"rezastore8677676\",\n      \"type\": \"Microsoft.Storage\/storageAccounts\",\n      \"apiVersion\": \"2021-04-01\",\n      \"location\": \"[resourceGroup().location]\",\n      \"sku\": {\n        \"name\": \"Standard_LRS\"\n      },\n      \"kind\": \"StorageV2\"\n    }\n  ],\n  \"outputs\": {}\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Same thing in Bicep:<\/p><pre class=\"wp-block-code\"><code>resource sa 'Microsoft.Storage\/storageAccounts@2025-01-01' = {\n  name : 'rezastorageaccount'\n  location: 'eastus'\n  sku: {\n    name: 'Standard_LRS'\n  }\n  kind: 'StorageV2'\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Look at the difference! Bicep is:<\/p><ul class=\"wp-block-list\"><li>7 lines vs 20 lines<\/li><li>No brackets everywhere<\/li><li>No quotes around property names<\/li><li>Much easier to read!<\/li><\/ul><h3 class=\"wp-block-heading\">Why Bicep is Better: 5 Big Reasons<\/h3><h3 class=\"wp-block-heading\">Reason 1: No More Schema and ContentVersion<\/h3><p class=\"wp-block-paragraph\">ARM template must start with:<\/p><pre class=\"wp-block-code\"><code>{\n  \"$schema\": \"https:\/\/schema.management.azure.com\/schemas\/2019-04-01\/deploymentTemplate.json#\",\n  \"contentVersion\": \"1.0.0.0\",\n  \"parameters\": {},\n  \"functions\": [],\n  \"variables\": {},\n  \"resources\": [],\n  \"outputs\": {}\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep: Just start writing! No schema needed.<\/p><pre class=\"wp-block-code\"><code>resource sa 'Microsoft.Storage\/storageAccounts@2025-01-01' = {\n  name : 'rezastorageaccount'\n  location: 'eastus'\n  sku: {\n    name: 'Standard_LRS'\n  }\n  kind: 'StorageV2'\n}<\/code><\/pre><h3 class=\"wp-block-heading\">Reason 2: Resource Type and API Version Together<\/h3><p class=\"wp-block-paragraph\">ARM template separates type and apiVersion:<\/p><pre class=\"wp-block-code\"><code>{\n  \"name\": \"rezastore8677676\",\n  \"type\": \"Microsoft.Storage\/storageAccounts\",\n  \"apiVersion\": \"2021-04-01\",\n  ...\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep combines them in one line:<\/p><pre class=\"wp-block-code\"><code>resource sa 'Microsoft.Storage\/storageAccounts@2025-01-01' = {\n  ...\n}<\/code><\/pre><p class=\"wp-block-paragraph\">This is cleaner! Type and version are together.<\/p><h3 class=\"wp-block-heading\">Reason 3: No Square Brackets for Functions<\/h3><p class=\"wp-block-paragraph\">ARM template uses square brackets everywhere:<\/p><pre class=\"wp-block-code\"><code>\"location\": \"[resourceGroup().location]\"<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep: Just write it normally!<\/p><pre class=\"wp-block-code\"><code>location: resourceGroup().location<\/code><\/pre><p class=\"wp-block-paragraph\">No brackets needed! It&#8217;s like writing normal code.<\/p><h3 class=\"wp-block-heading\">Reason 4: Resource Names Are Shorter<\/h3><p class=\"wp-block-paragraph\">ARM template needs full resource path:<\/p><pre class=\"wp-block-code\"><code>\"[resourceId('Microsoft.Network\/virtualNetworks\/subnets', 'appnetwork', 'SubnetA')]\"<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep: Just use the resource name!<\/p><pre class=\"wp-block-code\"><code>subnet.id<\/code><\/pre><p class=\"wp-block-paragraph\">Much simpler!<\/p><h3 class=\"wp-block-heading\">Reason 5: IntelliSense and Validation<\/h3><p class=\"wp-block-paragraph\">When I write Bicep in VS Code:<\/p><ul class=\"wp-block-list\"><li>IntelliSense shows me available properties<\/li><li>I get autocomplete suggestions<\/li><li>Errors show up immediately<\/li><li>I can hover to see documentation<\/li><\/ul><p class=\"wp-block-paragraph\">With ARM templates, I had to check documentation constantly!<\/p><h3 class=\"wp-block-heading\">My Real Examples: Side by Side Comparison<\/h3><h3 class=\"wp-block-heading\">Example 1: Creating Resource Group<\/h3><p class=\"wp-block-paragraph\">ARM template (you can&#8217;t create RG in template &#8211; need Azure CLI):<\/p><pre class=\"wp-block-code\"><code>az group create --name reza-rg --location eastus<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep can create resource group!<\/p><pre class=\"wp-block-code\"><code>targetScope = 'subscription'\n\nresource rg 'Microsoft.Resources\/resourceGroups@2025-04-01' ={\n  name: 'reza-eus'\n  location: 'eastus'\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Deploy it:<\/p><pre class=\"wp-block-code\"><code>az deployment sub create --location eastus --template-file 01-rg.bicep<\/code><\/pre><p class=\"wp-block-paragraph\">This was my first &#8220;wow&#8221; moment with Bicep!<\/p><h3 class=\"wp-block-heading\">Example 2: Creating Multiple Storage Accounts<\/h3><p class=\"wp-block-paragraph\">ARM template with copy loop (complicated!):<\/p><pre class=\"wp-block-code\"><code>{\n  \"type\": \"Microsoft.Storage\/storageAccounts\",\n  \"apiVersion\": \"2021-04-01\",\n  \"name\": \"[concat('storage', copyIndex())]\",\n  \"location\": \"eastus\",\n  \"sku\": {\n    \"name\": \"Standard_LRS\"\n  },\n  \"kind\": \"StorageV2\",\n  \"copy\": {\n    \"name\": \"storagecopy\",\n    \"count\": 3\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep with for loop (simple!):<\/p><pre class=\"wp-block-code\"><code>resource sa 'Microsoft.Storage\/storageAccounts@2025-01-01' = [for i in range(0,3):{\n  name : '${i}rezastorageaccount'\n  location: 'eastus'\n  sku: {\n    name: 'Standard_LRS'\n  }\n  kind: 'StorageV2'\n}]<\/code><\/pre><p class=\"wp-block-paragraph\">The Bicep for loop is much easier to understand!<\/p><h3 class=\"wp-block-heading\">Example 3: Creating Virtual Network<\/h3><p class=\"wp-block-paragraph\">ARM template:<\/p><pre class=\"wp-block-code\"><code>{\n  \"name\": \"appnetwork\",\n  \"type\": \"Microsoft.Network\/virtualNetworks\",\n  \"apiVersion\": \"2020-11-01\",\n  \"location\": \"North Europe\",\n  \"properties\": {\n    \"addressSpace\": {\n      \"addressPrefixes\": [\n        \"10.0.0.0\/16\"\n      ]\n    },\n    \"subnets\": [\n      {\n        \"name\": \"SubnetA\",\n        \"properties\": {\n          \"addressPrefix\": \"10.0.0.0\/24\"\n        }\n      },\n      {\n        \"name\": \"SubnetB\",\n        \"properties\": {\n          \"addressPrefix\": \"10.0.1.0\/24\"\n        }\n      }\n    ]\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep:<\/p><pre class=\"wp-block-code\"><code>resource vnet 'Microsoft.Network\/virtualNetworks@2024-10-01' ={\n  name: 'ezaeus'\n  location: 'eastus'\n  properties: {\n    addressSpace: {addressPrefixes:['10.0.0.0\/16']}\n    subnets:[\n      {\n        name:'snet-dev-eus-web-01'\n        properties:{addressPrefix:'10.0.0.0\/24'}\n      }\n      {\n        name:'snet-dev-eus-db-01'\n        properties:{addressPrefix:'10.0.1.0\/24'}\n      }\n    ]\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep is cleaner &#8211; no quotes around property names!<\/p><h3 class=\"wp-block-heading\">Example 4: Creating Network Security Group<\/h3><p class=\"wp-block-paragraph\">ARM template &#8211; lots of nesting:<\/p><pre class=\"wp-block-code\"><code>{\n  \"name\": \"app-nsg\",\n  \"type\": \"Microsoft.Network\/networkSecurityGroups\",\n  \"apiVersion\": \"2020-11-01\",\n  \"location\": \"[resourceGroup().location]\",\n  \"properties\": {\n    \"securityRules\": [\n      {\n        \"name\": \"Allow-RDP\",\n        \"properties\": {\n          \"description\": \"description\",\n          \"protocol\": \"Tcp\",\n          \"sourcePortRange\": \"*\",\n          \"destinationPortRange\": \"3389\",\n          \"sourceAddressPrefix\": \"*\",\n          \"destinationAddressPrefix\": \"*\",\n          \"access\": \"Allow\",\n          \"priority\": 100,\n          \"direction\": \"Inbound\"\n        }\n      }\n    ]\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep &#8211; using variables for clarity:<\/p><pre class=\"wp-block-code\"><code>var nsgName           = 'nsg-az104-shared-eus-01'\nvar any               = '*'\n\nvar ruleSshName       = 'in-allow-rdp-admin'\nvar ruleSshPriority   = 400\nvar SshPort           = '22'\n\nvar svcTagInternet    = 'Internet'\nvar httpDestIp        = '10.0.0.4'\nvar httpPort          = '80'\nvar ruleHttpName      = 'in-allow-http-from-internet-to-10-0-0-4'\nvar ruleHttpPriority  = 410\n\nresource nsg 'Microsoft.Network\/networkSecurityGroups@2024-10-01'={ \n  name: nsgName\n  location: resourceGroup().location\n  properties: { \n    securityRules: [ \n      {\n        name: ruleSshName\n        properties: {\n          description: 'Allow SSH for admin access.'\n          protocol: 'Tcp'\n          sourcePortRange: any\n          destinationPortRange: SshPort\n          sourceAddressPrefix: any\n          destinationAddressPrefix: any\n          access: 'Allow'\n          priority: ruleSshPriority\n          direction: 'Inbound'\n        }\n      }\n      {\n        name: ruleHttpName\n        properties: {\n          description: 'Allow HTTP from the Internet service tag to 10.0.0.4.'\n          protocol: 'Tcp'\n          sourcePortRange: any\n          destinationPortRange: httpPort\n          sourceAddressPrefix: svcTagInternet\n          destinationAddressPrefix: httpDestIp\n          access: 'Allow'\n          priority: ruleHttpPriority\n          direction: 'Inbound'\n        }\n      }\n    ]\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">I love this! Variables at the top make the code self-documenting. I can see what each value means!<\/p><h3 class=\"wp-block-heading\">Example 5: Referencing Existing Resources<\/h3><p class=\"wp-block-paragraph\">This is where Bicep really shines!<\/p><p class=\"wp-block-paragraph\">ARM template &#8211; checking if resource exists is hard:<\/p><pre class=\"wp-block-code\"><code>{\n  \"type\": \"Microsoft.Network\/networkInterfaces\",\n  \"name\": \"app-interface\",\n  \"properties\": {\n    \"ipConfigurations\": [\n      {\n        \"properties\": {\n          \"subnet\": {\n            \"id\": \"[resourceId('Microsoft.Network\/virtualNetworks\/subnets', 'appnetwork', 'SubnetA')]\"\n          }\n        }\n      }\n    ]\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep &#8211; use existing keyword:<\/p><pre class=\"wp-block-code\"><code>resource vnet 'Microsoft.Network\/virtualNetworks@2024-07-01' existing = {\n  name: 'vnet-dev-eus-01'\n}\n\nresource subnet 'Microsoft.Network\/virtualNetworks\/subnets@2024-07-01' existing = {\n  name: 'snet-dev-eus-web-01'\n  parent: vnet\n}\n\nresource pip 'Microsoft.Network\/publicIPAddresses@2024-10-01' existing = { \n    name: 'pip-web-dev-eus-01'\n}\n\nresource nic 'Microsoft.Network\/networkInterfaces@2024-10-01' = { \n    name:'nic-web-dev-eus-01'\n    location: resourceGroup().location\n    properties: { \n       ipConfigurations:[\n        {\n          name:'ipconfig-web-01'\n          properties: { \n              privateIPAllocationMethod: 'Dynamic'\n              subnet: { \n                 id: subnet.id\n              }\n              publicIPAddress: { \n                id: pip.id\n              }\n          }\n        }\n       ]\n    }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">See how clean this is! I declare existing resources at the top, then just use subnet.id and pip.id. So simple!<\/p><h3 class=\"wp-block-heading\">Example 6: Creating Virtual Machine<\/h3><p class=\"wp-block-paragraph\">ARM template is very long. Let me show you Bicep version:<\/p><pre class=\"wp-block-code\"><code>resource nic 'Microsoft.Network\/networkInterfaces@2024-10-01' existing = { \n  name: 'nic-web-dev-eus-01'\n}\n\nresource sa 'Microsoft.Storage\/storageAccounts@2025-01-01' = {\n  name : 'stcloudhubdev2000'\n  location: 'eastus'\n  sku: {\n    name: 'Standard_LRS'\n  }\n  kind: 'StorageV2'\n}\n\nvar bootDiagBlobEndpoint = sa.properties.primaryEndpoints.blob\n\nresource vm 'Microsoft.Compute\/virtualMachines@2024-11-01' = { \n  name: 'vm-dev-eus-web-01'\n  location: resourceGroup().location\n  properties: {\n    hardwareProfile: {\n      vmSize: 'Standard_B1s'\n    }\n\n    osProfile: {\n      computerName: 'vm-dev-eus-web-01'\n      adminUsername: 'linuxadmin'\n      adminPassword: 'Azure@123'\n      linuxConfiguration: {\n        disablePasswordAuthentication: false\n      }\n    }\n\n    storageProfile: {\n      imageReference: {\n        publisher: 'Canonical'\n        offer: 'ubuntu-24_04-lts'   \n        sku: 'server'\n        version: 'latest'\n      }\n\n      osDisk: {\n        name: 'osdisk-vm-dev-eus-web-01'\n        caching: 'ReadWrite'\n        createOption: 'FromImage'\n        managedDisk: {\n          storageAccountType: 'Standard_LRS'\n        }\n      }\n    }\n\n    networkProfile: { \n       networkInterfaces: [ {\n        id:nic.id\n       }]\n    }\n\n    diagnosticsProfile: { \n      bootDiagnostics: {\n        enabled:true\n        storageUri:bootDiagBlobEndpoint\n      }\n    }\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Look at this line:<\/p><pre class=\"wp-block-code\"><code>var bootDiagBlobEndpoint = sa.properties.primaryEndpoints.blob<\/code><\/pre><p class=\"wp-block-paragraph\">In ARM template, this would be:<\/p><pre class=\"wp-block-code\"><code>\"[reference(resourceId('Microsoft.Storage\/storageAccounts\/', toLower('rezastore8677676'))).primaryEndpoints.blob]\"<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep is SO much cleaner!<\/p><h3 class=\"wp-block-heading\">Example 7: Using Parameters<\/h3><p class=\"wp-block-paragraph\">ARM template:<\/p><pre class=\"wp-block-code\"><code>{\n  \"$schema\": \"https:\/\/schema.management.azure.com\/schemas\/2019-04-01\/deploymentTemplate.json#\",\n  \"contentVersion\": \"1.0.0.0\",\n  \"parameters\": {\n    \"vmName\": {\n      \"type\": \"string\",\n      \"metadata\": {\n        \"description\": \"Virtual machine name\"\n      }\n    },\n    \"adminUsername\": {\n      \"type\": \"string\",\n      \"metadata\": {\n        \"description\": \"Admin username for the VM\"\n      }\n    },\n    \"adminPassword\": {\n      \"type\": \"secureString\",\n      \"metadata\": {\n        \"description\": \"Admin password for the VM\"\n      }\n    }\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep:<\/p><pre class=\"wp-block-code\"><code>@description('Virtual machine name')\nparam vmName string\n\n@description('Admin username for the VM')\nparam adminUsername string\n\n@secure()\n@description('Admin password for the VM')\nparam adminPassword string<\/code><\/pre><p class=\"wp-block-paragraph\">Bicep parameters are cleaner! The @description decorator is right above the parameter.<\/p><h3 class=\"wp-block-heading\">Example 8: Availability Sets and Data Disks<\/h3><p class=\"wp-block-paragraph\">Here&#8217;s a more complex Bicep example I created:<\/p><pre class=\"wp-block-code\"><code>resource nic 'Microsoft.Network\/networkInterfaces@2024-10-01' existing = { \n  name: 'nic-web-dev-eus-01'\n}\n\nresource avset 'Microsoft.Compute\/availabilitySets@2025-04-01' = { \n name: 'avset-web-dev-eus-01'\n  location: resourceGroup().location\n  sku: { \n     name: 'Aligned'\n  }\n  properties: {\n    platformFaultDomainCount:2\n    platformUpdateDomainCount: 5\n  }\n}\n\nresource dataDisk 'Microsoft.Compute\/disks@2025-01-02' = { \n   name: 'disk-web-data-dev-eus-01'\n  location: resourceGroup().location\n  sku : { \n    name: 'Standard_LRS'\n  }\n  properties: { \n    creationData: {createOption:'Empty'}\n    diskSizeGB: 16\n  }\n}\n\nresource sa 'Microsoft.Storage\/storageAccounts@2025-01-01' = {\n  name : 'stcloudhubdev2000'\n  location: 'eastus'\n  sku: {\n    name: 'Standard_LRS'\n  }\n  kind: 'StorageV2'\n}\n\nvar bootDiagBlobEndpoint = sa.properties.primaryEndpoints.blob\n\nresource vm 'Microsoft.Compute\/virtualMachines@2024-11-01' = { \n  name: 'vm-dev-eus-web-01'\n  location: resourceGroup().location\n  properties: {\n    hardwareProfile: {\n      vmSize: 'Standard_B1s'\n    }\n\n    availabilitySet: { \n      id: avset.id\n    }\n\n    osProfile: {\n      computerName: 'vm-dev-eus-web-01'\n      adminUsername: 'linuxadmin'\n      adminPassword: 'Azure@123'\n      linuxConfiguration: {\n        disablePasswordAuthentication: false\n      }\n    }\n\n    storageProfile: {\n      imageReference: {\n        publisher: 'Canonical'\n        offer: 'ubuntu-24_04-lts'   \n        sku: 'server'\n        version: 'latest'\n      }\n\n      osDisk: {\n        name: 'osdisk-vm-dev-eus-web-01'\n        caching: 'ReadWrite'\n        createOption: 'FromImage'\n        managedDisk: {\n          storageAccountType: 'Standard_LRS'\n        }\n      }\n\n      dataDisks:[ {\n        createOption: 'Attach'\n        lun: 0\n        managedDisk: { \n          id: dataDisk.id\n        }\n      }]\n    }\n\n    networkProfile: { \n       networkInterfaces: [ {\n        id:nic.id\n       }]\n    }\n\n    diagnosticsProfile: { \n      bootDiagnostics: {\n        enabled:true\n        storageUri:bootDiagBlobEndpoint\n      }\n    }\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">I love how I can reference other resources easily: avset.id, dataDisk.id, nic.id. In ARM templates, these would be long resourceId() functions!<\/p><h3 class=\"wp-block-heading\">Example 9: Custom Script Extension<\/h3><p class=\"wp-block-paragraph\">Here&#8217;s how I add Nginx to my VM automatically:<\/p><pre class=\"wp-block-code\"><code>resource vm 'Microsoft.Compute\/virtualMachines@2024-11-01' = { \n  name: 'vm-dev-eus-web-01'\n  location: resourceGroup().location\n  properties: {\n    \/\/ ... VM configuration\n  }\n}\n\nresource cse 'Microsoft.Compute\/virtualMachines\/extensions@2021-04-01' = {\n  name: 'ext-nginx-setup'            \n  parent: vm                          \n  location: resourceGroup().location\n  properties: {\n    publisher: 'Microsoft.Azure.Extensions'\n    type: 'CustomScript'\n    typeHandlerVersion: '2.1'\n    autoUpgradeMinorVersion: true\n  \n    settings: {\n      fileUris: [\n        'https:\/\/stcloudhubdev2000.blob.core.windows.net\/scripts\/setup-nginx.sh?sp=r&amp;st=2025-10-07T12:56:23Z&amp;se=2025-10-07T21:11:23Z&amp;spr=https&amp;sv=2024-11-04&amp;sr=b&amp;sig=wo8Ne%2BcjxCw1URCROg8%2BvF0pz%2FcvNgMh2yo9cyMlL1s%3D'\n      ]\n    }\n  \n    protectedSettings: {\n      commandToExecute: 'bash setup-nginx.sh'\n    }\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">The parent: vm syntax is beautiful! It makes parent-child relationship clear.<\/p><h3 class=\"wp-block-heading\">Example 10: Web App with Linux<\/h3><p class=\"wp-block-paragraph\">Here&#8217;s my Web App deployment in Bicep:<\/p><pre class=\"wp-block-code\"><code>resource plan 'Microsoft.Web\/serverfarms@2024-11-01' = { \n  name: 'asp-web-dev-plan-01'\n  location:'centralus'\n  kind: 'linux'\n  sku: { \n      name: 'F1'\n      capacity:1 \n  }\n  properties: { \n     name:'asp-web-dev-plan-01'\n     reserved:true\n  }\n}\n\nresource web 'Microsoft.Web\/sites@2024-11-01' = { \n    name: 'asp-web-dev-01'\n  location:'centralus'\n  kind: 'app,linux'\n  properties: { \n    name: 'asp-web-dev-01'\n    serverFarmId: plan.id\n    siteConfig: {\n      linuxFxVersion: 'PHP|8.3'\n    }\n  }\n}<\/code><\/pre><p class=\"wp-block-paragraph\">Clean and simple! I reference the App Service Plan with just plan.id.<\/p><h3 class=\"wp-block-heading\">How to Install Bicep<\/h3><p class=\"wp-block-paragraph\">Installing Bicep is easy:<\/p><p class=\"wp-block-paragraph\">Windows (PowerShell):<\/p><pre class=\"wp-block-code\"><code>winget install -e --id Microsoft.Bicep<\/code><\/pre><p class=\"wp-block-paragraph\">Or with Azure CLI (already included):<\/p><pre class=\"wp-block-code\"><code>az bicep version<\/code><\/pre><p class=\"wp-block-paragraph\">VS Code Extension:<\/p><p class=\"wp-block-paragraph\">Install &#8220;Bicep&#8221; extension in VS Code for IntelliSense!<\/p><h3 class=\"wp-block-heading\">How to Deploy Bicep Files<\/h3><p class=\"wp-block-paragraph\">Deploy resource group:<\/p><pre class=\"wp-block-code\"><code>az deployment sub create \\\n  --location eastus \\\n  --template-file 01-rg.bicep<\/code><\/pre><p class=\"wp-block-paragraph\">Deploy resources:<\/p><pre class=\"wp-block-code\"><code>az deployment group create \\\n  --resource-group reza-eus \\\n  --template-file 02-storage.bicep<\/code><\/pre><p class=\"wp-block-paragraph\">That&#8217;s it! Same as ARM templates.<\/p><h3 class=\"wp-block-heading\">Bicep Converts to ARM Template<\/h3><p class=\"wp-block-paragraph\">Here&#8217;s the cool part: Bicep converts to ARM template automatically!<\/p><p class=\"wp-block-paragraph\">When I run az deployment group create, Azure CLI:<\/p><ol class=\"wp-block-list\"><li>Converts my Bicep file to ARM JSON<\/li><li>Sends ARM JSON to Azure<\/li><li>Azure deploys resources<\/li><\/ol><p class=\"wp-block-paragraph\">I can see the ARM template:<\/p><pre class=\"wp-block-code\"><code>az bicep build --file 02-storage.bicep<\/code><\/pre><p class=\"wp-block-paragraph\">This creates 02-storage.json file (ARM template).<\/p><p class=\"wp-block-paragraph\">So Bicep is just a better way to write ARM templates!<\/p><h3 class=\"wp-block-heading\">My Learning Journey<\/h3><p class=\"wp-block-paragraph\">When I started:<\/p><ul class=\"wp-block-list\"><li>Week 1: Wrote ARM templates, struggled with syntax<\/li><li>Week 2: Learned about Bicep<\/li><li>Week 3: Converted my first ARM template to Bicep<\/li><li>Week 4: Writing everything in Bicep now!<\/li><\/ul><p class=\"wp-block-paragraph\">The learning curve is much easier with Bicep!<\/p><h3 class=\"wp-block-heading\">What I Like Most About Bicep<\/h3><ol class=\"wp-block-list\"><li>Cleaner syntax &#8211; No extra brackets and quotes<\/li><li>IntelliSense &#8211; VS Code helps me write code<\/li><li>Error checking &#8211; Mistakes show immediately<\/li><li>Existing resources &#8211; Easy to reference existing stuff<\/li><li>Parent-child relationships &#8211; Clear with parent: keyword<\/li><li>Variables are simple &#8211; No square brackets<\/li><li>For loops &#8211; Much easier than ARM copy<\/li><\/ol><h3 class=\"wp-block-heading\">What ARM Templates Do Better<\/h3><p class=\"wp-block-paragraph\">Honestly? Nothing! Bicep is better in every way.<\/p><p class=\"wp-block-paragraph\">The only reason to use ARM templates is if:<\/p><ul class=\"wp-block-list\"><li>Your company already has many ARM templates<\/li><li>You&#8217;re maintaining old code<\/li><li>Your tools don&#8217;t support Bicep yet<\/li><\/ul><p class=\"wp-block-paragraph\">But for new projects, always use Bicep!<\/p><h3 class=\"wp-block-heading\">My Mistakes with Bicep<\/h3><p class=\"wp-block-paragraph\">I forgot to install VS Code extension:<\/p><p class=\"wp-block-paragraph\">First time I wrote Bicep without the extension. No IntelliSense, no autocomplete. It was hard!<\/p><p class=\"wp-block-paragraph\">Fix: Install Bicep extension in VS Code. Life became much easier!<\/p><p class=\"wp-block-paragraph\">I tried to use ARM template syntax:<\/p><p class=\"wp-block-paragraph\">I wrote:<\/p><pre class=\"wp-block-code\"><code>location: [resourceGroup().location]<\/code><\/pre><p class=\"wp-block-paragraph\">Error! No square brackets in Bicep.<\/p><p class=\"wp-block-paragraph\">Fix:<\/p><pre class=\"wp-block-code\"><code>location: resourceGroup().location<\/code><\/pre><p class=\"wp-block-paragraph\">I forgot parent relationship:<\/p><p class=\"wp-block-paragraph\">I created subnet without parent:<\/p><pre class=\"wp-block-code\"><code>resource subnet 'Microsoft.Network\/virtualNetworks\/subnets@2024-10-01' = {\n  name: 'snet-dev-eus-web-01'\n}<\/code><\/pre><pre class=\"wp-block-code\"><code>resource subnet 'Microsoft.Network\/virtualNetworks\/subnets@2024-10-01' = {\n  name: 'snet-dev-eus-web-01'\n  parent: vnet\n}<\/code><\/pre><h3 class=\"wp-block-heading\">What I Learned<\/h3><ol class=\"wp-block-list\"><li>Bicep is easier than ARM templates &#8211; Much cleaner syntax<\/li><li>Same functionality &#8211; Bicep can do everything ARM can do<\/li><li>Better tooling &#8211; IntelliSense and validation<\/li><li>Existing keyword is powerful &#8211; Reference existing resources easily<\/li><li>Parent-child is clear &#8211; Using parent: property<\/li><li>Variables are simple &#8211; No functions needed<\/li><li>For loops are intuitive &#8211; Easy to understand<\/li><li>Converts to ARM &#8211; So it works everywhere<\/li><\/ol>","protected":false},"excerpt":{"rendered":"<p>Hello everyone! After learning ARM templates, I started to learn Bicep. Let me show you why I made the switch from ARM&hellip;<\/p>\n","protected":false},"author":2,"featured_media":111,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_rezaops_part":"6","_rezaops_linkedin_url":"https:\/\/www.linkedin.com\/pulse\/from-arm-bicep-why-i-made-switch-reza-chegini-91jcc\/","footnotes":""},"categories":[50,10],"tags":[53,52,58,8,55],"series":[51],"class_list":["post-109","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure","category-devops","tag-arm-templates","tag-azure","tag-bicep","tag-devops","tag-iac","series-azure-arm-templates"],"jetpack_featured_media_url":"https:\/\/rezachegini.com\/wp-content\/uploads\/2026\/10\/from-arm-to-bicep-cover.png","_links":{"self":[{"href":"https:\/\/rezachegini.com\/index.php?rest_route=\/wp\/v2\/posts\/109","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rezachegini.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rezachegini.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rezachegini.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/rezachegini.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=109"}],"version-history":[{"count":1,"href":"https:\/\/rezachegini.com\/index.php?rest_route=\/wp\/v2\/posts\/109\/revisions"}],"predecessor-version":[{"id":110,"href":"https:\/\/rezachegini.com\/index.php?rest_route=\/wp\/v2\/posts\/109\/revisions\/110"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rezachegini.com\/index.php?rest_route=\/wp\/v2\/media\/111"}],"wp:attachment":[{"href":"https:\/\/rezachegini.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=109"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rezachegini.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=109"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rezachegini.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=109"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/rezachegini.com\/index.php?rest_route=%2Fwp%2Fv2%2Fseries&post=109"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}