Azure

Azure Bicep Tutorial: Syntax, Modules, What-If, and CI/CD Integration

2026-05-24
NicheeLab Editorial Team

Bicep is a declarative IaC DSL developed by Microsoft as the successor to Azure Resource Manager (ARM) templates. It went GA in 2021 and has rapidly become Microsoft Azure's official recommended IaC tool. This article walks through Bicep's core syntax, modularization, What-If deployments, CI/CD integration, and migration from ARM.

Why Bicep?

Key advantages of Bicep:

  • Dramatically more concise and readable syntax than ARM JSON
  • IntelliSense and type checking (via VS Code extension)
  • Modularization support (ideal for large IaC)
  • Immediate support for the latest Azure features (including preview features)
  • No state management required (ARM handles it automatically)
  • Free
  • Automatic transpilation to ARM JSON (Bicep -> ARM JSON)

As of 2026, Bicep is the de facto standard for new Azure projects; ARM JSON is reserved for maintaining existing templates rather than new development.

Basic Syntax

Bicep uses declarative syntax. The key elements are:

  • param: parameter declaration with type, default value, and allowed values
  • var: variables and computed expressions
  • resource: Azure resource definition with apiVersion
  • module: invoke and reuse child templates
  • output: export values to other templates
  • targetScope: subscription / managementGroup / tenant scope

Simple Storage Account example

param storageAccountName string
param location string = resourceGroup().location

resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
    allowBlobPublicAccess: false
    minimumTlsVersion: 'TLS1_2'
  }
}

output storageId string = storage.id
output storageEndpoint string = storage.properties.primaryEndpoints.blob

Type System

Bicep has a type system and types are specified explicitly.

  • string: string
  • int: integer
  • bool: boolean
  • array: array
  • object: object
  • Custom type (added in 2024): type myType = { name: string, age: int }

Modularization

Always modularize large IaC.

Recommended Directory Structure

infra/
├── main.bicep              # Entry point
├── main.parameters.json    # Environment-specific parameters
├── modules/
│   ├── network.bicep       # VNet / NSG / Subnet
│   ├── storage.bicep       # Storage Account / Container
│   ├── compute.bicep       # VM / App Service / AKS
│   └── identity.bicep      # Managed Identity / Key Vault
└── README.md

Module Invocation Example

module network 'modules/network.bicep' = {
  name: 'networkDeploy'
  params: {
    vnetName: vnetName
    addressPrefix: '10.0.0.0/16'
    location: location
  }
}

module compute 'modules/compute.bicep' = {
  name: 'computeDeploy'
  params: {
    vmName: vmName
    subnetId: network.outputs.appSubnetId
  }
  dependsOn: [
    network
  ]
}

Conditional Deployment and Loops

Conditional Deployment

param environment string

resource backup 'Microsoft.RecoveryServices/vaults@2023-01-01' = if (environment == 'prod') {
  name: 'rsv-prod'
  location: location
  sku: { name: 'Standard' }
}

Loops (for iteration)

param storageAccountNames array = [
  'stgapp001'
  'stgapp002'
  'stgapp003'
]

resource storageAccounts 'Microsoft.Storage/storageAccounts@2023-01-01' = [for name in storageAccountNames: {
  name: name
  location: location
  sku: { name: 'Standard_LRS' }
  kind: 'StorageV2'
}]

What-If Deployment

What-If deployment previews what a Bicep / ARM template would change before actually deploying it. It is a mandatory step before production deployments.

Command

az deployment group what-if \
  --resource-group myRG \
  --template-file main.bicep \
  --parameters main.parameters.json

Output Categories

  • + Create: newly created
  • - Delete: deleted
  • ~ Modify: modified (diff shown)
  • = NoChange: unchanged
  • * Deploy: deploy only (idempotent)

The modern best practice is to wire What-If into the CI/CD pipeline and run it automatically during the Pull Request review phase.

Bicep vs Terraform

AspectBicepTerraform
VendorMicrosoftHashiCorp
Cloud supportAzure onlyMulti-cloud (Azure / AWS / GCP)
LanguageBicep DSLHCL (HashiCorp Configuration Language)
State managementNot required (ARM handles it)Required (Storage Account / Terraform Cloud)
Latest Azure featuresImmediate supportSometimes delayed
PricingFreeOSS free / Terraform Cloud is paid
When to chooseAzure-only, newest featuresMulti-cloud or existing Terraform codebase

CI/CD Integration

GitHub Actions pattern

name: Deploy Bicep
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: What-If
        uses: azure/CLI@v2
        with:
          inlineScript: |
            az deployment group what-if \
              --resource-group myRG \
              --template-file infra/main.bicep
      - name: Deploy
        uses: azure/arm-deploy@v2
        with:
          resourceGroupName: myRG
          template: infra/main.bicep
          parameters: infra/main.parameters.json

Standard pipeline stages: Lint -> Build -> What-If -> Manual Approval (Production) -> Deploy. See the Azure Pipelines YAML complete guide for details.

Migrating from ARM Templates

Automatic conversion tool for existing ARM JSON -> Bicep:

