Hey there 👋

If you’re starting your journey into infrastructure as code on Azure, you’ll probably run into two things: ARM templates and Bicep. And if you’re anything like me, your reaction to ARM’s JSON templates was probably:

“Wait, what’s with all the brackets? And why does it feel like I’m writing a novel just to spin up a storage account?” 😅

That’s exactly why Bicep was created — to simplify the experience of writing templates that deploy Azure resources. Let’s break it down in normal-people terms.


🧱 What Even Is Bicep?

Bicep is a language made by Microsoft that lets you describe your Azure infrastructure using easy-to-read code. You use it to tell Azure what you want — like storage accounts, web apps, or databases — and Azure builds it all for you, in the right order, through something called Azure Resource Manager (ARM).

Think of it like placing an order at a restaurant:

  • You write the Bicep file = your order.
  • Azure Resource Manager = the kitchen.
  • Your infrastructure = the finished dish, ready to go!

🤝 Bicep vs ARM Templates (Why Bicep Wins for Humans)

ARM templates are the OG way to define Azure resources. But they’re written in JSON, and JSON can get really messy, really fast.

Here’s what writing in JSON feels like:

{
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2023-05-01",
      "name": "[parameters('storageName')]",
      ...
    }
  ]
}

Now compare that to the same thing in Bicep:

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
  name: storageName
  ...
}

Cleaner, right? It reads like real code, not machine instructions.


🔄 Under the Hood: Bicep Becomes JSON

When you deploy a Bicep template, Azure automatically converts it into JSON behind the scenes — this process is called transpilation.

You never really have to worry about it unless you're curious or need to debug. You can even run bicep build to see what the output JSON looks like, just for fun (or pain, depending on your perspective 😅).


🛠 What You Need to Start Using Bicep

To write and deploy Bicep files, all you need is:

  • Azure CLI or Azure PowerShell (both support Bicep out of the box).
  • Visual Studio Code with the Bicep extension.

VS Code gives you autocomplete, syntax checking, and even tells you when you’ve done something weird — like forgetting a required property. Super helpful.


🔨 How to Define Resources in Bicep

Here’s a simple example that creates a storage account:

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
  name: 'toylaunchstorage'
  location: 'westus3'
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
  }
}

Things to note:

  • resource tells Bicep you’re creating something.
  • storageAccount is a symbolic name (used only inside Bicep).
  • The rest defines what kind of resource you want and its settings.

🧩 Connecting Resources Together

Let’s say you want to build a website using Azure App Service, but you need an App Service Plan first (it’s like your hosting plan).

You’d create the plan:

resource appServicePlan 'Microsoft.Web/serverFarms@2023-12-01' = {
  name: 'toy-product-launch-plan'
  location: 'westus3'
  sku: {
    name: 'F1'
  }
}

Then your app, linked to that plan:

resource appServiceApp 'Microsoft.Web/sites@2023-12-01' = {
  name: 'toy-product-launch-1'
  location: 'westus3'
  properties: {
    serverFarmId: appServicePlan.id
    httpsOnly: true
  }
}

Notice that serverFarmId: appServicePlan.id connects the two — Bicep knows to deploy the plan first, because the app depends on it.


🎛 Making Bicep Templates Reusable with Parameters and Variables

Instead of hardcoding names, regions, or SKUs, use parameters:

param location string = resourceGroup().location
param storageSku string = 'Standard_LRS'

And define variables to reuse values or build them dynamically:

var storageAccountName = 'toylaunch${uniqueString(resourceGroup().id)}'

This way, you can deploy the same template in different environments just by changing the parameters.


📦 Organize Your Code with Modules

If your templates get too big (and they will), break them into modules — smaller Bicep files that do one job well, like creating storage, networking, or compute resources.

Use them in your main template like this:

module storage 'modules/storage.bicep' = {
  name: 'storageModule'
  params: {
    location: location
  }
}

You can even pass outputs between modules to connect them together.


🧪 Outputs: Getting Info After Deployment

You can return values from your template like this:

output storageAccountName string = storageAccount.name

This is super helpful in CI/CD pipelines where you need to know what was deployed.


🤔 So, When Should You Use Bicep?

Use Bicep when:

  • You’re deploying mostly to Azure
  • You want clean, easy-to-read IaC
  • You’re building a CI/CD pipeline
  • You like modular, reusable templates

Avoid Bicep when:

  • You’re doing multi-cloud (use Terraform)
  • Your team already uses another IaC tool

🎉 Wrapping Up

Bicep is like the friendly assistant for building stuff in Azure — it's easier to learn, easier to read, and just makes sense.

If you're building cool things with Bicep, I'd love to connect with fellow learners and builders. Drop me a hello on LinkedIn — always happy to chat about cloud, IaC, or what you're learning too.