Skip to content
DevOps AI ToolKit
Newsletter
All guides
Azure with AI By James Joyner IV · · 10 min read

Azure Error Guide: 'InvalidTemplateDeployment' ARM/Bicep Failures

Fix Azure InvalidTemplateDeployment and DeploymentFailed: diagnose ARM/Bicep schema errors, policy denials, bad parameters, dependsOn ordering, and API versions.

  • #azure
  • #troubleshooting
  • #errors
  • #arm

Overview

An Azure InvalidTemplateDeployment happens when Azure Resource Manager rejects a deployment before or during provisioning — the template parsed, but a pre-flight check, a parameter, a policy, or a resource operation failed. ARM wraps the underlying reason inside the InvalidTemplateDeployment envelope, so the real cause is almost always in a nested error.details block rather than the top-level message. A related DeploymentFailed appears when validation passed but a resource failed to provision at runtime.

You will see this in the CLI output or pipeline log:

(InvalidTemplateDeployment) The template deployment failed with error: 'The resource operation completed with terminal provisioning state 'Failed'.'
Code: InvalidTemplateDeployment
Message: The template deployment failed with error: ...

And the nested detail (the part that actually matters) reads like this:

{
  "code": "InvalidTemplateDeployment",
  "details": [
    {
      "code": "RequestDisallowedByPolicy",
      "message": "Resource 'stprod001' was disallowed by policy. Policy identifiers: 'Deny storage accounts without HTTPS'. ..."
    }
  ]
}

It occurs at az deployment group create time, in pipelines running ARM/Bicep, or during what-if and validate pre-flight. Because the envelope is generic, the fix depends entirely on the nested details[].code — schema, policy, parameter, dependency, or API version.

Symptoms

  • az deployment group create fails with (InvalidTemplateDeployment) and a generic top-level message.
  • A nested details[].code reveals the real cause (RequestDisallowedByPolicy, InvalidTemplate, MissingRegistrationForLocation, etc.).
  • DeploymentFailed appears with terminal provisioning state 'Failed' for a specific resource.
  • az deployment group validate rejects the template even before any resource is touched.
az deployment group create \
  --resource-group rg-prod \
  --template-file main.bicep \
  --parameters @prod.parameters.json
(InvalidTemplateDeployment) The template deployment failed with error: 'The resource operation completed with terminal provisioning state 'Failed'.'
Inner Errors:
{"code":"RequestDisallowedByPolicy","target":"stprod001"}
az deployment operation group list \
  --resource-group rg-prod --name main \
  --query "[?properties.provisioningState=='Failed'].properties.statusMessage.error" -o json
[ { "code": "RequestDisallowedByPolicy", "message": "Resource 'stprod001' was disallowed by policy..." } ]

Common Root Causes

1. Schema or validation error in the ARM/Bicep template

A malformed resource, an unknown property, or a Bicep syntax error fails compilation or ARM schema validation before deployment.

az bicep build --file main.bicep --stdout 1>/dev/null
Error BCP037: The property "skuName" is not allowed on objects of type "Microsoft.Storage/storageAccounts". Permissible properties include "sku".

az bicep build surfaces the exact line and property before ARM ever sees it.

2. Policy denial nested in InvalidTemplateDeployment

Azure Policy with a deny effect blocks the resource, and ARM reports it as RequestDisallowedByPolicy inside the InvalidTemplateDeployment envelope.

az deployment operation group list \
  --resource-group rg-prod --name main \
  --query "[?contains(properties.statusMessage.error.code, 'Policy')].properties.statusMessage.error" -o json
[ { "code": "RequestDisallowedByPolicy",
    "message": "Resource 'stprod001' was disallowed by policy. Policy identifiers: '[{\"policyAssignment\":{\"name\":\"Deny storage without HTTPS\"}}]'." } ]

The policy assignment name in the message tells you exactly which rule to satisfy.

3. Missing or invalid parameter

A required parameter has no value, or a supplied value violates an allowedValues / @allowed constraint.

az deployment group validate \
  --resource-group rg-prod \
  --template-file main.bicep \
  --parameters @prod.parameters.json
(InvalidTemplate) Deployment template validation failed: 'The provided value 'Standard_ZZZ' for the template parameter 'skuName' is not valid. The parameter value is not part of the allowed values: 'Standard_LRS', 'Standard_GRS'.'

4. Resource dependency ordering (dependsOn)

A resource references another that has not been created yet because an implicit reference or explicit dependsOn is missing, so ARM tries them out of order.

az deployment operation group list \
  --resource-group rg-prod --name main \
  --query "[?properties.provisioningState=='Failed'].{res:properties.targetResource.resourceType, msg:properties.statusMessage.error.message}" -o json
[ { "res": "Microsoft.Network/networkInterfaces",
    "msg": "Resource '/subscriptions/.../subnets/app' referenced by resource '/subscriptions/.../networkInterfaces/nic-01' was not found. ... It is possible that the referenced resource was not deployed yet." } ]

The “was not deployed yet” hint means the NIC needs a dependsOn (or a reference()) to the subnet.

5. API version mismatch

The apiVersion on a resource does not support a property used in the template, or is not available in the target region.

az provider show --namespace Microsoft.Storage \
  --query "resourceTypes[?resourceType=='storageAccounts'].apiVersions[] | [0:5]" -o tsv
2023-05-01
2023-04-01
2023-01-01
2022-09-01
2022-05-01

If the template pins an apiVersion not in this list, switch to a supported one shown here.

6. Pre-flight validation passes but runtime fails (DeploymentFailed)