az bicep decompile --file template.json

This automatically converts ARM JSON into a Bicep file. The conversion is not perfect; cases that require manual adjustment include:

  • Translating complex expressions and functions to Bicep syntax
  • Optimizing Conditional / Loop syntax
  • Splitting non-modularized templates into modules
  • Publishing to the Public Registry or an internal Registry

Realistic migration strategy: new = Bicep, existing = convert only when needed -- a hybrid approach is recommended. Microsoft explicitly supports continued use of existing ARM templates, so a forced big-bang migration is not required.

Best Practices

  1. Always modularize (per feature in a modules/ directory)
  2. Environment-specific parameters (dev.parameters.json, prod.parameters.json)
  3. Always run What-If before production deploys
  4. Run static analysis with az bicep lint
  5. Use Workload Identity Federation for CI/CD auth (no secrets required)
  6. Follow resource naming conventions (CAF: Cloud Adoption Framework recommended patterns)
  7. Apply tags (Environment, Owner, CostCenter, Project)
  8. Reference Key Vault for secret parameters (@secure() decorator)
  9. Leverage AVM (Azure Verified Modules) (Microsoft's official verified modules)
  10. Visualize dependencies with Bicep Visualizer (VS Code extension)

Related Certifications

Frequently Asked Questions

What is Bicep?

Bicep is a declarative IaC (Infrastructure as Code) DSL (Domain Specific Language) developed by Microsoft as the successor to Azure Resource Manager (ARM) templates. It went GA in 2021 and transpiles to ARM JSON automatically (Bicep -> ARM JSON). Compared to ARM JSON, the syntax is dramatically more concise and readable, with IntelliSense, type checking, and modular support. As Microsoft Azure's official recommended IaC tool, it gets faster support for Azure-specific capabilities (preview features and newest APIs) than Terraform. In 2026, Bicep is the de facto standard for new Azure projects; ARM JSON is reserved for maintaining existing templates rather than new development.

What are the basic Bicep syntax elements?

Bicep uses declarative syntax with these key elements: 1) param (parameter declaration with type, default value, allowed values), 2) var (variables and computed expressions), 3) resource (Azure resource definitions with apiVersion), 4) module (invoke and reuse child templates), 5) output (export values to other templates), 6) targetScope (subscription/managementGroup/tenant scope). Example: `param storageAccountName string` and `resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = { name: storageAccountName, location: location, sku: { name: 'Standard_LRS' }, kind: 'StorageV2' }`. Types are explicit (string, int, bool, array, object, plus custom types added in 2024) and far more intuitive than ARM JSON.

What are the best practices for modularizing Bicep?

Always modularize large IaC. The canonical directory layout: 1) main.bicep (entry point, with environment-specific parameters in parameters.bicepparam), 2) a modules/ directory with feature-scoped modules (network.bicep, storage.bicep, compute.bicep, identity.bicep), 3) common modules published to the Azure Bicep Public Registry or an internal Bicep Registry. Example: `module network 'modules/network.bicep' = { name: 'networkDeploy', params: { vnetName: vnetName, addressPrefix: '10.0.0.0/16' } }`. Bicep also supports Conditional Deployment (if) and Loops (for). AVM (Azure Verified Modules) is Microsoft's official set of verified modules and serves as a reference implementation for Bicep best practices.

How is Bicep different from Terraform?

Bicep: built by Microsoft, Azure-only DSL, immediate support for the newest Azure features, no state management needed (ARM handles it), free, and rich IntelliSense via the VS Code extension. Terraform: built by HashiCorp, multi-cloud (Azure via the AzureRM Provider), a huge community, written in HCL (HashiCorp Configuration Language), requires state management (Azure Storage or Terraform Cloud), free as OSS while Terraform Cloud is paid. Decision rule: Bicep for Azure-only projects that need the newest features; Terraform when you need multi-cloud or already have a Terraform codebase. As of 2026, Microsoft recommends Bicep, but the partnership with HashiCorp continues and the Terraform AzureRM Provider is still actively updated.

What is a What-If deployment?

A What-If deployment previews what a Bicep / ARM template would change before actually deploying it. Command: `az deployment group what-if --resource-group myRG --template-file main.bicep`. Output categorizes changes as '+ Create', '- Delete', '~ Modify', '= NoChange', and '* Deploy'. This is a mandatory step before production deployments because it surfaces unexpected resource deletions or configuration overwrites in advance. The modern best practice is to wire What-If into the CI/CD pipeline and run it automatically during the Pull Request review phase.

How do you integrate Bicep with CI/CD?

Integration patterns for GitHub Actions / Azure DevOps Pipelines. GitHub Actions: authenticate via Workload Identity Federation with `azure/login@v2`, then deploy Bicep with `azure/arm-deploy@v2` or `azure/CLI@v2`. Azure Pipelines: run `az deployment group create` via the AzureCLI@2 task. The standard pipeline stages: 1) Lint (`az bicep lint`), 2) Build (`az bicep build`), 3) What-If (`az deployment group what-if`), 4) Manual Approval (Production), 5) Deploy (`az deployment group create`). Use Environments to require an Approval Gate for production, and version Bicep modules via the Bicep Public Registry or GitHub Container Registry. See the Azure Pipelines YAML complete guide for details.

