Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Infrastructure as Code By James Joyner IV · · 10 min read

IaC Error Guide: 'InvalidTemplate' Bicep Deployment & BCP Compile Errors

Fix Bicep BCP compile errors and Deployment failed with InvalidTemplate: diagnose BCP033 type mismatch, missing parameters, circular dependencies, and bad ARM expressions.

  • #iac
  • #troubleshooting
  • #errors
  • #bicep

Overview

Bicep failures fall into two distinct phases. A BCP-coded error is a compile-time failure: the Bicep CLI refuses to transpile your .bicep file into an ARM JSON template, so nothing is ever sent to Azure. An InvalidTemplate error is a deploy-time failure: the file compiled fine, but Azure Resource Manager rejected the generated ARM template — usually because an expression cannot be resolved, a parameter is missing, or two resources depend on each other in a cycle.

A compile error looks like this:

main.bicep(42,28) : Error BCP033: Expected a value of type "string" but the provided value is of type "int".

A deploy-time rejection looks like this:

ERROR: {"code": "InvalidTemplate", "message": "Deployment template validation failed: 'The template resource 'storageAccount' reference to 'vnet' requires an API version. Please see https://aka.ms/arm-template for usage details.'."}

The key distinction: if you see a BCP code, az bicep build failed and the deployment never started. If you see InvalidTemplate, the build succeeded but ARM validation failed. The fix path differs completely, so always identify which phase failed first.

Symptoms

  • az deployment group create exits non-zero with BCP-prefixed errors and a line/column reference.
  • az deployment group create returns Deployment failed with InvalidTemplate after a clean compile.
  • az bicep build writes nothing and prints type or syntax errors.
  • A what-if preview fails before showing any change set.
az deployment group create \
  --resource-group rg-prod-eastus \
  --template-file main.bicep \
  --parameters @params.json
ERROR: {"status":"Failed","error":{"code":"InvalidTemplate","message":"Deployment template validation failed: 'The template parameter 'adminPassword' is not found in the template. Please see https://aka.ms/arm-template for usage details.'.","additionalInfo":[{"type":"TemplateViolation"}]}}

Common Root Causes

1. BCP033 — type mismatch

A value’s type does not match what the property or parameter expects. Passing an int where a string is required, or an object where an array is required, triggers BCP033 at compile time.

