Deploy an ARM Template from CLI

Deploy an ARM template from CLI — Azure Tutorial. Learn how to deploy Azure resources using ARM templates via the Azure CLI, with hands-on steps and best practices.

Focus: deploy an arm template from cli

Sponsored

You’ve written infrastructure as code, committed it to a repo, and maybe even deployed a template from the portal. But now you’re staring at a terminal, wondering how to deploy an ARM template from CLI without clicking through Azure’s web console. The pain is real: manual deployments are slow, error-prone, and impossible to reproduce in CI/CD pipelines. The Azure CLI makes this feel like a superpower — one command turns your JSON template into live, verified Azure resources, and it works equally well on your laptop and in a GitHub Actions workflow.

The problem this lesson solves

Imagine you need to spin up a resource group, a storage account, and a virtual network for a new environment — maybe staging, maybe a test sandbox. Doing that through the Azure portal takes dozens of clicks per resource, and every click is a chance to misconfigure a name, a region, or a permission. Worse, you can’t rerun the same steps for another environment without starting over.

That’s the core problem: manual cloud provisioning doesn’t scale for developers or operations teams. The solution is infrastructure as code — you define your entire environment in a declarative JSON template (an ARM template), then deploy it repeatedly with a single command. But knowing the template syntax isn’t enough; you must master the deployment workflow from the command line, including validating, creating, and troubleshooting. This lesson fills that gap, showing you exactly how to deploy an ARM template from CLI — the Azure CLI, to be precise — and how to do it safely and predictably.

Core concept / mental model

Think of an ARM template as a blueprint for your Azure resources. It describes what you want (a storage account, a VM, a database) and how those resources relate, but not how Azure builds them. The Azure CLI is the builder that reads the blueprint and orders the work.

A deployment is the act of telling Azure: “Here’s my template — make it real.” The CLI sends your template to the Azure Resource Manager (ARM) service, which then provisions or updates resources to match the template’s desired state. Crucially, ARM deployments are idempotent: if you run the same deployment twice, Azure will not create duplicates — it reconciles the current state with the template’s intended state.

Three core concepts anchor the mental model:

  1. Deployment scope — where the deployment takes effect: resource group, subscription, management group, or tenant. Most commonly for beginners: a resource group.
  2. Template and parameters — the template (JSON) defines resources; a parameters file (also JSON) supplies environment-specific values like names and SKUs.
  3. Deployment name — a required, unique identifier for each deployment operation, which shows up in Azure activity logs and helps you track what happened.

A helpful analogy: think of your template as a Dockerfile and the CLI as docker build. You don’t manually construct the container; you describe it and let the tool produce the result — you can rebuild it anywhere, anytime.

How it works step by step

The journey from template to live resources follows a logical sequence:

  1. Authenticate — your CLI must be logged into Azure and have permission to create resources in the target subscription.
  2. Prepare your template — write or download a valid ARM template (JSON) and, optionally, a parameters file.
  3. Validate — run a test deployment (what-if or dry-run) to catch errors before committing changes.
  4. Deploy — issue the deployment command, pointing to your template and parameters.
  5. Verify — confirm resources were created as expected via CLI queries or the portal.

Each step has a cause-and-effect relationship. For example, if you skip validation, a typo in your resource type will cause deployment to fail midway — but you could have caught it early. If you miss the parameters file, the CLI will prompt you interactively, which breaks automation.

The magic happens in step 4. For a resource group–scoped deployment, you use the az deployment group create command. For subscription-level deployments (e.g., creating a resource group itself), you use az deployment sub create. The key difference is what you can deploy and how you define parameters.

Hands-on walkthrough

Prerequisites

  • Azure CLI installed (≥ 2.40.0) — check with az version.
  • You are logged in (az login) and have selected the correct subscription (az account set --subscription "<id>").
  • A local file template.json with a simple storage account definition.

Example 1: Create a resource group and deploy a simple template

First, create a resource group (if you don’t have one):

az group create --name rg-demo-eastus --location eastus

Next, save this minimal ARM template as template.json in your working directory:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2023-01-01",
      "name": "[concat('storagedemo', uniqueString(resourceGroup().id))]",
      "location": "[resourceGroup().location]",
      "sku": {
        "name": "Standard_LRS"
      },
      "kind": "StorageV2"
    }
  ]
}

Deploy it:

az deployment group create \
  --resource-group rg-demo-eastus \
  --template-file template.json \
  --name demo-deployment-001

Expected output (trimmed):

{
  "id": "/subscriptions/.../providers/Microsoft.Resources/deployments/demo-deployment-001",
  "properties": {
    "provisioningState": "Succeeded",
    ...
  }
}

The storage account now exists, and you can verify it with az storage account list --resource-group rg-demo-eastus.

Example 2: Use a parameters file for reusable deployments

The beauty of ARM templates is that the same template can deploy to different environments with different parameter values. Create a parameters.json file:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "value": "storagedemoeast"
    },
    "skuName": {
      "value": "Standard_GRS"
    }
  }
}

Now modify your template to accept parameters:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "type": "string",
      "defaultValue": "[concat('stor', uniqueString(resourceGroup().id))]"
    },
    "skuName": {
      "type": "string",
      "defaultValue": "Standard_LRS"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2023-01-01",
      "name": "[parameters('storageAccountName')]",
      "location": "[resourceGroup().location]",
      "sku": {
        "name": "[parameters('skuName')]"
      },
      "kind": "StorageV2"
    }
  ]
}

Run the deployment referencing both files:

az deployment group create \
  --resource-group rg-demo-eastus \
  --template-file template.json \
  --parameters @parameters.json \
  --name demo-deployment-002

Now you can deploy to westus by swapping the parameter values — no template edits needed.

