How to Fix Azure Resource Manager Templates

Microsoft Fix Intermediate 14 min read Official Docs Grounded Updated April 20, 2026

Why This Is Happening

I've seen this exact situation on dozens of Azure projects: you've spent hours writing your Azure Resource Manager templates, you push the deployment, and then , nothing. A cryptic error message, a deployment stuck in a "Running" state, or a template that looks perfectly valid but refuses to apply correctly. ARM template deployment errors are genuinely one of the most common pain points for Azure engineers at every level, from beginners deploying their first storage account to architects managing enterprise-wide infrastructure.

The core problem is that Azure Resource Manager templates are JSON files that define your infrastructure declaratively. That sounds simple. In practice, JSON is notoriously unforgiving , a single misplaced comma, an outdated API version, or a dependency declared in the wrong order can torpedo your entire deployment. And Azure's error messages? They often tell you what broke but not why or where.

Here's the full picture of what goes wrong most often:

  • Invalid JSON syntax, a trailing comma, missing bracket, or incorrect string escaping. The ARM template validator catches these, but the line numbers in error messages are often misleading.
  • Wrong or deprecated API versions, every resource type in an ARM template requires an explicit apiVersion. Using a version that doesn't exist for a given resource type, or using one that was retired, causes an immediate validation failure.
  • Parameter and variable mismatches, referencing a parameter name that doesn't exist in the parameters block, or using an expression that resolves to the wrong type at runtime.
  • Resource dependency ordering, ARM template deployment is partially parallel by design, which is great for speed, but if a resource tries to deploy before its dependency is ready and you haven't declared dependsOn correctly, you'll hit race condition failures.
  • Scope misconfigurations, deploying to a resource group when your template targets a subscription scope, or vice versa. The schema URL in the $schema field actually changes depending on your deployment scope, and many people copy templates without updating this.
  • Quota and permission errors, your template is fine, but the subscription doesn't have capacity in the target region, or the service principal running the deployment lacks the right RBAC role.

The frustrating part is that Azure doesn't always distinguish clearly between these categories. A "deployment failed" message in the Azure portal can mean any of the above. I know this is genuinely disruptive, especially when your CI/CD pipeline is gated on a successful ARM template deployment.

The good news: every one of these problems has a reliable fix, and I'm going to walk you through all of them. Browse all Microsoft fix guides →

The Quick Fix, Try This First

Before you go digging into every corner of your template, start with ARM template validation. This one step catches probably 70% of ARM template deployment errors before you even attempt a live deployment.

