5.1 ARM Templates, Bicep, and Deployment Workflow
Key Takeaways
- ARM templates and Bicep describe desired Azure resource state and are deployed through Azure Resource Manager at subscription, resource group, management group, or tenant scope.
- Bicep is the preferred authoring language for most administrator scenarios because it compiles to ARM JSON while remaining easier to read and modify.
- Use what-if, validation, deployment names, parameters, outputs, and deployment history to predict and troubleshoot infrastructure changes.
- Exported templates are useful for discovery, but exam scenarios usually expect you to simplify, parameterize, and redeploy rather than blindly reuse exported JSON.
Deployment Model
Azure Resource Manager is the control plane for most Azure resources. When you create a virtual machine in the portal, run az vm create, deploy a Bicep file, or submit an ARM template, the request is evaluated by ARM. ARM checks identity, RBAC, policy, resource provider registration, location availability, dependencies, quotas, and resource properties before the target resource provider creates or updates the resource.
For AZ-104, the important point is that templates are not a separate provisioning engine. They are a repeatable way to send the same desired-state request to ARM. You should be comfortable reading a template, finding the resource type, API version, location, name, properties, dependencies, parameters, and outputs. You should also be able to choose the right deployment scope.
| Scope | Common command | Use case | Exam clue |
|---|---|---|---|
| Resource group | az deployment group create | VMs, NICs, disks, NSGs, public IPs | Most compute deployments |
| Subscription | az deployment sub create | Resource groups, policy assignments, role assignments | Template creates the resource group |
| Management group | az deployment mg create | Governance across subscriptions | Policy or initiative at hierarchy level |
| Tenant | az deployment tenant create | Tenant-level configuration | Rare administrator scenario |
Bicep Versus ARM JSON
ARM templates are JSON documents. They are powerful, but complex expressions and nested objects can be hard to maintain. Bicep is a domain-specific language that compiles to ARM JSON. It uses symbolic names, cleaner parameters, modules, loops, and conditionals. You can decompile many ARM templates with az bicep decompile, but decompilation is a starting point, not guaranteed production-quality code.
A compact Bicep deployment for a VM support resource might look like this:
param location string = resourceGroup().location
param vnetName string = 'vnet-prod-compute'
param subnetName string = 'snet-vms'
resource vnet 'Microsoft.Network/virtualNetworks@2024-05-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: [
'10.40.0.0/16'
]
}
subnets: [
{
name: subnetName
properties: {
addressPrefix: '10.40.1.0/24'
}
}
]
}
}
output subnetId string = vnet.properties.subnets[0].id
The equivalent ARM template would contain the same resource provider type and properties but with JSON syntax, parameters, variables, and expression strings such as [resourceGroup().location]. In exam questions, Bicep answers often stand out because the symbolic resource name is used for dependency tracking. Explicit dependsOn is still possible, but Bicep usually infers dependencies when one resource references another.
Deployment Workflow
A practical workflow starts with a small, validated file. Use parameters for names, locations, VM sizes, admin usernames, environment labels, and settings that vary between dev, test, and production. Avoid hard-coding passwords. Use secure parameters, Key Vault references, managed identities, or deployment-time secrets handled by your pipeline.
Typical CLI workflow:
az login
az account set --subscription 00000000-0000-0000-0000-000000000000
az group create --name rg-prod-compute --location eastus
az deployment group what-if \
--resource-group rg-prod-compute \
--template-file main.bicep \
--parameters vmName=vm-app-01 adminUsername=azureadmin
az deployment group create \
--name vm-build-20260505 \
--resource-group rg-prod-compute \
--template-file main.bicep \
--parameters @prod.parameters.json
Use what-if before changes that may replace, delete, or reconfigure resources. A deployment mode also matters. Incremental mode updates or creates resources defined in the template and leaves unrelated resources alone. Complete mode can delete resources not present in the template at the deployment scope. Complete mode is dangerous in shared resource groups and should not be selected casually in an exam scenario.
Parameters, Outputs, and Modules
A parameter file keeps environment values separate from reusable logic. Secure values should not be committed. Outputs are useful when later deployments need IDs, host names, or principal IDs. Modules let you split a design into network, VM, monitoring, and backup pieces.
param location string
param vmNames array
module vmModule './modules/windows-vm.bicep' = [for vmName in vmNames: {
name: 'deploy-${vmName}'
params: {
location: location
vmName: vmName
}
}]
Loops are common for VM Scale Sets, diagnostic settings, role assignments, and repeated resource creation. The exam may ask you to identify a syntax issue, missing parameter, bad scope, or invalid dependency. Read the error and locate the layer: template parsing, ARM validation, Azure Policy denial, RBAC denial, provider registration, quota, or resource provider runtime error.
Export and Convert
The portal can export a template from a resource group or from deployment history. Export is useful for learning the property shape of a VM, disk, NIC, or scale set. It often includes generated names, defaults, read-only properties, and values you should parameterize or remove. For study purposes, compare the exported resource type with Microsoft Learn examples and current API versions.
Command examples:
az group export --name rg-prod-compute > exported.json
az bicep decompile --file exported.json
az bicep build --file main.bicep
A common troubleshooting scenario is a deployment that works in one subscription but fails in another. Check whether the destination subscription has the resource provider registered, the VM family quota available in the selected region, the same policy assignments, the same allowed locations, and permissions at the target scope. The same template can fail because the environment is different.
Troubleshooting Tree
| Symptom | First check | Likely fix |
|---|---|---|
AuthorizationFailed | Role assignment at deployment scope | Assign Contributor, VM Contributor plus network rights, or a custom role |
InvalidTemplate | Syntax, parameter name, function, scope | Build Bicep locally and validate deployment |
RequestDisallowedByPolicy | Policy assignment and exemption | Choose allowed SKU, tag, location, or request exemption |
SkuNotAvailable | Region and VM size | Pick a supported region or size |
OperationNotAllowed quota | Compute usage and limits | Request quota increase or choose another family |
| Resource provider error | Activity log and deployment operations | Fix resource-specific properties |
In the portal, use Resource group > Deployments > deployment name > Deployment details to inspect failed operations. In CLI, use az deployment operation group list --resource-group rg-prod-compute --name vm-build-20260505. The failed child operation usually contains more useful detail than the top-level deployment status.
Scenario Recognition
If a question asks for repeatable VM deployment across environments, choose Bicep or ARM templates. If it asks for one-time manual creation, the portal or CLI may be sufficient. If it asks to preview effects, choose what-if. If it asks to create resource groups and then VMs in one file, use subscription-scope deployment with modules or nested deployments. If it asks to simplify an exported template, remove read-only properties, parameterize environment differences, and keep only required resource definitions.
On the exam, do not assume Microsoft Learn access means you can research every syntax detail. Role-based exams can provide Learn access, but no extra exam time is added and access excludes Q&A, Practice Assessments, and profile. Know the deployment workflow well enough to answer from the scenario.
You need to deploy a resource group and then deploy several VMs into it from one repeatable template workflow. Which deployment scope should start the process?
A Bicep deployment fails with RequestDisallowedByPolicy. What should you check first?
Why should an exported ARM template usually be edited before reuse?