Bicep for Azure IaC

Learn to use Bicep for infrastructure as code in Azure. This tutorial covers the core concepts, hands-on steps, and how to apply Bicep to manage your Azure resources efficiently.

Focus: use bicep for infrastructure as code

Sponsored

If you have ever clicked through the Azure portal to create resources one by one, you know the pain: environments drift, manual steps get forgotten, and replicating a setup in a new subscription feels like a chore. This lesson teaches you how to use Bicep for infrastructure as code (IaC) — a declarative language that codifies your Azure resources into versionable files, so you can deploy consistent environments in minutes, not hours. No more clicking — just code.

The problem this lesson solves

Imagine you need to deploy a web app, a database, and a storage account. In the portal, you’d spend 30 minutes clicking, tweaking settings, and hoping you didn’t miss a toggle. Now imagine you need to deploy the same setup to dev, staging, and production — three times the clicking, three times the chance for error. Worse, if someone manually changes a resource in the portal, your environment silently drifts from your intended configuration.

Infrastructure as code solves this by treating your infrastructure like application code — stored in Git, reviewed in pull requests, and deployed through pipelines. Bicep is Azure’s domain-specific language for IaC, designed to be simpler and more readable than ARM templates while giving you the same power. With Bicep, you write a .bicep file that describes what you want, and Azure handles how to create it. The result: reproducible, auditable, and idempotent deployments.

This lesson is step 57 in the Azure Tutorial, so if you’ve already set up subscriptions and managed identity, you’re ready to bring it all together with Bicep.

Core concept / mental model

Think of Bicep as a blueprint for your Azure resources. Just as an architect’s drawing defines the structure of a building without specifying every nail, a Bicep file defines the resources you want — virtual networks, storage accounts, app services — without dictating the underlying API calls. When you deploy, Azure reads the blueprint and creates or updates resources to match.

Bicep is a declarative language: you state the desired end state, not the sequence of commands. This contrasts with imperative scripting (e.g., PowerShell or CLI commands) where you must orchestrate every step. Declarative means Bicep is idempotent — run the same deployment twice, and Azure ensures the environment matches your file, nothing more, nothing less.

Here’s a quick mental picture:

  • File: main.bicep — your declarative blueprint.
  • Deployment: az deployment group create — sends the blueprint to Azure.
  • Azure: Compares current resources to the blueprint, makes changes to align them.
  • Result: A predictable, repeatable environment.

Bicep is also transparent. Under the hood, it compiles to Azure Resource Manager (ARM) JSON templates, so you can always see what will be deployed. You get the best of both worlds: the simplicity of Bicep and the maturity of ARM.

How it works step by step

Follow this logical sequence to go from zero to a deployed resource group with Bicep.

  1. Install the tools — You need the Azure CLI (or PowerShell) and the Bicep CLI. The Azure CLI installs Bicep automatically when you run az bicep install.

  2. Authenticate to Azure — Use az login to sign in and az account set to choose your subscription.

  3. Write your Bicep file — Create a main.bicep file that declares parameters, variables, and resources. Parameters let you customize the deployment (e.g., environment name) without editing the file.

  4. Validate — Run az deployment group validate to catch syntax errors early. This is like compiling code before runtime.

  5. Preview changes — Use az deployment group what-if to see exactly what Azure will add, modify, or delete without making changes. This is your safety net.

  6. Deploy — Run az deployment group create to apply the blueprint. Azure performs the changes idempotently.

  7. Manage lifecycle — Store your .bicep files in Git, build a CI/CD pipeline, and redeploy as your infrastructure evolves.

The key advantage: every step is scriptable and repeatable. You can automate validations and deployments in Azure Pipelines or GitHub Actions, making your infrastructure reviewable like application code.

Hands-on walkthrough

Let’s put Bicep into action. This exercise assumes you have the Azure CLI installed and are logged in.

1. Set up your environment

First, install Bicep and select your subscription:

az bicep install
az login
az account set --subscription "your-subscription-id"

2. Create a Bicep file

Create a file named main.bicep with the following content. It creates a resource group and a storage account — a common starting point for many apps.

// Declare parameters for customization
param environment string = 'dev'
param storageName string = 'st${uniqueString(resourceGroup().id)}'

// Import the resource group (if you want to deploy into an existing one)
targetScope = 'resourceGroup'

// Storage account resource
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: storageName
  location: resourceGroup().location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

// Output the storage account name for later use
output storageAccountName string = storageAccount.name

This file declares a storage account with a unique name based on the resource group ID, and outputs the name for use in other templates.

3. Validate and preview

Before deploying, validate the file and run a what-if to see what Azure will do:

az deployment group validate --resource-group my-rg --template-file main.bicep
az deployment group what-if --resource-group my-rg --template-file main.bicep

You’ll see a list of changes — for a new resource group, it will show a new storage account. This step catches errors early and prevents surprises.

4. Deploy the Bicep file

Now deploy to create the resource group and storage account:

az group create --name my-rg --location eastus
az deployment group create --resource-group my-rg --template-file main.bicep

Expected output (abbreviated):

{
  "properties": {
    "outputs": {
      "storageAccountName": {
        "type": "String",
        "value": "stxxxxxx"
      }
    },
    "provisioningState": "Succeeded"
  }
}

Your storage account is now live! Run the same deployment again — Bicep will make no changes because the resource already matches the template. That’s idempotence in action.