az bicep build --file main.bicep
main.bicep(42,28) : Error BCP033: Expected a value of type "string" but the provided value is of type "int". [https://aka.ms/bicep/core-diagnostics#BCP033]

Here line 42 passes enableHttpsTrafficOnly: 1 instead of enableHttpsTrafficOnly: true, or name: 7 where a string is required. Fix the literal or wrap it (string(myInt)).

2. BCP028 — duplicate identifier

The same symbol name is declared twice — two param, var, resource, or module statements share an identifier. Bicep cannot disambiguate them.

az bicep build --file main.bicep
main.bicep(58,11) : Error BCP028: Identifier "location" is declared multiple times. Remove or rename the duplicates. [https://aka.ms/bicep/core-diagnostics#BCP028]

This usually happens after copy-pasting a param location string block, or declaring both a param storageName and a var storageName. Rename one.

3. BCP018 — missing required property / character

The compiler expected a token (a colon, brace, or required property) and did not find it. Often a resource is missing a required field, or a syntax element was dropped.

az bicep build --file main.bicep
main.bicep(73,3) : Error BCP018: Expected the ":" character at this location. [https://aka.ms/bicep/core-diagnostics#BCP018]
main.bicep(81,1) : Error BCP035: The specified "resource" declaration is missing the following required properties: "sku". [https://aka.ms/bicep/core-diagnostics#BCP035]

Add the missing colon or required property. az bicep build lists every required property by name in the BCP035 companion error.

4. InvalidTemplate from an unresolved reference or bad ARM expression

The file compiled, but the generated ARM expression references something that does not exist at deploy time — a reference() to a resource that was never declared, or a missing apiVersion. ARM only catches this during validation.

az deployment group validate \
  --resource-group rg-prod-eastus \
  --template-file main.bicep \
  --parameters @params.json
ERROR: {"code":"InvalidTemplate","message":"Deployment template validation failed: 'The template resource 'nsgRule' at line '0' and column '0' is not valid: The language expression property 'subnetId' can't be evaluated.'."}

The expression refers to a symbolic name or output that resolves to null. Confirm the referenced resource is actually declared and spelled correctly.

5. Missing parameter

A parameter has no default and was not supplied in the parameters file or on the command line. ARM rejects the template because a required value is absent.

az deployment group create \
  --resource-group rg-prod-eastus \
  --template-file main.bicep \
  --parameters @params.json
ERROR: {"code":"InvalidTemplate","message":"Deployment template validation failed: 'The value for the template parameter 'adminPassword' at line '1' and column '20' is not provided. Please see https://aka.ms/arm-deploy/#parameter-file for usage details.'."}

Add the value to params.json or pass --parameters adminPassword=<value>. For secrets, reference a Key Vault in the parameters file rather than hard-coding.

6. Circular dependency, unsupported API version, or scope/linked-template mismatch

Two resources reference each other (dependsOn cycle), the apiVersion on a resource type is not recognized in the target region, or a module is deployed at the wrong scope (a subscription-scoped resource referenced from a resource-group deployment).

az bicep build --file main.bicep
az deployment group create --resource-group rg-prod-eastus --template-file main.bicep
main.bicep(90,1) : Error BCP080: The expression is involved in a cycle ("storageAccount" -> "diagnosticSettings" -> "storageAccount"). [https://aka.ms/bicep/core-diagnostics#BCP080]
ERROR: {"code":"InvalidTemplate","message":"Deployment template validation failed: 'The resource type 'Microsoft.Network/privateEndpoints' does not support API version '2018-01-01' in location 'westus3'.'."}

Break the cycle by removing one direction of the reference, bump the apiVersion to one the region supports, or correct the module scope property.

Diagnostic Workflow

Step 1: Determine the failure phase

az bicep build --file main.bicep --stdout > /dev/null

If this prints BCP errors, it is a compile failure — fix those first; the deployment never ran. If it succeeds silently, the failure is at deploy time (InvalidTemplate).

Step 2: Lint and read the BCP codes

az bicep lint --file main.bicep

Each error includes a file, line, column, code, and a docs link. Address the first error first — later errors are often cascade noise from the first.

Step 3: Validate against ARM without deploying

az deployment group validate \
  --resource-group rg-prod-eastus \
  --template-file main.bicep \
  --parameters @params.json

validate runs the same template checks as a real deployment but provisions nothing. It surfaces InvalidTemplate, missing parameters, and unsupported API versions safely.

Step 4: Preview the change set with what-if

az deployment group what-if \
  --resource-group rg-prod-eastus \
  --template-file main.bicep \
  --parameters @params.json

what-if resolves expressions and shows the resources that would be created/modified. If it fails before printing a change set, the template still has an unresolved reference.

Step 5: Inspect failed deployment operations

az deployment operation group list \
  --resource-group rg-prod-eastus \
  --name main \
  --query "[?properties.provisioningState=='Failed'].{res:properties.targetResource.resourceName, msg:properties.statusMessage}" \
  -o jsonpath 2>/dev/null || \
az deployment operation group list -g rg-prod-eastus -n main -o table

This pinpoints which resource operation failed and the ARM status message for it — essential when a multi-resource template partially deploys.

Example Root Cause Analysis

A pipeline runs az deployment group create for main.bicep and fails with:

ERROR: {"code":"InvalidTemplate","message":"Deployment template validation failed: 'The template resource 'privateEndpoint' reference to 'storageAccount' requires an API version. Please see https://aka.ms/arm-template for usage details.'."}

First, confirm the phase. az bicep build --file main.bicep --stdout > /dev/null succeeds — so this is deploy-time, not a compile error. Running validate reproduces it cleanly. Reading the template, the private endpoint references the storage account by its raw resource ID string rather than its symbolic name:

privateLinkServiceId: '/subscriptions/.../storageAccounts/${storageName}'

Because the ID is a concatenated string, ARM has no symbolic dependency and cannot infer the API version of the target — it wants an explicit reference() with a version. The fix is to reference the resource by its Bicep symbolic name so the dependency and API version are inferred:

# change the property to use the symbolic resource reference, then:
az deployment group validate -g rg-prod-eastus --template-file main.bicep --parameters @params.json

After switching privateLinkServiceId: storageAccount.id, validate passes and the deployment proceeds. The root cause was a hand-built resource ID defeating Bicep’s automatic dependency inference.

Prevention Best Practices

  • Run az bicep build and az bicep lint in CI on every commit so BCP errors fail fast, before a deployment is ever attempted.
  • Add az deployment group validate and az deployment group what-if as a required gate in pull requests — they catch InvalidTemplate and unsupported API versions without touching production.
  • Reference resources by their Bicep symbolic name (storageAccount.id) instead of hand-built ID strings so dependencies and API versions are inferred automatically.
  • Keep secrets out of parameter files by referencing Key Vault, and give every required parameter a sensible default or an explicit value in each environment’s params.json.
  • Pin and review apiVersion values against the regions you deploy to; an API version valid in one region may be unsupported in another. See more in infrastructure-as-code guides.
  • When a deployment fails mid-run, the free incident assistant can summarize the ARM operation log into the failing resource and likely cause.

Quick Command Reference

# Compile only — surfaces BCP errors before any deploy
az bicep build --file main.bicep

# Lint for style and correctness
az bicep lint --file main.bicep

# Validate against ARM without provisioning anything
az deployment group validate -g rg-prod-eastus --template-file main.bicep --parameters @params.json

# Preview the resolved change set
az deployment group what-if -g rg-prod-eastus --template-file main.bicep --parameters @params.json

# Deploy
az deployment group create -g rg-prod-eastus --template-file main.bicep --parameters @params.json

# Inspect which operation failed
az deployment operation group list -g rg-prod-eastus -n main -o table

Conclusion

A Bicep failure is either a compile-time BCP error (the build never produced a template) or a deploy-time InvalidTemplate (ARM rejected the generated JSON). Identify the phase first, then work the cause:

  1. BCP033 — a value’s type does not match the expected property or parameter type.
  2. BCP028 — an identifier (param/var/resource/module) is declared more than once.
  3. BCP018/BCP035 — a required character or required resource property is missing.
  4. InvalidTemplate from an unresolved reference() or a hand-built ID with no API version.
  5. A required parameter has no default and was not supplied.
  6. A circular dependsOn, an unsupported apiVersion in the region, or a module deployed at the wrong scope.

Run build, lint, validate, and what-if as CI gates and most of these never reach a real deployment.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

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.