Home / Azure

AzureHow-toBeginner

Azure Bicep first deployment: write a template, preview with what-if, deploy safely

Your first Bicep deployment from start to finish - install the tooling, write a small storage template, add a parameter file, preview changes with what-if, deploy it and find the error when it fails.

Deepak Bhardwaj4 min read

Applies toAzure Bicep, Azure CLI 2.x, Azure Resource Manager

The goal

You've been creating resources by hand in the portal and want a repeatable way to do it. Bicep is Azure's template language: you describe what should exist, and Azure Resource Manager works out how to create it. By the end of this post you'll have a storage account deployed from a file you can keep in Git.

Step 1: install and check the tools

az --version
az bicep install
az bicep version
az login
az account show --query "{name:name, id:id}" -o table

Check the subscription name carefully. Deploying to the wrong one is the easiest mistake to make. Switch it with az account set --subscription "<name-or-id>".

Create a resource group to deploy into:

az group create --name rg-bicep-demo --location centralindia

Step 2: write the template

Save this as main.bicep:

@description('Globally unique name: 3-24 lowercase letters and numbers')
@minLength(3)
@maxLength(24)
param storageName string

param location string = resourceGroup().location

@allowed(['Standard_LRS', 'Standard_ZRS'])
param sku string = 'Standard_LRS'

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

output blobEndpoint string = storage.properties.primaryEndpoints.blob

The parts you just used:

Keyword Meaning
param A value supplied at deployment time, optionally with a default
resource Something to create, with its type and API version
output A value returned after deployment
@allowed, @minLength Rules checked before anything is deployed

Step 3: add a parameter file

Keep per-environment values out of the template. Save this as dev.bicepparam beside it:

using './main.bicep'

param storageName = 'stbicepdemo2611'
param sku = 'Standard_LRS'

Choose your own storage name, because it must be unique across Azure.

Step 4: validate, then preview

Validation catches bad values and syntax:

az deployment group validate \
  --resource-group rg-bicep-demo \
  --template-file main.bicep \
  --parameters dev.bicepparam

What-if shows what would change, without changing anything:

az deployment group what-if \
  --resource-group rg-bicep-demo \
  --template-file main.bicep \
  --parameters dev.bicepparam

Read the symbols in the output:

Symbol Meaning
+ Will be created
~ Will be modified
- Will be deleted
* Ignored by this deployment
= No change

Tip

What-if can report some properties as changing when they won't really change. Treat it as a review aid, and look closely at anything marked - or any unexpected ~.

Step 5: deploy

az deployment group create \
  --name first-storage \
  --resource-group rg-bicep-demo \
  --template-file main.bicep \
  --parameters dev.bicepparam

az deployment group show \
  --name first-storage \
  --resource-group rg-bicep-demo \
  --query properties.outputs

Run the same command again. Nothing breaks, because Bicep describes the desired state rather than a list of steps. That's why a template is safer to repeat than a script.

When the deployment fails

List the failed operations to get the real error:

az deployment operation group list \
  --name first-storage \
  --resource-group rg-bicep-demo \
  --query "[?properties.provisioningState=='Failed'].properties.statusMessage" -o json
Error Meaning Fix
StorageAccountAlreadyTaken The name is used elsewhere in Azure Pick another name
AccountNameInvalid The name has capitals or symbols Use lowercase letters and numbers
RequestDisallowedByPolicy An Azure Policy blocked it Read the policy name in the error
AuthorizationFailed Your role can't create this resource Ask for the role on this scope
InvalidTemplate The file has an error Run az bicep build --file main.bicep

The find the real deployment error cheat sheet covers more of these.

Caution

The default deployment mode is Incremental, which leaves resources not named in the template alone. Don't use --mode Complete while you're learning: it deletes resources in the group that aren't in the template.

Clean up

az group delete --name rg-bicep-demo --yes

This deletes everything in the group, so double-check the name first.

Prevent it next time

  • Keep templates in Git and review the what-if output in the pull request.
  • Use one parameter file per environment rather than copying the template.
  • Run az bicep decompile --file exported.json to get a starting point from an exported ARM template; expect to tidy up the result.
  • Once you're comfortable, read what-if and deployment stacks.