Example 3: Validate before you commit

Never deploy a broken template. Use the --what-if flag to preview changes without applying them:

az deployment group what-if \
  --resource-group rg-demo-eastus \
  --template-file template.json \
  --parameters @parameters.json

You’ll see a color-coded list of resources to create, modify, or delete. If your template has a syntax error, this command will report it without touching your resource group.

Compare options / when to choose what

You have several ways to deploy ARM templates, and the CLI is just one of them. Here’s how they stack up:

Method Best for Automation Learning curve
Azure CLI Local dev, quick tests, scripted ops Great (can be called in scripts) Low
Azure PowerShell Windows-centric workflows, PowerShell skills Great (same as CLI) Low-Medium
Azure Portal (deploy custom template) One-off manual deployments, visual review Poor Low
REST API Custom tooling, deep integration Excellent High
CI/CD pipelines (GitHub Actions or Azure DevOps) Production deployments, team collaboration Excellent Medium-High

When to choose CLI: For day-to-day development, quick experiments, and when you want a single command that works locally and in scripts. For production, you’d likely wrap the same CLI commands inside a pipeline, but the command itself is identical.

Pro tip: The CLI is essentially a thin wrapper over the ARM REST API. Every az deployment ... command translates to an HTTP request to Azure. This means you can use the same command in a shell script, a CI job, or an Azure Cloud Shell session — no extra dependencies.

Variation: Deployment modes. The CLI supports two deployment modes: Incremental (default) and Complete. Incremental only adds or updates resources; Complete also deletes any resources in the resource group that aren’t in the template. Use --mode Complete with extreme caution — it’s risky and rarely needed outside special cases.

Troubleshooting & edge cases

Error: “ authorization failed ”

If your CLI user doesn’t have Contributor rights on the resource group, deployments will fail. Fix: run az role assignment create to grant the user the proper role, or ask your admin.

Error: “ InvalidTemplate ” or “ Deployment failed ”

This usually means your template has a syntax error, a missing parameter, or an unsupported API version. Fix: examine the error message — it often points to the exact JSON path. Use az deployment group validate to get more detailed feedback:

az deployment group validate \
  --resource-group rg-demo-eastus \
  --template-file template.json \
  --parameters @parameters.json

Resource name conflicts

Storage account names must be globally unique. If you try to create mystorageaccount and it’s taken, deployment fails. Fix: use uniqueString() or [concat('mystore', uniqueString(resourceGroup().id))] as shown in the example.

Deployment name already exists

Deployments are immutable — you cannot reuse a deployment name within the same scope. Fix: append a timestamp to your deployment name in scripts, e.g., --name $(date +%s).

Template exceeds size limits

The ARM template size limit is 4 MB (for linked templates, it’s different). For larger templates, break them into linked or nested templates.

Edge case: subscription-level deployments

If you need to deploy a resource group itself, use az deployment sub create and include the resource group definition in your template. This is common for management-group-level policies.

What you learned & what's next

You’ve now mastered the essential skill of deploying an ARM template from CLI. You can:

  • Explain the core idea: ARM templates are declarative blueprints, and the CLI turns them into live resources via resource-manager deployments.
  • Complete a practical exercise: create a resource group, validate with --what-if, and deploy a template with parameters using az deployment group create.
  • Identify when to use the CLI versus other methods, and troubleshoot common failures like authorization errors, invalid templates, and naming collisions.

These fundamentals directly enable the next lessons in this Azure track: you’ll soon wire these deployments into CI/CD pipelines, manage state with Bicep (an easier template language), and handle multi-environment deployments with confidence. In the next lesson, you’ll learn how to parameterize and reuse templates across environments — a natural step after mastering the CLI deployment flow.

Pro tip: Save your ARM template and parameters file in a Git repo alongside your application code. Then, any teammate (or a CI job) can recreate your environment with one command — that’s the foundation of true infrastructure as code.

Practice recap

Create a new resource group, then copy the template from the hands-on section and deploy it with az deployment group create and a parameters file. Run --what-if first to see the predicted changes, then deploy and verify with az resource list. Try changing the skuName parameter and redeploying to see how ARM reconciles the state.

Common mistakes

  • Running --what-if is optional, but skipping it can cost you minutes of failed deployments — always validate first.
  • Not picking a unique deployment name — reusing a name will fail; append a timestamp or unique suffix.
  • Forgetting to include required parameters; the CLI will prompt interactively, which breaks automation — always pass a parameters file.
  • Using --mode Complete without understanding it — this can delete resources you intended to keep.

Variations

  1. Azure PowerShell: use New-AzResourceGroupDeployment instead of az deployment group create.
  2. Bicep: a friendlier DSL that compiles to ARM templates; deploy with az deployment group create --template-file main.bicep.
  3. REST API: call PUT /subscriptions/{subId}/resourcegroups/{rg}/providers/Microsoft.Resources/deployments/{name} directly for custom integrations.

Real-world use cases

  • Deploying a storage account and network resources for a staging environment from a CI pipeline using the same CLI command.
  • Recreating a development sandbox every morning by running a single az deployment group create script that pulls the latest template from Git.
  • Teaching a team to move from portal click-ops to reproducible deployments by sharing a one-command template that provisions a demo app.

Key takeaways

  • ARM templates are declarative blueprints — the CLI is just the builder that makes them real.
  • Always validate with az deployment group what-if before applying changes — it saves time and surprises.
  • Use parameters files to keep templates reusable across environments without editing JSON.
  • The Azure CLI command for resource group deployments is az deployment group create.
  • Deployment names must be unique within a scope — use timestamps in automation.
  • Incremental is the default and safe mode; avoid --mode Complete unless you really know what you're doing.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.