Bicep Error: 'BCP028: Identifier "storageAccount" is declared multiple times' — Cause, Fix, and Troubleshooting Guide
Fix Bicep 'Error BCP028: Identifier "storageAccount" is declared multiple times. Remove or rename the duplicates' by finding and renaming duplicate symbols.
- #iac
- #infrastructure-as-code
- #bicep
- #troubleshooting
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
BCP028 is a compile-time error raised by the Bicep CLI before anything is sent to Azure. Every symbolic name in a Bicep file — a param, var, resource, module, output, or type declaration — must be unique within that file. When two declarations share the same identifier, Bicep cannot decide which one a later reference means, so it refuses to build.
The error looks like this:
main.bicep(31,10) : Error BCP028: Identifier "storageAccount" is declared multiple times. Remove or rename the duplicates. [https://aka.ms/bicep/core-diagnostics#BCP028]
The (31,10) is line 31, column 10 — the location of one of the offending declarations. Bicep will typically emit BCP028 once per duplicate occurrence, so you may see the same identifier flagged at several lines. This is a pure naming collision; it is not about resource names in Azure (the name: property), only about the Bicep symbolic names you use to reference declarations inside the file.
Symptoms
az bicep buildoraz deployment group createfails immediately withError BCP028and a line/column reference.- The same identifier is flagged on two or more lines.
- VS Code’s Bicep extension underlines the duplicate names in red before you ever run a command.
- No ARM JSON is produced — the build never completes, so the deployment never starts.
Common Root Causes
1. Two resources given the same symbolic name
The classic case: copy-pasting a resource block and forgetting to rename the symbol. Both blocks are valid on their own, but they share storageAccount.
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'examplestordata'
location: location
// ...
}
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'examplestorlogs' // BCP028: "storageAccount" declared twice
location: location
}
2. A param and a var (or resource) sharing a name
Symbols share one namespace. A param named location and a var location = ... collide even though they are different kinds of declaration.
param storageAccount string
var storageAccount = 'examplestor' // BCP028
3. Duplicate output or module names
Two output blocks or two module declarations reusing an identifier hit the same rule.
module network 'network.bicep' = { name: 'net1', params: {} }
module network 'network.bicep' = { name: 'net2', params: {} } // BCP028
4. Merged files or bad rebase
Concatenating two Bicep snippets, or a Git merge that duplicated a block, silently reintroduces a name that already exists elsewhere in the file.
5. Loop variable colliding with an outer symbol
A for loop item variable (e.g. for storageAccount in accounts) that reuses the name of a top-level declaration.
How to Diagnose
Step 1: Read the line/column the compiler gives you
az bicep build --file main.bicep
The main.bicep(31,10) coordinate points at one occurrence. Jump there first.
Step 2: Find every occurrence of the identifier
Search the whole file (and any files you merged in) for the declaration keyword plus the name:
grep -nE '(resource|param|var|module|output|type)[[:space:]]+storageAccount\b' main.bicep
This lists every line that declares storageAccount, ignoring the many lines that merely reference it.
Step 3: Confirm with the language server
Open the file in VS Code with the Bicep extension. Duplicate symbols are underlined, and “Go to Definition” on the name will show multiple targets — a fast way to spot the second declaration you missed.
Fixes
Rename one of the declarations
Give each declaration a distinct, descriptive symbol and update its references:
resource dataStorageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'examplestordata'
location: location
}
resource logStorageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'examplestorlogs'
location: location
}
Collapse duplicates into a loop
If you truly wanted several near-identical resources, use a single declaration with a for loop instead of copy-pasting:
param storageNames array = [ 'examplestordata', 'examplestorlogs' ]
resource storageAccounts 'Microsoft.Storage/storageAccounts@2023-01-01' = [for name in storageNames: {
name: name
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}]
Remove the accidental duplicate
If a merge or paste created a straight duplicate, delete the redundant block and rebuild:
az bicep build --file main.bicep && echo "compiles clean"
What to Watch Out For
- Symbolic names share a single namespace across
param,var,resource,module,output, andtype— a name used once anywhere is used up. - Don’t confuse the symbolic name with the Azure
name:property; BCP028 is only about the former, and two resources can share neither symbol. - After a Git merge, run
az bicep buildbefore committing — merges are a frequent source of duplicated declarations. - Adopt a naming convention (
dataStorageAccount,logStorageAccount) so copy-paste collisions are obvious on sight. - Prefer loops over copy-pasted resource blocks when you need multiple similar resources; it removes the whole class of duplicate-symbol bugs.
Related Guides
- Bicep InvalidTemplate & BCP Compile Errors
- IaC Testing Strategies That Actually Work
- Managing Secrets in Infrastructure as Code
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.