Skip to main content

Bicep

Bicep is a domain-specific language (DSL) for deploying Azure resources declaratively.
It simplifies the process of defining and deploying infrastructure by providing a more concise syntax compared to traditional JSON-based ARM templates.
Bicep files use the .bicep file extension and can be compiled into ARM templates for deployment.

Key Features of Bicep

  • Declarative Syntax: Bicep allows you to define what resources you want to deploy without specifying how to deploy them.
  • Modularity: Bicep supports modules, enabling you to reuse and share code across different deployments.
  • Type Safety: Bicep provides type checking, which helps catch errors during development
  • Integration with Azure: Bicep is tightly integrated with Azure services, making it easier to manage and deploy resources.
  • Tooling Support: Bicep has excellent tooling support, including Visual Studio Code extensions that provide syntax highlighting, autocompletion, and linting.
  • Parameterization: Bicep allows you to define parameters, making it easy to customize deployments for different environments or scenarios.
  • Resource Management: Bicep simplifies resource management by allowing you to define dependencies and relationships between resources.
  • Open Source: Bicep is an open-source project, allowing the community to contribute and improve the language.
  • Easier Learning Curve: Bicep's syntax is more user-friendly compared to ARM templates, making it easier for newcomers to learn and use.
  • Better Readability: Bicep files are generally more readable and maintainable than equivalent ARM templates, making it easier to understand the infrastructure being defined.
  • Automatic Resource Provider Registration: Bicep automatically registers resource providers as needed during deployment, reducing manual steps.
  • Built-in Functions: Bicep includes a variety of built-in functions to manipulate strings, arrays, and objects, making it easier to work with data within your templates.
  • Support for Loops and Conditions: Bicep supports loops and conditional logic, allowing for more dynamic and flexible resource definitions.
  • Better Error Messages: Bicep provides more informative error messages during compilation and deployment, helping developers quickly identify and resolve issues.
  • Versioning and Updates: Bicep is actively maintained and updated by Microsoft, ensuring compatibility with the latest Azure features and services.
  • Seamless Integration with CI/CD: Bicep can be easily integrated into continuous integration and continuous deployment (CI/CD) pipelines, enabling automated infrastructure deployments.
  • Cross-Platform Support: Bicep can be used on various operating systems, including Windows, macOS, and Linux, making it accessible to a wide range of developers.
  • Extensibility: Bicep can be extended with custom modules and libraries, allowing organizations to create reusable components tailored to their specific needs.
  • Community and Ecosystem: Bicep has a growing community and ecosystem, with numerous resources, tutorials, and examples available to help users get started and advance their skills.
  • Backward Compatibility: Bicep is designed to be backward compatible with existing ARM templates, allowing users to gradually transition from ARM to Bicep without disrupting existing deployments.
  • Cost Management: Bicep can help optimize resource usage and costs by allowing users to define and manage resources more efficiently.
  • State Management: Bicep does not require separate state management like some other IaC tools (e.g., Terraform), as it leverages Azure's native resource management capabilities.
  • Rich Ecosystem of Tools: Bicep has a rich ecosystem of tools and extensions that enhance the development experience, including linters, formatters, and deployment tools.
  • Support for Nested Deployments: Bicep supports nested deployments, allowing users to break down complex infrastructure into smaller, manageable components.
  • Improved Collaboration: Bicep's modularity and readability facilitate collaboration among team members, making it easier to work together on infrastructure projects.
  • Better Integration with Azure Policies: Bicep allows for easier integration with Azure Policies, enabling organizations to enforce governance and compliance across their deployments.
  • Faster Iteration: Bicep's concise syntax and tooling support enable faster iteration and development cycles, allowing teams to quickly test and deploy changes to their infrastructure.
  • Rich Documentation: Bicep has comprehensive documentation provided by Microsoft, making it easier for users to learn and reference the language features and best practices.

Bicep templates vs Bicep modules (what they are)

In Bicep you will usually work with templates and modules.

Bicep template (the main deployment file)

A template is a .bicep file that represents a complete deployment at a certain scope (resource group / subscription / etc.).

You can think of it as the main plan:

  • Defines the parameters you need (name, location, SKU, etc.)
  • Defines the resources Azure must create/update
  • Often calls modules for reusable building blocks

Common names: main.bicep, deploy.bicep, rg.bicep.

Bicep module (a reusable building block)

A module is also a .bicep file, but it is designed to be called from another Bicep file.

You can think of it as a LEGO sub-build:

  • One module does one job (e.g., “create a VNet”, “create a storage account”, “create diagnostics settings”)
  • You can reuse the same module in many projects/environments
  • You pass input values to the module using parameters
  • A module can return useful values using outputs (for example the resource ID)