Open Azure Cloud Shell (hit the Cloud Shell icon in the top right of the Azure portal, it's the terminal icon) or use your local Azure CLI install. Then run this command:

az deployment group validate \
  --resource-group YOUR-RESOURCE-GROUP-NAME \
  --template-file azuredeploy.json \
  --parameters @azuredeploy.parameters.json

If you're using PowerShell instead:

Test-AzResourceGroupDeployment `
  -ResourceGroupName "YOUR-RESOURCE-GROUP-NAME" `
  -TemplateFile "azuredeploy.json" `
  -TemplateParameterFile "azuredeploy.parameters.json"

The validator runs the template through Azure Resource Manager's own engine server-side. It checks JSON syntax, schema conformance, API version validity, parameter types, and expression resolution, all without actually creating any resources. When it finds a problem, it returns a structured error that points you directly at the issue.

If the validate command passes clean but your actual deployment still fails, that tells you something important: the issue isn't in the template definition itself, it's a runtime condition, a quota limit, a permission error, a naming conflict, or a region availability issue. That distinction alone saves you enormous debugging time.

For an even more powerful option, use what-if deployment. This goes further than validation, it shows you exactly what changes ARM would make if you ran the deployment, including which resources would be created, modified, or deleted:

az deployment group what-if \
  --resource-group YOUR-RESOURCE-GROUP-NAME \
  --template-file azuredeploy.json \
  --parameters @azuredeploy.parameters.json

What-if is the closest thing to a "dry run" that Azure gives you. Use it every time before pushing to production.

Pro Tip
Run validation in your CI/CD pipeline as a required gate step before any deployment step. The ARM template validation API adds only seconds to your pipeline run and will catch template errors at pull request time, not at 2 AM when the production deployment fires. Wire it into GitHub Actions or Azure DevOps as a separate job that must succeed before the deployment job starts.
1
Validate Your JSON Schema and Structure

The very first line of every ARM template is the $schema declaration. This is not decoration, Azure Resource Manager uses it to determine what deployment scope you're targeting and which validation rules apply. If you copy a template and don't update this line, you'll get confusing errors that seem unrelated to the actual problem.

Here's the mapping you need to know:

# Resource Group scope (most common)
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#"

# Subscription scope
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#"

# Management Group scope
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#"

# Tenant scope
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#"

Open your azuredeploy.json file and check this first. Then check that your contentVersion follows the 1.0.0.0 format, it's a required field and must be a string in that format.

Next, validate the overall JSON structure. Your ARM template must have these top-level keys in valid JSON: $schema, contentVersion, and resources. The parameters, variables, functions, and outputs keys are optional but must be properly formed if present.

The fastest way to check raw JSON validity is to paste your template into Visual Studio Code with the Prettier extension installed, then run Format Document (Shift+Alt+F on Windows). If your JSON has syntax errors, VS Code will flag them with red underlines before you even touch Azure. After confirming the JSON is structurally valid, run the az deployment group validate command from the Quick Fix section. If validation passes, move on. If not, the error output will now make a lot more sense.

2
Fix API Version Errors in Your ARM Template

This is the single most common Azure Resource Manager template error I see. Every resource block in your template requires an apiVersion field, and it must be a version that actually exists for that specific resource type. Use a made-up version, a typo, or a version that was retired, and you'll get a validation error like: "No registered resource provider found for location X and API version Y for type Z."

Here's an example of a correctly structured storage account resource with a valid API version:

{
  "type": "Microsoft.Storage/storageAccounts",
  "apiVersion": "2025-06-01",
  "name": "mystorageaccount",
  "location": "[parameters('location')]",
  "sku": {
    "name": "Standard_LRS"
  },
  "kind": "StorageV2"
}

To find valid API versions for any resource type, run this Azure CLI command:

az provider show \
  --namespace Microsoft.Storage \
  --query "resourceTypes[?resourceType=='storageAccounts'].apiVersions[]" \
  --output table

Replace Microsoft.Storage with your resource provider namespace and storageAccounts with your resource type. This returns every supported API version, including preview versions. Pick the most recent stable (non-preview) version unless you specifically need a preview feature.

One thing the official docs make very clear: once you choose an API version and your template is deployed and working, stick with it. The idempotent nature of ARM templates means you can redeploy the same template repeatedly and get consistent results, but only if the API version stays constant. Bumping the API version in a running template can introduce breaking changes from the newer API contract that cause previously working deployments to fail.

After fixing API versions, rerun validation. If it clears, proceed to step 3.

3
Resolve Parameter and Variable Reference Errors

ARM template expressions use a function-based syntax inside square brackets: [expression]. When these expressions reference parameters, variables, or resource outputs incorrectly, deployment fails with errors like "Template parameter 'location' is not found" or "The template function 'reference' is not expected here."

The most common parameter mistakes:

// WRONG - typo in parameter name
"location": "[parameters('locaton')]"

// WRONG - referencing a parameter that isn't declared
"location": "[parameters('region')]"

// CORRECT - parameter declared AND referenced consistently
"parameters": {
  "location": {
    "type": "string",
    "defaultValue": "[resourceGroup().location]"
  }
},
...
"location": "[parameters('location')]"

Go through your template and do a manual audit: every parameters('X') call must have a matching X key in the top-level parameters block. Same rule applies to variables('X'), every variable reference must have a corresponding entry in variables.

Watch for type mismatches too. If a parameter is declared as "type": "int" but you're passing it a quoted string value in your parameters file, ARM will reject it. Your parameters file must pass values in the correct JSON type, integers without quotes, booleans as true/false without quotes, strings with quotes.

For parameters with allowedValues restrictions, the value in your parameters file must exactly match one of the allowed values, case-sensitive. Check this carefully if you're getting parameter validation errors that seem inexplicable.

Once you've verified every parameter and variable reference is consistent, run az deployment group validate again. Expression errors are among the most reliably caught by the validator.

4
Declare Resource Dependencies Correctly

One of ARM's most powerful features is that it deploys resources in parallel wherever possible. Resource Manager figures out the dependency graph and spins up independent resources simultaneously, which is why ARM deployments finish much faster than running a sequence of imperative CLI commands. But when you haven't told ARM about a dependency, it may try to deploy a resource before its prerequisite exists, and that's when you see errors like "Resource not found" or "The referenced resource does not exist" mid-deployment.

Use the dependsOn property to declare explicit dependencies:

{
  "type": "Microsoft.Network/networkInterfaces",
  "apiVersion": "2024-01-01",
  "name": "myNetworkInterface",
  "dependsOn": [
    "[resourceId('Microsoft.Network/virtualNetworks', 'myVNet')]",
    "[resourceId('Microsoft.Network/publicIPAddresses', 'myPublicIP')]"
  ],
  ...
}

The resourceId() function inside dependsOn is the correct pattern. Do not use the resource name as a plain string, use resourceId() so ARM can resolve the dependency correctly even across scopes.

A few rules to keep in mind:

  • Only declare direct dependencies in dependsOn. If Resource C depends on Resource B which depends on Resource A, you only need to declare C → B and B → A. Don't add C → A as well, ARM already infers transitive dependencies.
  • Circular dependencies will cause an immediate deployment failure. If Resource A depends on Resource B and Resource B depends on Resource A, the template is invalid. Refactor your resources to break the cycle.
  • For child resources defined inline inside a parent resource, the dependency is implicit, you don't need dependsOn.

After adding or correcting dependsOn declarations, run a what-if deployment. The output will show the planned creation order, which lets you visually verify the dependency chain looks right before committing to a real deployment.

5
Debug Failed Deployments Using the Azure Portal and CLI

When your ARM template deployment fails mid-flight, meaning validation passed and the deployment started but then stopped with an error, you need to dig into the deployment history to find out exactly which resource failed and why.

In the Azure Portal: navigate to your Resource Group, then select Settings > Deployments from the left menu. You'll see a list of all deployments for that resource group with status indicators. Click the failed deployment name, then look at the Operations tab. This shows every individual resource operation with its own status. Click any failed operation to see the full error detail, including the error code and message from the resource provider.

The error code is the key piece. Some common ones you'll encounter:

InvalidTemplate         , template structure or expression error
ResourceNotFound        , a referenced resource doesn't exist yet (dependency issue)
RequestDisallowedByPolicy, Azure Policy is blocking the resource type or config
QuotaExceeded           , subscription regional quota reached
AuthorizationFailed     , RBAC permissions insufficient for the deploying identity
InvalidResourceType     , resource type doesn't exist in the target region
Conflict                , resource already exists with different properties

From the CLI, you can pull deployment operation details directly:

az deployment operation group list \
  --resource-group YOUR-RESOURCE-GROUP \
  --name YOUR-DEPLOYMENT-NAME \
  --query "[?properties.provisioningState=='Failed']" \
  --output json

The JSON output gives you the statusMessage field with the full error detail from the resource provider, often more verbose than what the portal shows. Once you identify the specific resource and error code, you have a precise fix target. Address it, rerun the template, and monitor the Operations tab again to confirm that resource now succeeds.

Advanced Troubleshooting

If the standard steps haven't resolved your Azure Resource Manager template issues, these deeper techniques cover the scenarios that catch experienced engineers off guard.

Linked and Nested ARM Templates Failing

One of ARM's most powerful architectural features is the ability to break your deployment into modular, reusable pieces and link them together at deployment time. A parent template references child templates, which can themselves reference other templates. This is how you'd typically deploy a three-tier application, a main template linking three purpose-specific templates, one per tier.

The catch: linked templates must be accessible via a public HTTPS URL at deployment time. If you reference a linked template URI that's unreachable (a private storage account with no SAS token, a GitHub private repo URL, a local file path), the deployment fails with a URI resolution error. Fix this by placing linked templates in Azure Blob Storage with a Shared Access Signature token appended to the URI, or use template specs instead, the modern, supported approach for storing and sharing ARM templates within your organization.

For nested templates (defined inline within the parent template's resources` array using "type": "Microsoft.Resources/deployments"), scope is a common pitfall. By default, nested templates inherit the parent scope. If you need to deploy to a different scope, set the expressionEvaluationOptions property to "scope": "inner" so the nested template resolves its own expressions independently.

