bicepazurecafnaming conventionsiac

Azure Bicep Naming Conventions: CAF-Compliant Resource Names

How to generate and enforce CAF-compliant Azure resource names in Bicep templates — including the azure-naming module, string functions, and practical examples.

AzureNamer Team ·

If you’re writing Bicep templates, naming resources consistently is one of the first design decisions you’ll make — and one of the hardest to change later. This guide covers how to apply Microsoft’s Cloud Adoption Framework (CAF) naming conventions in Bicep, from simple string construction to reusable naming modules.

The CAF naming pattern

Microsoft’s CAF defines a standard component order:

{type}-{company}-{department}-{workload}-{environment}-{region}-{instance}

Not every resource uses every component. In practice, most teams use a simplified pattern:

{type}-{workload}-{environment}-{region}-{instance}

Examples:

ResourceType prefixExample name
Virtual machinevmvm-payments-prod-eus-001
Key Vaultkvkv-payments-prod-eus-001
Storage accountststpaymentsprodeus001
App Serviceappapp-payments-prod-eus-001
Container Registrycrcrpaymentsprodeus001

Building names in Bicep

Bicep gives you string interpolation and functions to compose names dynamically. Here’s a straightforward approach:

param workload string = 'payments'
param environment string = 'prod'
param region string = 'eus'
param instance string = '001'

var kvName = 'kv-${workload}-${environment}-${region}-${instance}'
var stName = 'st${workload}${environment}${region}${instance}'

resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: kvName
  location: resourceGroup().location
  properties: {
    sku: {
      family: 'A'
      name: 'standard'
    }
    tenantId: subscription().tenantId
  }
}

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

Note that storage accounts only allow lowercase alphanumeric characters — no hyphens. The variable definition reflects this.

Using the azure-naming Bicep module

The community azure-naming Bicep module handles resource-specific constraints automatically — max lengths, allowed characters, and separator rules per resource type.

module naming 'br/public:avm/utl/naming/azure:0.1.0' = {
  name: 'naming'
  params: {
    suffix: [workload, environment, region, instance]
  }
}

resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: naming.outputs.keyVaultName
  location: resourceGroup().location
  properties: {
    sku: { family: 'A', name: 'standard' }
    tenantId: subscription().tenantId
  }
}

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

This is the recommended approach for production templates — the module enforces length limits and character restrictions per resource type so you don’t have to manage them manually.

A reusable naming module (no external dependencies)

If you prefer not to use the public registry, here’s a lightweight naming module you can include directly in your repo:

// modules/naming.bicep
param workload string
param environment string
param region string
param instance string = '001'

var base = '${workload}-${environment}-${region}-${instance}'
var baseNoHyphen = toLower(replace(replace(base, '-', ''), '_', ''))

output keyVault string = take('kv-${base}', 24)
output storageAccount string = take('st${baseNoHyphen}', 24)
output appService string = take('app-${base}', 60)
output containerRegistry string = take('cr${baseNoHyphen}', 50)
output resourceGroup string = 'rg-${base}'
output virtualNetwork string = take('vnet-${base}', 64)
output keyVaultPrivateEndpoint string = take('pe-kv-${base}', 64)

Call it from your main template:

module names 'modules/naming.bicep' = {
  name: 'naming'
  params: {
    workload: 'payments'
    environment: 'prod'
    region: 'eus'
    instance: '001'
  }
}

resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: names.outputs.keyVault
  // resolves to: kv-payments-prod-eus-001
  location: resourceGroup().location
  properties: {
    sku: { family: 'A', name: 'standard' }
    tenantId: subscription().tenantId
  }
}

Environment and region abbreviations

Use consistent short codes across all templates:

Environments:

EnvironmentAbbreviation
Productionprod
Stagingstg
Developmentdev
Testingtst
UATuat

Regions (community convention):

RegionAbbreviation
East USeus
East US 2eus2
West US 2wus2
West Europeweu
North Europeneu
UK Southuks
Southeast Asiasea
Australia Eastaue

Key naming constraints

Different resource types have very different constraints. These are the ones most likely to trip you up:

ResourceMax lengthAllowed charactersHyphens allowed
Storage account24Lowercase alphanumericNo
Key Vault24Alphanumeric and hyphensYes
Container Registry50AlphanumericNo
Virtual machine (Windows)15Alphanumeric and hyphensYes
Virtual machine (Linux)64Alphanumeric and hyphensYes
App Service60Alphanumeric and hyphensYes
Resource group90Alphanumeric, hyphens, underscores, periodsYes

Enforcing conventions with Azure Policy

Naming conventions documented in a wiki don’t enforce themselves. Pair your Bicep templates with an Azure Policy Deny effect that rejects names not matching your pattern:

{
  "if": {
    "allOf": [
      { "field": "type", "equals": "Microsoft.KeyVault/vaults" },
      { "field": "name", "notMatch": "kv-*" }
    ]
  },
  "then": { "effect": "Deny" }
}

AzureNamer generates the Bicep resource snippet, the Terraform equivalent, and the Azure Policy for all 203 Azure resource types — free, no login required.

Summary

  • Use string interpolation for simple templates; the azure-naming module for production
  • Store naming parameters (workload, environment, region) in a params file — never hardcode them
  • Watch out for storage accounts and container registries: no hyphens, lowercase only, short max length
  • Enforce conventions with Azure Policy from day one — retrofitting names onto existing resources is painful

Try AzureNamer

Generate CAF-compliant names for all 203 Azure resource types — free, no login required.

Open the Generator →