5. Add a second resource

Let’s extend the template to include a blob container, showing how resources depend on each other. Bicep automatically manages dependencies for you.

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: 'logs'
}

After deploying again, you’ll have a logs container ready for use.

Compare options / when to choose what

Bicep is not the only way to do IaC on Azure. Here’s a quick comparison of popular options:

Tool Language Ease of use Azure-native Best for
Bicep Declarative DSL High Yes Azure-only teams wanting simplicity
ARM templates JSON Medium Yes Legacy templates, maximum portability
Terraform HCL Medium No (multi-cloud) Multi-cloud environments
Pulumi General-purpose (Python, TS) Medium No Teams wanting code-like logic
  • Choose Bicep if you’re all-in on Azure and want the shortest learning curve while staying native. It’s the recommended path by Microsoft.
  • Choose Terraform if you must manage resources across AWS and GCP as well. Its provider model is mature but adds a learning curve.
  • Choose ARM templates only if you have existing templates or need to avoid any compilation step. Bicep compiles to ARM, so you get the best of both.
  • Choose Pulumi if you want to use your favorite programming language to define infrastructure, but be ready to handle more complexity.

For this course, Bicep is the sweet spot: it’s Azure-native, easy to read, and fully supported.

Troubleshooting & edge cases

Even with a great tool, things go wrong. Here are common pitfalls and how to fix them.

Error: “The resource type ... could not be found”

This usually means the API version in your Bicep file is incorrect. Check the Azure resource provider documentation for the latest version. For example, Microsoft.Storage/storageAccounts@2023-01-01 is valid; older versions may be deprecated.

Error: “Invalid resource name”

Storage account names must be 3–24 characters, lowercase, and alphanumeric. Your uniqueString() call can generate underscores or uppercase? Actually, uniqueString() returns lowercase letters and digits, but keep an eye on length. Always test with az deployment group validate.

What-if says “No changes” but you expected a change

This often happens when you edit the template but deploy to a different resource group or subscription. Verify you’re using the correct --resource-group and --subscription. Also, Bicep ignores changes to parameters that aren’t used — check for typos in your parameter names.

Bicep CLI not found

If az bicep install fails, ensure your Azure CLI is up to date (az upgrade). In restricted environments, you can download Bicep binaries directly from the Bicep releases.

“Unauthorized” errors during deployment

Your service principal or user must have permissions at the subscription or resource group scope. If you’re using managed identity (as covered in earlier lessons), grant Contributor or a custom role via role-based access control.

Deployment takes too long

Large templates with many resources can be slow. Consider breaking your infrastructure into modules and deploying them independently or in parallel using a pipeline.

What you learned & what's next

You now understand the core idea behind using Bicep for infrastructure as code: you define your desired Azure state in a declarative file, and Azure makes it happen. You practiced creating a Bicep template, validating it with what-if, and deploying it idempotently. You also learned how Bicep compares to other IaC tools and how to troubleshoot common issues.

Keep these takeaways with you: Bicep is Azure’s native IaC, it’s idempotent, it compiles to ARM for maximum compatibility, and the what-if command is your best friend for safe changes.

In the next lesson, you’ll build on this foundation by integrating Bicep into a CI/CD pipeline, automating deployments with GitHub Actions or Azure Pipelines. You’ll also explore modules and loops to manage complex architectures. For now, practice converting a resource you’ve manually created in the portal into a Bicep file — you’ll solidify your understanding faster than reading a dozen tutorials.

Practice recap

Write a Bicep template that creates a resource group, a storage account, and a blob container. Use parameters for environment name and location. Deploy it to a new resource group, then run what-if again to confirm no changes on a second deployment. Modify the template to add a second container, deploy, and observe the incremental changes.

Common mistakes

  • Forgetting to run az bicep install — the Azure CLI may not have Bicep commands enabled by default. Run it once before your first deployment.
  • Hardcoding resource names instead of using uniqueString() or parameters, leading to conflicts across environments.
  • Skipping the what-if command, which is your best safety net to preview changes before they happen.
  • Using an outdated API version in your Bicep file, causing 'resource type not found' errors.
  • Not parameterizing values like locations or SKUs, making templates inflexible and hard to reuse.

Variations

  1. Use Bicep modules to break large templates into reusable components, similar to functions in code.
  2. Deploy Bicep templates via CI/CD pipelines (Azure Pipelines or GitHub Actions) for automated, versioned infrastructure.
  3. Use az deployment sub create for subscription-level deployments (e.g., creating resource groups) as opposed to resource group-level.

Real-world use cases

  • Automating the creation of isolated environments for each pull request in a development workflow.
  • Managing multi-region infrastructure for a globally distributed application, ensuring consistent configurations across regions.
  • Provisioning a complete data stack (storage, database, analytics) for a data science team with a single, version-controlled template.

Key takeaways

  • Bicep is Azure's native IaC language that compiles to ARM templates, giving you simplicity and full Azure compatibility.
  • Bicep deployments are idempotent — running the same template multiple times yields the same result, preventing drift.
  • Always validate with az deployment group validate and preview with what-if before applying changes.
  • Use parameters and uniqueString() to create flexible, reusable templates that avoid naming conflicts.
  • Compare Bicep with Terraform and ARM to choose the right tool based on your cloud strategy and team skills.
  • Integrate Bicep into CI/CD pipelines to automate infrastructure deployment and enforce review processes.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.