Enterprise and Domain-Joined Scenarios

In enterprise environments, Azure Policy is frequently the invisible wall behind ARM template deployment failures. Your template may be technically valid, but if a policy denies the resource configuration, a storage account without HTTPS-only enforcement, a VM SKU not on the approved list, or a resource being deployed to a non-compliant region, you'll get an RequestDisallowedByPolicy error. Run this to identify relevant policies:

az policy state list \
  --resource-group YOUR-RESOURCE-GROUP \
  --filter "complianceState eq 'NonCompliant'" \
  --output table

Service principal permissions are another enterprise gotcha. The identity running your ARM deployment, whether a managed identity, service principal, or user account, must have appropriate RBAC at the deployment scope. For resource group deployments, the deploying identity typically needs at minimum the Contributor role on the target resource group. For subscription-scoped deployments, it needs Contributor at the subscription level.

Template Spec Versioning Issues

Template specs let you store ARM templates as first-class Azure resources and manage access with RBAC. Users with read access can deploy the spec but cannot modify the underlying template, which is exactly what you want for shared infrastructure standards. If you're getting errors when deploying from a template spec, check that the spec version you're referencing actually exists:

az ts show \
  --name YOUR-SPEC-NAME \
  --resource-group YOUR-SPEC-RG \
  --version "1.0"
