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.
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:
| Resource | Type prefix | Example name |
|---|---|---|
| Virtual machine | vm | vm-payments-prod-eus-001 |
| Key Vault | kv | kv-payments-prod-eus-001 |
| Storage account | st | stpaymentsprodeus001 |
| App Service | app | app-payments-prod-eus-001 |
| Container Registry | cr | crpaymentsprodeus001 |
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:
| Environment | Abbreviation |
|---|---|
| Production | prod |
| Staging | stg |
| Development | dev |
| Testing | tst |
| UAT | uat |
Regions (community convention):
| Region | Abbreviation |
|---|---|
| East US | eus |
| East US 2 | eus2 |
| West US 2 | wus2 |
| West Europe | weu |
| North Europe | neu |
| UK South | uks |
| Southeast Asia | sea |
| Australia East | aue |
Key naming constraints
Different resource types have very different constraints. These are the ones most likely to trip you up:
| Resource | Max length | Allowed characters | Hyphens allowed |
|---|---|---|---|
| Storage account | 24 | Lowercase alphanumeric | No |
| Key Vault | 24 | Alphanumeric and hyphens | Yes |
| Container Registry | 50 | Alphanumeric | No |
| Virtual machine (Windows) | 15 | Alphanumeric and hyphens | Yes |
| Virtual machine (Linux) | 64 | Alphanumeric and hyphens | Yes |
| App Service | 60 | Alphanumeric and hyphens | Yes |
| Resource group | 90 | Alphanumeric, hyphens, underscores, periods | Yes |
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-namingmodule 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 →