Lab: read, modify, export, and deploy ARM/Bicep

Outcome

Read a small ARM template as a declaration of desired state, make one safe change in both JSON and Bicep, inspect the planned change, deploy it, export the deployed resource, and compare the export before using it again. You will deliberately submit an invalid template and fix the actual validation error.

Before you begin

Use a non-production subscription and Cloud Shell or a machine with Azure CLI and Bicep available. Create a disposable resource group. Replace angle-bracket values; the storage name must be globally unique, lowercase, and 3–24 characters.

az group create --name rg-azlearn-iac --location <azure-region>
mkdir azlearn-iac && cd azlearn-iac

Learn: how to read the file

In an ARM JSON template, parameters are inputs, resources are the desired Azure objects, dependsOn expresses an ordering only when Azure cannot infer it, and outputs return useful values after deployment. In Bicep, the same ideas are shorter: a symbolic resource name lets Bicep infer many dependencies.

Create main.json:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": { "storageName": { "type": "string" } },
  "resources": [{
    "type": "Microsoft.Storage/storageAccounts",
    "apiVersion": "2023-05-01",
    "name": "[parameters('storageName')]",
    "location": "[resourceGroup().location]",
    "sku": { "name": "Standard_LRS" },
    "kind": "StorageV2",
    "properties": { "allowBlobPublicAccess": false }
  }],
  "outputs": { "storageId": { "type": "string", "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]" } }
}

Do

  1. Validate the JSON without creating resources, then preview and deploy it.

    az deployment group validate --resource-group rg-azlearn-iac --template-file main.json --parameters storageName=<unique-name>
    az deployment group what-if --resource-group rg-azlearn-iac --template-file main.json --parameters storageName=<unique-name>
    az deployment group create --name first-json --resource-group rg-azlearn-iac --template-file main.json --parameters storageName=<unique-name>
  2. Modify the parameter value, not the resource type: deploy a second uniquely named storage account only after what-if shows the expected addition. Explain why changing a name creates a different resource rather than renaming the existing one.

  3. Convert the original JSON to a starting-point Bicep file, then inspect it before trusting it.

    az bicep decompile --file main.json
    az bicep build --file main.bicep
    az deployment group what-if --resource-group rg-azlearn-iac --template-file main.bicep --parameters storageName=<third-unique-name>
  4. Change the Bicep resource to add the tag purpose: 'training'. Build, preview, deploy, and verify it:

    az deployment group create --name tagged-bicep --resource-group rg-azlearn-iac --template-file main.bicep --parameters storageName=<third-unique-name>
    az storage account show --name <third-unique-name> --resource-group rg-azlearn-iac --query '{id:id,tags:tags,publicBlob:allowBlobPublicAccess}' -o json
  5. In the portal, open one storage account, choose Automation > Export template, and download the result. Also export its resource group with az group export --name rg-azlearn-iac > exported.json. Diff the export against main.json.

An export is a snapshot or a historical deployment copy, not automatically production-quality source. Remove generated noise, parameterize environment-specific values, validate, and preview any changed template. Decompilation is best effort and can require manual repair.

Verify

Record the successful validate, what-if, deployment output, final storage properties, and one concrete difference between your authored file and the export. You should now understand: a template is desired state; preview reduces surprise but does not replace review; export/decompile starts work rather than finishes it.

Break and fix

  1. In a copy of main.json, change Standard_LRS to the invalid Standard_NOTREAL. Run validate; capture the error; restore a supported SKU before deploying.
  2. In a copy of main.bicep, make the storageName parameter name disagree with its use. Run az bicep build, identify the line and symbol in the diagnostic, then restore it.
  3. Do not use --force with decompile over your working Bicep file. If you need to repeat conversion, use a separate directory or first save a diff.

Scenario practice

  1. Configure: A reviewer wants the same resources in test and production. Which values belong in parameters rather than copied files? Explain using location and name.
  2. Diagnose: what-if predicts a replacement/addition you did not expect after a name edit. What identity property changed, and what must you do before deployment?
  3. Distinguish: When do you choose an exported current-state template, a deployment-history template, or an authored Bicep module? Contrast snapshot, exact historical input, and maintainable source.

Clean up

az group delete --name rg-azlearn-iac --yes --no-wait

Sources and re-audit

Re-audit when template export, Bicep conversion, or Azure CLI validation behavior changes.

ARM · ARM templates · Bicep · Compute path · All labs