When to Call Microsoft Support

If you've validated the template, confirmed API versions, checked permissions, ruled out Azure Policy, and the deployment still fails with an error code you can't trace, especially errors from the resource provider itself (like StorageErrorCode or provider-specific 5xx errors), it's time to escalate. Open the Azure Portal, click the Support + Troubleshooting icon (the question mark, top right), type "ARM template" in the issue description, select Portal under Monitoring & Management, choose your subscription, then select Issue with ARM templates. This routes you to the team that actually owns the ARM deployment engine. For anything involving billing impact or production outages, use severity A or B. You can also reach Microsoft Support directly with your deployment correlation ID, pull that from the deployment history before you call.

Prevention & Best Practices

Most ARM template deployment errors are preventable. Here's what I've seen the most effective teams do consistently.

Pin your API versions and review them on a schedule. The idempotent nature of ARM templates is a huge advantage, the same template deployed a hundred times produces the same result. But that guarantee only holds when the API version is stable. Pick a specific, non-preview API version for each resource, document your choices, and set a quarterly reminder to check for newer stable versions. Don't chase every new version, but don't let your templates go years without a review either.

Use Bicep for new templates. The official Microsoft docs make this recommendation clearly: Bicep is the preferred approach for new infrastructure-as-code work on Azure. Every Bicep file is automatically compiled to a valid ARM template at deployment time, so you get all the ARM benefits, idempotency, parallel deployment, full Azure resource coverage, with a cleaner syntax that's much harder to break with a stray comma. If you're starting a new project, start with Bicep. If you have existing ARM templates, there's no urgency to migrate, but new additions should be Bicep.

Store templates in source control and treat them like application code. Version your ARM templates in Git, require pull request reviews before merging template changes, and tag releases. This gives you a full audit trail of every infrastructure change and makes rollback straightforward. Anyone on your team should be able to run the template and deploy a consistent environment, that's the infrastructure-as-code promise, and it only works if the templates are in source control.

Run what-if in every CI/CD pipeline before production deployments. What-if deployment is fast, safe, and catches configuration drift, cases where someone manually changed a resource in the portal and the template no longer reflects reality. Make it a required step in your pipeline. If what-if shows unexpected deletions or modifications, that's a conversation to have before the deployment runs, not after.

