← Back to Guides

Getting Started with Bicep

BeginnerBicep & Terraform2026-03-14

What Is Bicep?

Bicep is a domain-specific language (DSL) for deploying Azure resources declaratively. It transpiles to ARM JSON templates but offers a much cleaner, more readable syntax.

Why Bicep over ARM Templates?

{}

ARM JSON

Traditional Azure Resource Manager templates
SyntaxVerbose JSON
ModulesLinked/nested templates
Type safetyLimited
ReadabilityPoor for complex templates
ToolingVS Code extension
💪

Bicep

Clean, concise domain-specific language
SyntaxClean, concise DSL
ModulesFirst-class module support
Type safetyFull IntelliSense and validation
ReadabilityExcellent
ToolingVS Code extension with rich features

Your First Bicep File

Create a file called main.bicep:

@description('The Azure region for resources')
param location string = resourceGroup().location

@description('Environment name')
@allowed([
  'dev'
  'test'
  'prod'
])
param environment string = 'dev'

var storageName = 'st${uniqueString(resourceGroup().id)}${environment}'

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: storageName
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
  properties: {
    minimumTlsVersion: 'TLS1_2'
    supportsHttpsTrafficOnly: true
  }
}

output storageAccountId string = storageAccount.id
output storageAccountName string = storageAccount.name

Deploying

# Create a resource group
az group create --name rg-demo --location uksouth

# Deploy the Bicep file
az deployment group create \
  --resource-group rg-demo \
  --template-file main.bicep \
  --parameters environment=dev

Parameters

Parameters accept input values at deployment time:

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

@minValue(1)
@maxValue(10)
param instanceCount int = 1

@secure()
param adminPassword string

Use a parameter file for environment-specific values:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "appName": { "value": "myapp" },
    "instanceCount": { "value": 3 }
  }
}

Modules

Break large deployments into reusable modules:

module vnet './modules/vnet.bicep' = {
  name: 'vnet-deployment'
  params: {
    location: location
    vnetName: 'vnet-${environment}'
    addressPrefix: '10.0.0.0/16'
  }
}

// Reference module outputs
output vnetId string = vnet.outputs.vnetId

Best Practices

  1. Use parameter decorators (@description, @allowed, @minValue) for documentation and validation
  2. Organise with modules — one module per logical resource group
  3. Use existing keyword to reference resources deployed elsewhere
  4. Store modules in a Bicep Registry for cross-team reuse
  5. Always set minimumTlsVersion and other security properties