How do you migrate from ARM templates to Bicep?

The automatic conversion tool for existing ARM JSON -> Bicep is `az bicep decompile --file template.json`, which converts ARM JSON into a Bicep file automatically. The conversion is not perfect; expect to manually adjust: 1) complex expressions and functions translated to Bicep syntax, 2) Conditional / Loop syntax optimization, 3) splitting non-modularized templates into modules, 4) publishing to the Public Registry or an internal Registry. Migration strategies: 1) convert all existing ARM templates to Bicep in one pass (large effort) vs 2) keep existing ARM as-is and only write new code in Bicep (incremental). A hybrid approach -- new = Bicep, existing = convert only when needed -- is the realistic choice. Microsoft explicitly supports continued use of existing ARM templates, so a forced big-bang migration is not required.

Which certifications cover Bicep?

AZ-104 (Administrator) covers Bicep / ARM fundamentals in Domain 1; AZ-204 (Developer Associate, note: retires 2026-07) covers IaC usage from a developer's perspective; AZ-400 (DevOps Engineer Expert) Domain 3 covers Bicep integration in CI/CD pipelines (the highest-weighted domain); AZ-305 (Solutions Architect Expert) covers IaC strategy from an architect's perspective; AZ-120 (SAP on Azure) covers automated Bicep deployment for ACSS. Understanding Bicep is required for any engineer working with Azure, and DevOps engineers and SREs need deep knowledge. See the Azure DevOps engineer career roadmap for details.

Related Articles and Technical Deep Dives

ARM テンプレート → Bicep 移行ガイド|decompile・手動調整・段階的移行・What-If 検証【2026 年版】

既存 ARM テンプレートから Bicep への移行完全ガイド。az bicep decompile による自動変換、手動調整が必要なケース、段階的移行ロードマップ (4 Phase)、Module 化、What-If デプロイ検証、Bicep Linter 活用、関連認定試験 (AZ-400 / AZ-104 / AZ-305) を日本語で網羅。

Terraform on Azure 完全ガイド|AzureRM/AzAPI Provider・State 管理・Module 設計・CI/CD 統合【2026 年版】

Terraform で Azure リソースを管理する完全ガイド。AzureRM Provider と AzAPI Provider の使い分け、Terraform State の Azure Storage Backend 管理、Module 設計と Azure Verified Modules (AVM)、Bicep との併用、Drift Detection、GitHub Actions / Azure Pipelines 統合、関連認定試験 (AZ-400 / HashiCorp Terraform Associate) を日本語で網羅。

Azure DevOps エンジニア キャリアロードマップ|AZ-104 → AZ-400 → SC-100 シニア DevOps への道【2026 年版】

Azure DevOps Engineer になるための認定取得ロードマップ完全版。AZ-900 → AZ-104 → AZ-400 の王道ルート、GitHub と Azure DevOps の両方を扱う AZ-400 の構成、Kubernetes 認定 (CKA / CKAD / CKS) との二刀流、IaC (Bicep / Terraform) 戦略、年収レンジまで日本語で網羅。

GitHub Actions vs Azure Pipelines 完全比較|CI/CD 選定・Self-hosted Runner・OIDC 認証【2026 年版】

GitHub Actions と Azure Pipelines を完全比較。機能差・Marketplace エコシステム・Self-hosted Runner / Agent・OIDC (Workload Identity Federation) 認証・Matrix Build・Composite Action / Reusable Workflow・DevSecOps 統合・関連認定試験 (AZ-400 / AZ-204) を日本語で網羅。

Technical information in this article is based on the Azure Bicep Documentation. This article is not an official Microsoft Corporation product and has no affiliation or endorsement relationship. Microsoft and Azure are trademarks of the Microsoft group of companies. Terraform is a trademark of HashiCorp. Information is based on official public materials as of May 24, 2026. Always verify the latest information on official pages.

Check what you learned with practice questions

Practice with certification-focused question sets

View Azure exam prep
Author

NicheeLab Editorial Team

NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.


Related articles
Azure

AZ-900 Azure Fundamentals: Complete Exam Guide (2026)

Pass AZ-900 — cloud concepts, Azure architecture, management...

Azure

Azure Certification Roadmap: Which Cert to Take Next (2026)

Choose your Azure certification path — Fundamentals, Associa...

Azure

AI-901 Azure AI Fundamentals (Beta): Complete Guide (2026)

Pass AI-901 — Microsoft Foundry, generative AI, responsible ...

Azure

Microsoft Entra ID Fundamentals for Azure Certs (2026)

Entra ID basics every cert candidate needs — tenants, identi...

Azure

DP-900 Azure Data Fundamentals: Complete Guide (2026)

Pass DP-900 — relational, non-relational, analytics, Power B...

Browse all Azure articles (104)
© 2026 NicheeLab All rights reserved.