Quick Wins
  • Add az deployment group validate as a required CI gate step on every pull request that touches ARM templates
  • Use the defaultValue: "[resourceGroup().location]" pattern for the location parameter so deployments automatically target the resource group's region without hardcoding
  • Break large monolithic ARM templates into smaller, purpose-specific templates linked by a main template, easier to debug, easier to reuse, easier to test independently
  • Use template specs to share approved, validated templates across teams rather than copying JSON files around, RBAC controls who can deploy vs. who can modify

Frequently Asked Questions

Why should I use Azure Resource Manager templates instead of just clicking through the portal?

The portal is fine for one-off exploration, but it doesn't scale and it doesn't give you consistency. ARM templates are declarative, you define what you want, and Azure makes it happen, every time. They're idempotent, meaning you can deploy the same template a hundred times and get the same result without worrying about accidental duplicate resources or configuration drift. They're also version-controlled, so your infrastructure history lives in Git alongside your application code. For any environment you need to reproduce, dev, staging, prod, disaster recovery, ARM templates are the right answer.

What's the difference between ARM templates and Bicep, and should I switch?

Bicep is Microsoft's newer domain-specific language for Azure infrastructure-as-code. Under the hood, every Bicep file compiles directly to an ARM template, so they're not competing technologies, Bicep is just a better way to write ARM templates. The syntax is cleaner, less repetitive, and much harder to break with JSON-specific mistakes like trailing commas. Microsoft's official recommendation is to use Bicep for new projects. If you have existing ARM templates that work, there's no pressure to migrate immediately, but Bicep is where the tooling investment is going.

My ARM template deployment says "succeeded" but some resources are missing, what happened?

This usually means a subset of resources deployed successfully while others hit errors, and the overall deployment status reflects the majority or the last-evaluated condition. Go to your Resource Group in the Azure portal, click Settings > Deployments, select the deployment, and open the Operations tab. Each resource operation has its own status. Look for any operations showing Failed or Canceled, those are your missing resources. The error message on each failed operation tells you exactly why it didn't deploy. Fix those specific resources in your template and redeploy, because ARM is idempotent, resources that already exist and haven't changed won't be recreated.

How do I find the right API version for a resource type in my ARM template?

The most reliable method is the Azure CLI: run az provider show --namespace Microsoft.YourProvider --query "resourceTypes[?resourceType=='yourResourceType'].apiVersions[]" --output table. This hits the live Azure API and returns every supported version for that resource type in your subscription, including preview versions. Pick the most recent non-preview version for production templates. You can also check the Azure Resource Manager REST API reference documentation, which lists all valid API versions per resource type. Avoid copying API versions from old blog posts, they go stale fast.

Can I deploy an ARM template to multiple regions at once?

Not from a single deployment command, each ARM deployment targets one scope (resource group, subscription, management group, or tenant). To deploy to multiple regions, you'd typically use a subscription-scoped or management group-scoped ARM template that creates resource groups in each target region and then deploys resources into them using nested deployments. Alternatively, you can run multiple parallel deployments from a CI/CD pipeline, each targeting a different regional resource group with the same template. The template itself stays the same; only the target resource group and the location parameter change per deployment.

How do I open a support ticket specifically for an ARM template deployment failure?

In the Azure Portal, click the Support + Troubleshooting icon (question mark) in the top-right corner. Type "ARM template" in the issue description field and click Go. When asked which service, select Portal under the Monitoring & Management category, then click Next. Select your subscription, click Next, then choose Issue with ARM templates and proceed through the ticket creation flow. Before you submit, grab your deployment correlation ID from the deployment history, it's the fastest way for Microsoft support to pull up the exact deployment logs on their end.

Related Microsoft Fix Guides

H
Sai Kiran Pandrala
Our team includes certified Microsoft engineers, Azure architects, and system administrators with 10+ years of enterprise IT experience. Every guide is written from hands-on troubleshooting, not guesswork. We test every fix before publishing.