Why use modules:

  • Reuse: don’t copy/paste the same resources everywhere
  • Consistency: security/naming/tags are applied the same way
  • Easier maintenance: fix a bug once in the module, then redeploy
  • Cleaner main files: the main template stays readable

How modules work (simple example)

Example folder structure

infra/
main.bicep
modules/
vnet.bicep
storage.bicep

Module file (example): modules/vnet.bicep

// Creates a VNet and outputs its resource ID
param location string
param vnetName string
param addressPrefix string

resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: [
addressPrefix
]
}
}
}

output vnetId string = vnet.id

Main template file (example): main.bicep

param location string = resourceGroup().location
param environment string
param baseName string

var vnetName = '${baseName}-${environment}-vnet'

module vnetModule './modules/vnet.bicep' = {
name: 'deployVnet'
params: {
location: location
vnetName: vnetName
addressPrefix: '10.10.0.0/16'
}
}

// You can use module outputs in the main template:
output deployedVnetId string = vnetModule.outputs.vnetId

Quick summary

  • Template: the main .bicep file you deploy.
  • Module: a reusable .bicep building block called by the template.
  • Parameters: inputs (values you provide).
  • Outputs: results (values you can use later).

What a Bicep deploy file does

A Bicep file (.bicep) is an Infrastructure as Code (IaC) template for Azure.
Think of it as a recipe that describes what Azure should create or change.

What it contains

A typical Bicep file defines:

  • Scope: Where you deploy (resource group, subscription, management group, tenant)
    Example: creating a resource group requires targetScope = 'subscription'.
  • Parameters (param): Values you can change without editing the template (like location, names, SKU).
  • Resources (resource): The actual Azure things to deploy (resource groups, VNets, storage accounts, etc.).
  • (Optional) Outputs: Values you want to print after deployment (like resource IDs).

What happens when you deploy it

When you run a command like:

az deployment sub create --template-file .\resourcegroup-networking.bicep --location westeurope

Azure will:

  1. Read your Bicep file
  2. Compile it to an ARM JSON template behind the scenes
  3. Compare the desired state (your template) with what exists
  4. Create / update resources to match your template (usually incremental changes)

So the Bicep file is the source of truth for how your Azure environment should look.

Why use parameters?

If your .bicep file hardcodes values like:

  • resource group name = rg-networking-corp-pro-001
  • location = westeurope
targetScope = 'subscription'

param location string = 'westeurope'
param name string = 'rg-networking-corp-pro-001'

resource resResourceGroup 'Microsoft.Resources/resourceGroups@2025-04-01' = {
location: location
name: name
}

Then deploying the same template for another environment (like test, acceptance, production) becomes annoying because you must edit the template each time.

That creates risks:

  • Someone forgets to change a value and deploys to the wrong place
  • You end up with multiple slightly different templates (template drift)
  • It’s harder to review and standardize

Parameters solve part of this, because you can pass different values in each deployment.

What a .bicepparam file is (and why you use it)

A Bicep parameter file (.bicepparam) is a separate file that stores parameter values for a specific deployment.

Why it’s useful

It lets you keep:

  • One shared template (main.bicep) = the "recipe"
  • Multiple parameter files (dev.bicepparam, test.bicepparam, prod.bicepparam) = the "ingredient lists"

This is the cleanest way to do multiple deployments because:

  • You don’t modify the template per environment
  • You can deploy the same template repeatedly with different settings
  • It’s easier for juniors/operators: "pick the right param file and run the command"
  • It’s better for change control and audits (you can review param changes separately)

Simple example(s)

Template

This stays the same for every environment:

targetScope = 'subscription'

param location string
param name string

resource resResourceGroup 'Microsoft.Resources/resourceGroups@2025-04-01' = {
name: name
location: location
}

Parameter file for Development

using './resourcegroup-networking.bicep'

param location = 'westeurope'
param name = 'rg-networking-dev-001'

Parameter file for Production

using './resourcegroup-networking.bicep'

param location = 'westeurope'
param name = 'rg-networking-prd-001'

Deploy Development vs Production

You deploy the same template, just swap the parameter file:

az deployment sub create --location westeurope --parameters .\dev.bicepparam
az deployment sub create --location westeurope --parameters .\prod.bicepparam

Overview

  • .bicep = blueprint / recipe (what should be built)
  • .bicepparam = configuration sheet (names, regions, sizes for this environment)
  • Deployment command = "build it now" using the blueprint + configuration