ARM Templates and Bicep: AZ-104 Deployment Guide
Azure Resource Manager (ARM) templates and Bicep files let you define your infrastructure as code, so you deploy the same resources the same way every time instead of clicking through the portal by hand. An ARM template is a JSON document with a fixed structure of parameters, variables, resources, and outputs. Bicep is a cleaner, declarative language that transpiles down to that same ARM JSON, so anything you can do in ARM you can do in Bicep with far less syntax. As an Azure administrator preparing for AZ-104, you need to read either format, modify it to add a resource or change a parameter, and deploy it with the correct deployment mode. In this lesson you will learn how to interpret an ARM template and a Bicep file, how to modify them safely, the difference between Incremental and Complete deployment modes and why Complete can delete resources, and how to export a deployment as a template or convert ARM JSON to Bicep.
On this page7 sections
- Interpret the structure of an ARM template — parameters, variables, resources, and outputs
- Interpret a Bicep file and explain how it transpiles to ARM JSON
- Modify an existing template to add a resource or change a parameter value
- Choose between Incremental and Complete deployment modes and predict which resources are deleted
- Deploy a template using the portal, Azure CLI, or Azure PowerShell
- Export a deployment as a template and convert ARM JSON to Bicep with decompile
Infrastructure as code with ARM and Bicep
Infrastructure as code means describing the Azure resources you want in a text file and letting Azure Resource Manager create them for you, rather than building each resource by hand in the portal. The file is declarative: you state the desired end result — a storage account here, a virtual network there — and Azure Resource Manager works out the order and the API calls needed to reach it. You do not write step-by-step commands.
There are two authoring formats you must know. An ARM template is a JSON file that Azure Resource Manager has understood for years. A Bicep file is a newer, purpose-built language that is much easier to read and write; when you deploy it, the Bicep tooling transpiles (compiles) it into an ordinary ARM template behind the scenes, so Azure Resource Manager still receives JSON. Bicep is a full replacement authoring experience, not a separate engine.
Both formats are idempotent: deploying the same file twice produces the same result, because Azure Resource Manager compares your declared state to what already exists and only changes what is different. This is why templates are safe to run repeatedly in a pipeline. On the exam, remember that Bicep and ARM JSON are two ways to describe the same deployment, and that a declarative template describes the end state, not the steps to get there.
Interpreting an ARM template: the JSON structure
Every ARM template is a JSON object with a small set of top-level sections, and the exam expects you to recognise each one by its job. The two required elements are $schema (which version of the template language this file uses) and contentVersion (your own version stamp). After those come the working sections:
- parameters — values the person deploying supplies at deployment time, such as a VM name, admin username, or environment. Parameters make one template reusable across dev, test, and production.
- variables — values you compute or reuse inside the template, such as a naming convention built from a parameter. Variables keep the resource definitions tidy and avoid repetition.
- resources — the heart of the template: the actual Azure resources to create or update, each with a
type,apiVersion,name,location, andproperties. - outputs — values returned after deployment, such as a public IP address or a connection string, so a pipeline or a later template can use them.
A template can also declare functions and use built-in expressions such as resourceGroup().location or parameters('vmName') to keep values dynamic. When you read an ARM template on the exam, trace a value from a parameter, through a variable, into a resource property, and finally to an output — that flow is exactly what the questions test.
Interpreting a Bicep file
A Bicep file describes the same resources as an ARM template but with far less noise. Where JSON needs nested braces, quotation marks, and long function calls, Bicep uses a clean, declarative syntax that reads almost like a list of statements. You declare a parameter with param adminUsername string, a variable with var storageName = ..., and a resource with a resource keyword followed by a symbolic name and its type.
The biggest day-to-day win is symbolic references. In Bicep you refer to another resource by the name you gave it — for example nic.id to get a network interface's resource ID — and Bicep works out the dependency order for you automatically. In raw ARM JSON you often have to add explicit dependsOn arrays by hand. Bicep also gives you type checking and IntelliSense in the editor, so mistakes surface before you deploy, and modules that let you break a large deployment into reusable files.
Crucially, Bicep is not a different deployment engine. When you deploy, the tooling transpiles your .bicep file into an ARM template and hands that JSON to Azure Resource Manager. So a Bicep file and its generated ARM template produce an identical deployment. On the exam, if a scenario wants cleaner, more maintainable authoring with automatic dependency management, the answer is Bicep, not hand-written JSON.
ARM template versus Bicep: a comparison
Both formats deploy the same resources through Azure Resource Manager, so the choice is about authoring experience, not capability. The table below summarises the differences you should recognise on the exam.
| Aspect | ARM template (JSON) | Bicep file |
|---|---|---|
| Format | JSON with braces and quotes | Concise domain-specific language |
| Readability | Verbose, harder to scan | Clean and easy to read |
| Dependencies | Often explicit dependsOn | Inferred from symbolic references |
| Tooling | Basic editor support | Type checking, IntelliSense, modules |
| Engine | Sent directly to Resource Manager | Transpiled to ARM JSON first |
| File extension | .json | .bicep |
The key point is that anything you can express in an ARM template you can express in Bicep, because Bicep compiles down to the same JSON. Microsoft positions Bicep as the recommended language for new Azure deployments, while ARM JSON remains fully supported for existing templates. You can also move between them freely — a two-way conversion covered later — so teams with a library of JSON templates are not locked out of Bicep. When a question stresses maintainability, modules, or automatic dependency handling, lean Bicep; when it references an existing JSON template or an exported template, that is ARM.
Modifying an existing template or Bicep file
Most real work is not writing a template from scratch but modifying an existing one, and the exam tests two everyday edits: adding a resource and changing a parameter.
To change a parameter, you either edit the parameter's defaultValue in the template or, more commonly, supply a new value at deployment time — through a separate parameters file (azuredeploy.parameters.json), a --parameters flag on the CLI, or the portal's parameter form. Changing a parameter such as VM size or environment name lets one template serve many deployments without editing the resource definitions themselves.
To add a resource, you insert a new entry into the resources array in ARM JSON, or add a new resource block in Bicep. In ARM JSON you must give it a type, apiVersion, name, location, and properties, and add a dependsOn if it relies on another resource. In Bicep you simply reference the other resource symbolically and the dependency is handled for you. For example, adding a data disk to an existing VM template means adding a disk resource and referencing it from the VM's storageProfile.
Because templates are idempotent, you can redeploy the edited file over the existing deployment: Azure Resource Manager adds the new resource and leaves the untouched ones alone — provided you use the default Incremental mode, which the next section explains.
Deployment modes: Incremental versus Complete
When you deploy a template you choose a deployment mode, and this is the single most tested — and most dangerous — concept in the topic. The mode decides what happens to resources that already exist in the target resource group.
Incremental mode is the default. Azure Resource Manager adds or updates the resources in your template and leaves any other resources in the resource group untouched. Nothing is deleted just because it is missing from the template. This is the safe, everyday choice.
Complete mode makes the resource group match the template exactly. Azure Resource Manager deploys what is in the template and then deletes any resource in the resource group that is not defined in the template. This guarantees the group contains only what you declared, but it removes anything you forgot to include.
| Behaviour | Incremental (default) | Complete |
|---|---|---|
| Resources in the template | Created or updated | Created or updated |
| Existing resources not in the template | Left unchanged | Deleted |
| Risk of accidental deletion | None | High |
| Typical use | Everyday deployments | Enforcing an exact set of resources |
Scenario: a resource group holds a web app and a separate storage account created earlier by a colleague. You redeploy your template — which defines only the web app — in Complete mode. Because the storage account is not in the template, Complete mode deletes it. Had you used Incremental mode, the storage account would have survived. On the exam, the trigger phrase "deletes resources not in the template" always means Complete deployment mode.
Deploying, exporting, and converting templates
You can deploy a template three ways, and the exam may reference any of them. In the portal, use "Deploy a custom template" to paste or load a file. With the Azure CLI, run az deployment group create --resource-group rg --template-file main.bicep, adding --mode Complete to change the mode. With Azure PowerShell, run New-AzResourceGroupDeployment. Deployments target a scope — most often a resource group, but also a subscription, management group, or tenant. You can also package a template as a template spec, a resource stored in Azure so a team can share and version a template centrally.
Two operations help you work with existing infrastructure. Exporting a template generates a template from resources you built by hand: use "Export template" on a resource group, or export from the deployment history of a past deployment. Exported templates often need cleanup before reuse, but they are a fast starting point.
Converting moves between the two formats. Run az bicep decompile --file template.json to convert an ARM template to a Bicep file — remember decompile means JSON to Bicep. The reverse, az bicep build --file main.bicep, compiles a Bicep file to ARM JSON. Together, export plus decompile let you turn a hand-built resource group into a clean, reusable Bicep file.
Tip. Expect scenario questions that hinge on deployment mode: a phrase like "deletes resources not in the template" always points to Complete deployment mode, while Incremental (the default) leaves untouched resources alone. You must also know that Bicep transpiles to ARM JSON, that "convert ARM JSON to Bicep" means bicep decompile (and bicep build goes the other way), and that you can export a template from a resource group or its deployment history. Be ready to read an ARM template and identify parameters, variables, resources, and outputs.
- An ARM template is JSON with parameters, variables, resources, and outputs; parameters are supplied at deployment, outputs are returned after it.
- Bicep is a cleaner declarative language that transpiles to ARM JSON, infers dependencies from symbolic references, and can do anything ARM JSON can.
- Incremental mode (the default) adds and updates resources and leaves everything else untouched.
- Complete mode deletes any resource in the resource group that is not defined in the template — the classic accidental-deletion gotcha.
- Templates are idempotent: redeploying the same file is safe and only changes what differs from the current state.
- Export a template from a resource group or the deployment history to capture resources you built by hand.
- Convert ARM JSON to Bicep with bicep decompile, and Bicep to ARM JSON with bicep build.
- Deploy via the portal, az deployment group create, or New-AzResourceGroupDeployment, targeting a resource group, subscription, management group, or tenant.
Frequently asked questions
What is the difference between Incremental and Complete deployment mode?
Incremental mode, the default, adds or updates the resources defined in your template and leaves any other resources in the resource group untouched, so nothing is deleted just because it is missing from the template. Complete mode also deploys the template's resources but then deletes any resource in the resource group that is not defined in the template, making the group match the template exactly. Complete mode is the common cause of accidental deletions, so use Incremental unless you specifically need to enforce an exact set of resources.
What is the difference between an ARM template and a Bicep file?
An ARM template is a JSON document with a fixed structure of parameters, variables, resources, and outputs. A Bicep file describes the same resources with a much cleaner, declarative syntax, infers dependencies automatically from symbolic references, and offers type checking, IntelliSense, and modules. When you deploy a Bicep file, the tooling transpiles it into an ARM template, so both produce an identical deployment. Bicep is Microsoft's recommended language for new deployments, while ARM JSON remains fully supported.
How do you convert an ARM template to a Bicep file?
Run bicep decompile — for example, az bicep decompile --file template.json — to convert an existing ARM JSON template into a Bicep file. Remember the direction: decompile turns JSON into Bicep. To go the other way and compile a Bicep file back to ARM JSON, run bicep build (az bicep build --file main.bicep). The decompiled Bicep may need minor cleanup, but it gives you a working starting point for adopting Bicep on existing templates.
How do you export an existing deployment as a template?
Use the Export template option on a resource group to generate a template from the resources it currently contains, or export from the deployment history of a specific past deployment. Exporting is useful when you built resources by hand in the portal and want to capture them as reusable infrastructure as code. Exported templates often contain hard-coded values and need some cleanup before reuse, but they save you from writing the template from scratch.
Are ARM template and Bicep deployments idempotent?
Yes. Both formats are declarative, so they describe the desired end state rather than a sequence of steps. When you redeploy the same template, Azure Resource Manager compares your declared state with what already exists and only changes what is different, which means running the same deployment twice produces the same result. This idempotency is why templates are safe to run repeatedly in a deployment pipeline. Note that Complete mode still deletes resources not in the template, so idempotency does not protect against that.
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.