Skip to content
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.

Tushar Verma · · 4 min read

In Bicep, compose names with string interpolation, for example var kvName = 'kv-${workload}-${environment}-${region}-${instance}', and use take() to enforce length limits. For production templates, the community avm/utl/naming module applies each resource type’s rules for you.

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 machine64Alphanumeric and hyphensYes
App Service60Alphanumeric and hyphensYes
Resource group90Alphanumeric, hyphens, underscores, periodsYes

Note that the Windows computer name (the OS host name) is capped at 15 characters and is set separately from the 64-character VM resource name, see the VM naming guide.

AzureNamer exports ready-to-paste Bicep for all 200+ Azure resource types, either exact names or a parameterized, variable-driven file, 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
  • Standardize names from day one, retrofitting them onto existing resources is painful

Try AzureNamer

Generate CAF compliant names for all 200+ Azure resource types, free, no login required.

Open the Generator →