validate and what-if only check what ARM can know ahead of time; a quota shortfall, a name collision, or a backend failure surfaces only at provision time as DeploymentFailed.

az deployment operation group list \
  --resource-group rg-prod --name main \
  --query "[?properties.statusMessage.error.code=='QuotaExceeded'].properties.statusMessage.error" -o json
[ { "code": "QuotaExceeded",
    "message": "Operation could not be completed as it results in exceeding approved standardDSv3Family Cores quota. Current Limit: 10, ..." } ]

A clean validate followed by a runtime QuotaExceeded confirms it is a provisioning-time failure, not a template flaw.

Diagnostic Workflow

Step 1: Compile the template locally first

az bicep build --file main.bicep --stdout 1>/dev/null
# (for ARM JSON, this step is skipped)

Step 2: Run pre-flight validation

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

Step 3: Preview the resource graph with what-if

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

Step 4: Extract the nested error from the failed deployment

az deployment operation group list \
  --resource-group rg-prod --name main \
  --query "[?properties.provisioningState=='Failed'].properties.statusMessage.error" -o json

The nested code (policy, quota, parameter, dependency) is what you actually fix.

Step 5: Confirm a supported API version when the error points at properties

az provider show --namespace <Namespace> \
  --query "resourceTypes[?resourceType=='<type>'].apiVersions" -o tsv

Example Root Cause Analysis

A Bicep deployment that creates a VNet, subnet, NIC, and VM fails with the generic envelope:

(InvalidTemplateDeployment) The template deployment failed with error: 'The resource operation completed with terminal provisioning state 'Failed'.'

The top-level message says nothing useful, so the nested operation error is pulled:

az deployment operation group list \
  --resource-group rg-net --name main \
  --query "[?properties.provisioningState=='Failed'].{res:properties.targetResource.resourceType, msg:properties.statusMessage.error.message}" -o json
[ { "res": "Microsoft.Network/networkInterfaces",
    "msg": "Resource '/subscriptions/.../virtualNetworks/vnet-app/subnets/app' referenced by resource 'nic-01' was not found. It is possible that the referenced resource was not deployed yet." } ]

The NIC references the subnet by a constructed resourceId() string rather than by symbolic reference, so Bicep never inferred an implicit dependency and ARM tried to create the NIC before the subnet existed. A what-if would have shown the NIC and subnet as parallel, not ordered.

Fix: reference the subnet symbolically (or add an explicit dependsOn) so ARM orders the creation correctly, then re-validate before re-deploying:

# after switching nic to: subnet: { id: appSubnet.id }
az bicep build --file main.bicep --stdout 1>/dev/null
az deployment group validate -g rg-net --template-file main.bicep --parameters @prod.parameters.json
az deployment group create -g rg-net --template-file main.bicep --parameters @prod.parameters.json

The symbolic reference creates an implicit dependsOn, the subnet provisions first, and the NIC and VM reach Succeeded.

Prevention Best Practices

  • Compile Bicep with az bicep build and run az deployment group validate in CI on every pull request so schema, parameter, and API-version errors fail fast before any resource is touched.
  • Run az deployment group what-if against the target environment to preview the resource graph and catch unexpected changes or ordering issues.
  • Prefer symbolic references over hand-built resourceId() strings so Bicep infers dependsOn for you and dependency-ordering failures disappear.
  • Test against the same Azure Policy assignments that production enforces; policy denials only appear when the deployment hits a scope with the deny effect.
  • Pin apiVersion values to ones returned by az provider show and review them when adopting new resource properties. See more in the Azure guides.
  • For ad-hoc triage, the free incident assistant can unwrap an InvalidTemplateDeployment envelope and surface the nested details[].code that you actually need to fix.

Quick Command Reference

# Compile Bicep locally (surfaces schema errors)
az bicep build --file main.bicep --stdout 1>/dev/null

# Pre-flight validation
az deployment group validate -g <RG> --template-file main.bicep --parameters @params.json

# Preview the resource graph
az deployment group what-if -g <RG> --template-file main.bicep --parameters @params.json

# Pull the nested error from a failed deployment (the real cause)
az deployment operation group list -g <RG> --name <DEPLOYMENT> \
  --query "[?properties.provisioningState=='Failed'].properties.statusMessage.error" -o json

# Isolate policy denials
az deployment operation group list -g <RG> --name <DEPLOYMENT> \
  --query "[?contains(properties.statusMessage.error.code,'Policy')].properties.statusMessage.error" -o json

# List supported API versions for a resource type
az provider show --namespace Microsoft.Storage \
  --query "resourceTypes[?resourceType=='storageAccounts'].apiVersions" -o tsv

# Re-deploy after fixing
az deployment group create -g <RG> --template-file main.bicep --parameters @params.json

Conclusion

An InvalidTemplateDeployment is a generic envelope around a specific nested failure; a DeploymentFailed means validation passed but a resource failed at provision time. The usual root causes:

  1. A schema or syntax error in the ARM/Bicep template that fails compilation or validation.
  2. An Azure Policy deny effect reported as RequestDisallowedByPolicy inside the envelope.
  3. A missing required parameter or a value outside the allowed set.
  4. A resource dependency-ordering problem from a missing dependsOn or symbolic reference.
  5. An apiVersion that does not support a used property or is unavailable in the region.
  6. A runtime-only failure (quota, name collision) that pre-flight validation cannot catch.

Always pull the nested details[].code from the failed deployment operation first — the envelope is generic, but the inner error tells you exactly what to fix.

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.