Bicep and ARM for AZ-104: A Practical Deployment Lab
An administrator receives a template for an Azure application environment and must change a storage configuration without disturbing the virtual network, role assignments or the application’s running resources. Editing a single property sounds simple until a deployment changes an unexpected dependency or is blocked by policy. Infrastructure as code is useful here only when the administrator understands what the template will do.
The AZ-104 exam expects you to interpret, modify and deploy Azure Resource Manager (ARM) templates and Bicep files—not merely recognize their names. A disciplined laboratory exercises the same sequence a production change should follow: inspect the resource model, parameterize it, preview the proposed changes, deploy at the correct scope, validate the result and plan how to undo an unwanted configuration.
A template describes resources and their properties, with relationships expressed through identifiers and dependencies. Azure Resource Manager compares that declaration with the existing environment and coordinates the required operations. Reusing a template should make common deployments more consistent than repeating an undocumented set of portal clicks, but a template can still request an undesirable or destructive configuration. Declarative is not a synonym for harmless.
The operational question is always: “What existing resources does this deployment reference, what properties will change and what else depends on them?” Consider a template that provisions a storage account, an App Service plan and diagnostic settings. Changing the storage account name may cause the platform to create a new account because that identity is part of the resource declaration. An application still pointing at the old endpoint does not automatically migrate its data. Treat names, scopes and immutable properties as change-control inputs.
Begin by inspecting the file’s resource types, API versions, parameters, expressions, dependencies and outputs. You should be able to describe the intended infrastructure before you press Deploy.
ARM templates use JSON to declare Azure resources and properties. Bicep provides a more concise syntax with resource symbols, parameters, modules and type-aware editing. Bicep compiles to an ARM template for deployment through Azure Resource Manager, so both approaches ultimately work with the same resource providers and authorization boundaries.
This example is a small illustrative Bicep fragment for a test account. It does not attempt to model a complete compliant enterprise deployment or select a storage SKU for every region. The important concepts are the deployment scope, resource type and API version, parameters, symbolic resource name and explicit output.
param location string = resourceGroup().location
param storageName string
resource store 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: storageName
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
properties: {
allowBlobPublicAccess: false
}
}
output storageResourceId string = store.id
Storage account names have length and character constraints and must be globally unique, so choose a valid disposable name before deploying the example. In a real workload, additional security, network, diagnostic and retention settings would be governed by organizational policy. The output is an Azure resource identifier that another deployment or script can consume, not a secret.
A reusable template should not hardcode every region, resource name and SKU. Define parameters for settings that legitimately change among environments, while treating secret values and security-sensitive choices separately. A developer should not be able to turn off organizational controls simply by passing a convenient parameter. If a property must always meet a standard, enforce the requirement through the template and the appropriate Azure Policy assignment.
For a small AZ-104 lab, use a parameters file or command-line parameter values to choose a test location and a unique account name. Avoid embedding storage keys, passwords or access tokens in parameter files committed to source control. Where a deployment genuinely requires a secret, use a supported secure workflow and verify how that value is supplied and logged.
Review variables and expressions that construct resource names or references. A wrong resource ID can target the wrong existing service. An incorrect parameter scope can make a perfectly valid template fail with a confusing not-found or authorization error. Maintain a clear distinction between configuration defaults, tenant-specific values and immutable resource identity.
If an App Service configuration references the output of a provisioned storage account, Resource Manager can infer that the dependent resource must wait until the referenced resource is available. Bicep resource symbols make such relationships easier to see. Unnecessary explicit dependencies can slow deployments and conceal the true dependency chain; missing references can produce failures when one resource needs information that has not yet been created.
A module is a useful boundary for a reusable group of resources, such as networking or monitoring. It does not remove the need to consider scopes and access. A platform team might own the network module while an application team owns the web app module; both deployments still need appropriately scoped identities. If one module deploys into a different resource group or subscription, verify that the caller has authorization there and that the deployment declaration sets the intended scope.
The existing ARM template guide provides broader template mechanics. For AZ-104, practice reading the dependency graph and predicting what an in-place change will touch rather than memorizing nested-template vocabulary.
Before changing a shared environment, use the ARM what-if operation to preview the create, modify and delete actions implied by a template. For a test resource group, the Azure CLI invocation might look like this after authenticating the deployment identity:
az deployment group what-if \
--resource-group rg-az104-lab \
--template-file main.bicep \
--parameters storageName=az104sampleunique123
What-if output is a proposal based on the current model and available resource provider information. It may report noisy differences for provider-managed properties or properties that are not fully comparable. It is also subject to changes in state between preview and deployment. Ask why a resource is shown as modified and inspect the property-level change, especially networking, access, encryption and deletion-related fields. Do not assume that an empty-looking preview proves that every application dependency is safe.
Keep a copy of the reviewed template, parameter values and preview decision. That record is more useful than a screenshot of an eventual deployment succeeded message because it explains what the administrator expected to happen.
A resource-group deployment and a subscription- or management-group deployment have different scope rules. A team might be allowed to deploy an App Service into its own resource group but not create policy assignments at the subscription level. Check the template’s declared target and the command you run, then confirm the authenticated account and subscription before executing.
For a resource-group deployment after the preview is approved, an Azure CLI command might be:
az deployment group create \
--resource-group rg-az104-lab \
--template-file main.bicep \
--parameters storageName=az104sampleunique123
The outcome should be evaluated beyond Resource Manager reporting success. Check that the account exists in the intended resource group and region, that public blob access has the intended setting and that a test client can perform only the authorized operations. If a deployment touches an existing application, verify health and user-facing behavior rather than assuming its dependent service settings remain usable.
A deployment may need subscription resource-provider registration, quota availability or approval for a particular SKU. Those constraints belong in the preflight check; they are not reasons to grant Owner blindly after a failure.
Using infrastructure as code does not create a privileged tunnel around Azure governance. An identity may have permission to submit the deployment yet lack permission for one of the individual resource actions. A deny policy can reject a non-compliant storage configuration, while a ReadOnly lock on an existing resource can block a management update. The deployment failure should be traced to the specific operation and rule rather than fixed by broadening permissions across the subscription.
Imagine a team changing a public IP allocation in a network template. The caller is Contributor on the resource group, but an inherited policy prohibits a configuration. A useful troubleshooting note identifies the policy assignment, the invalid value, and the compliant alternative. Removing the policy to get a green deployment would be a poor outcome unless the governance owner deliberately approves an appropriate exception.
This is the practical link between templates and governance: automation makes deployments repeatable, while roles, policy and locks establish the limits of those deployments. Neither replaces the other.
ARM deployments can leave a mixture of completed and failed operations. If an update fails halfway through, do not assume the environment automatically reverts to the prior complete state. Inspect the deployment operation list and current properties of affected resources. The corrective action may be a targeted repair, a redeployment of a known good configuration, a resource restore or an application-level rollback, depending on the change.
For instance, a storage configuration update might succeed while a new diagnostic setting fails for lack of permissions. A second deployment that changes only diagnostics may be sufficient; restoring the entire application stack could cause unnecessary disruption. Conversely, a deployment that changed endpoint names may require updates to application configuration and a verified data-migration or recovery process. Validate the actual resource state, not only the deployment status.
Keep a versioned template and parameters in source control, have a reviewed rollback plan, and avoid deleting the old environment before the replacement is tested. A template can recreate infrastructure, but it is not automatically a backup of data stored inside it.
Start with a disposable resource group and deploy a small Bicep template. Record the resulting resource IDs, location, tags and any policy assignments. Change one safe property, review what-if, deploy it and verify the actual state. Next try a change that conflicts with an intentionally scoped lab policy. Capture the effective assignment and explain why the caller’s Azure role did not overcome the policy.
Repeat with a temporary resource lock to demonstrate a different failure mechanism. Make sure the lock is removed before cleaning up. If practical, use a second test identity with narrower permissions and record which deployment operations it can complete. Avoid using production secrets or subscription-wide security changes merely to simulate an exam question.
The final lab report should answer four questions: What resources did the declaration target? What changes did what-if predict? Which deployment operation actually failed or succeeded? What evidence would justify promoting the same change to production? The ability to answer those questions is a stronger AZ-104 skill than writing a template from memory.