AZ-104 Objective 3.1: Automate Deployment of Resources by Using ARM Templates or Bicep Files

35 min readMicrosoft Azure Administrator

AZ-104 Exam Focus: This objective covers Infrastructure as Code (IaC) using Azure Resource Manager (ARM) templates and Bicep files. Understanding template structure, deployment automation, and resource management is crucial for Azure administrators. Master these concepts for both exam success and real-world Azure infrastructure automation.

Understanding Infrastructure as Code (IaC)

Infrastructure as Code (IaC) is the practice of managing and provisioning infrastructure through machine-readable definition files, rather than through physical hardware configuration or interactive configuration tools. Azure provides ARM templates and Bicep files as declarative IaC solutions. This approach works hand-in-hand with Azure governance policies to ensure your infrastructure deployments comply with organizational standards.

IaC Benefits:

  • Consistency: Repeatable and consistent deployments
  • Version Control: Track infrastructure changes over time
  • Automation: Automated deployment and management
  • Collaboration: Team-based infrastructure development
  • Disaster Recovery: Quick infrastructure recreation
  • Compliance: Standardized and auditable infrastructure

Understanding ARM Templates and Bicep

Azure Resource Manager (ARM) Templates

ARM templates are JSON files that define the infrastructure and configuration for your Azure solution. They use declarative syntax, which means you describe what you want to deploy without writing the sequence of programming commands to create it.

ARM Template Structure

Template Components:
  • $schema: JSON schema file that describes the version of the template language
  • contentVersion: Version of the template (such as 1.0.0.0)
  • parameters: Values that are provided when deployment is executed
  • variables: Values that are used as JSON fragments in the template
  • functions: User-defined functions that are available within the template
  • resources: Resources that are deployed or updated
  • outputs: Values that are returned after deployment

Bicep Files

Bicep is a domain-specific language (DSL) that uses declarative syntax to deploy Azure resources. It provides a more concise syntax compared to ARM templates while maintaining the same capabilities.

Bicep Advantages

Key Benefits:
  • Simplified Syntax: More readable and concise than JSON
  • Type Safety: Built-in type checking and validation
  • IntelliSense: Better IDE support and autocomplete
  • Modularity: Easy to break into modules and reuse
  • Compilation: Compiles to ARM templates automatically
  • No State Management: No need to manage state files

Interpret an Azure Resource Manager Template or a Bicep File

ARM Template Example

Basic ARM Template Structure:
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "type": "string",
      "defaultValue": "mystorageaccount",
      "metadata": {
        "description": "Name of the storage account"
      }
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]",
      "metadata": {
        "description": "Location for all resources"
      }
    }
  },
  "variables": {
    "storageAccountName": "[concat(parameters('storageAccountName'), uniqueString(resourceGroup().id))]"
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2021-09-01",
      "name": "[variables('storageAccountName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "Standard_LRS"
      },
      "kind": "StorageV2",
      "properties": {
        "accessTier": "Hot"
      }
    }
  ],
  "outputs": {
    "storageAccountName": {
      "type": "string",
      "value": "[variables('storageAccountName')]"
    }
  }
}

Bicep File Example

Equivalent Bicep File:
@description('Name of the storage account')
@minLength(3)
@maxLength(24)
param storageAccountName string = 'mystorageaccount'

@description('Location for all resources')
param location string = resourceGroup().location

var uniqueStorageAccountName = '${storageAccountName}${uniqueString(resourceGroup().id)}'

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
  name: uniqueStorageAccountName
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
  }
}

output storageAccountName string = storageAccount.name

Template Interpretation

Key Elements to Understand

Template Analysis:
  • Schema and Version: Template language version and compatibility
  • Parameters: Input values that can be customized during deployment
  • Variables: Computed values used throughout the template
  • Resources: Azure resources being deployed with their properties
  • Dependencies: Resource dependencies and deployment order
  • Outputs: Values returned after successful deployment

Modify an Existing Azure Resource Manager Template

Common Template Modifications

Modifying ARM templates involves understanding the existing structure and making targeted changes to parameters, resources, or configuration.

Adding Parameters

Parameter Addition Example:
// Add new parameter to existing template
"parameters": {
  "storageAccountName": {
    "type": "string",
    "defaultValue": "mystorageaccount"
  },
  "storageAccountType": {
    "type": "string",
    "defaultValue": "Standard_LRS",
    "allowedValues": [
      "Standard_LRS",
      "Standard_GRS",
      "Standard_RAGRS",
      "Premium_LRS"
    ],
    "metadata": {
      "description": "Storage account type"
    }
  },
  "environment": {
    "type": "string",
    "defaultValue": "dev",
    "allowedValues": [
      "dev",
      "test",
      "prod"
    ],
    "metadata": {
      "description": "Environment name"
    }
  }
}

Modifying Resources

Resource Modification Example:
// Modify existing storage account resource
{
  "type": "Microsoft.Storage/storageAccounts",
  "apiVersion": "2021-09-01",
  "name": "[variables('storageAccountName')]",
  "location": "[parameters('location')]",
  "sku": {
    "name": "[parameters('storageAccountType')]"
  },
  "kind": "StorageV2",
  "properties": {
    "accessTier": "Hot",
    "allowBlobPublicAccess": false,
    "networkAcls": {
      "defaultAction": "Deny",
      "bypass": "AzureServices"
    }
  },
  "tags": {
    "Environment": "[parameters('environment')]",
    "Project": "MyProject"
  }
}

Adding New Resources

Adding Virtual Network Resource:
// Add virtual network to existing template
{
  "type": "Microsoft.Network/virtualNetworks",
  "apiVersion": "2021-05-01",
  "name": "[variables('virtualNetworkName')]",
  "location": "[parameters('location')]",
  "properties": {
    "addressSpace": {
      "addressPrefixes": [
        "[parameters('vnetAddressPrefix')]"
      ]
    },
    "subnets": [
      {
        "name": "[variables('subnetName')]",
        "properties": {
          "addressPrefix": "[parameters('subnetAddressPrefix')]"
        }
      }
    ]
  },
  "dependsOn": []
}

Modify an Existing Bicep File

Bicep File Modifications

Bicep files are easier to modify due to their simplified syntax and better readability. Common modifications include adding parameters, resources, and improving modularity.

Adding Parameters in Bicep

Parameter Addition Example:
@description('Name of the storage account')
@minLength(3)
@maxLength(24)
param storageAccountName string = 'mystorageaccount'

@description('Storage account type')
@allowed([
  'Standard_LRS'
  'Standard_GRS'
  'Standard_RAGRS'
  'Premium_LRS'
])
param storageAccountType string = 'Standard_LRS'

@description('Environment name')
@allowed([
  'dev'
  'test'
  'prod'
])
param environment string = 'dev'

@description('Location for all resources')
param location string = resourceGroup().location

Modifying Resources in Bicep

Resource Modification Example:
var uniqueStorageAccountName = '${storageAccountName}${uniqueString(resourceGroup().id)}'

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
  name: uniqueStorageAccountName
  location: location
  sku: {
    name: storageAccountType
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
    allowBlobPublicAccess: false
    networkAcls: {
      defaultAction: 'Deny'
      bypass: 'AzureServices'
    }
  }
  tags: {
    Environment: environment
    Project: 'MyProject'
  }
}

Adding Modules in Bicep

Module Usage Example:
// Create a module for virtual network
module vnetModule 'modules/vnet.bicep' = {
  name: 'vnet-${environment}'
  params: {
    vnetName: 'vnet-${environment}'
    location: location
    addressPrefix: '10.0.0.0/16'
    subnetName: 'subnet-${environment}'
    subnetAddressPrefix: '10.0.1.0/24'
  }
}

// Reference module outputs
output vnetId string = vnetModule.outputs.vnetId
output subnetId string = vnetModule.outputs.subnetId

Deploy Resources by Using an ARM Template or Bicep File

Deployment Methods

Azure provides multiple methods for deploying ARM templates and Bicep files, each with different use cases and capabilities.

Azure Portal Deployment

Portal Deployment Process:
  1. Navigate to Deploy: Go to Azure Portal → "Deploy a custom template"
  2. Select Template: Choose "Build your own template in the editor"
  3. Upload Template: Paste template JSON or upload file
  4. Configure Parameters: Set parameter values
  5. Review and Deploy: Review settings and deploy

PowerShell Deployment

PowerShell Deployment Commands:
# Deploy ARM template
New-AzResourceGroupDeployment -ResourceGroupName "MyRG" -TemplateFile "template.json" -TemplateParameterFile "parameters.json"

# Deploy with inline parameters
New-AzResourceGroupDeployment -ResourceGroupName "MyRG" -TemplateFile "template.json" -storageAccountName "mystorageaccount" -location "East US"

# Deploy with parameter object
$parameters = @{
    storageAccountName = "mystorageaccount"
    location = "East US"
    environment = "prod"
}
New-AzResourceGroupDeployment -ResourceGroupName "MyRG" -TemplateFile "template.json" -TemplateParameterObject $parameters

# Deploy Bicep file
New-AzResourceGroupDeployment -ResourceGroupName "MyRG" -TemplateFile "template.bicep" -TemplateParameterFile "parameters.json"

# Deploy with What-If preview
New-AzResourceGroupDeployment -ResourceGroupName "MyRG" -TemplateFile "template.json" -WhatIf

# Deploy with validation only
New-AzResourceGroupDeployment -ResourceGroupName "MyRG" -TemplateFile "template.json" -ValidateOnly

Azure CLI Deployment

Azure CLI Deployment Commands:
# Deploy ARM template
az deployment group create --resource-group MyRG --template-file template.json --parameters @parameters.json

# Deploy with inline parameters
az deployment group create --resource-group MyRG --template-file template.json --parameters storageAccountName=mystorageaccount location="East US"

# Deploy Bicep file
az deployment group create --resource-group MyRG --template-file template.bicep --parameters @parameters.json

# Deploy with What-If preview
az deployment group create --resource-group MyRG --template-file template.json --what-if

# Deploy with validation only
az deployment group validate --resource-group MyRG --template-file template.json --parameters @parameters.json

# Deploy to subscription level
az deployment sub create --location "East US" --template-file template.json --parameters @parameters.json

# Deploy to management group level
az deployment mg create --location "East US" --template-file template.json --parameters @parameters.json

Deployment Best Practices

✅ Deployment Recommendations:
  • Use What-If: Always preview changes before deployment
  • Validate Templates: Validate templates before deployment
  • Incremental Deployments: Use incremental deployment mode
  • Parameter Files: Use separate parameter files for different environments
  • Version Control: Store templates in version control
  • Testing: Test templates in non-production environments first
  • Monitoring: Monitor deployment status and logs

Export a Deployment as an ARM Template or Convert ARM Template to Bicep

Exporting Deployments

Azure allows you to export existing resource configurations as ARM templates, which can then be modified and redeployed or converted to Bicep files.

Azure Portal Export

Portal Export Process:
  1. Navigate to Resource Group: Go to Azure Portal → Resource groups → Select resource group
  2. Access Export Template: Click "Export template" in the left navigation
  3. Select Resources: Choose resources to include in the template
  4. Download Template: Download the generated ARM template
  5. Review Template: Review and modify the exported template

PowerShell Export

PowerShell Export Commands:
# Export resource group as ARM template
Export-AzResourceGroup -ResourceGroupName "MyRG" -Path "exported-template.json"

# Export specific resources
Export-AzResourceGroup -ResourceGroupName "MyRG" -Resource "Microsoft.Storage/storageAccounts/mystorageaccount" -Path "storage-template.json"

# Export with parameters
Export-AzResourceGroup -ResourceGroupName "MyRG" -Path "exported-template.json" -IncludeParameterDefaultValue

# Export with comments
Export-AzResourceGroup -ResourceGroupName "MyRG" -Path "exported-template.json" -IncludeComments

Azure CLI Export

Azure CLI Export Commands:
# Export resource group as ARM template
az group export --name MyRG --output-file exported-template.json

# Export with parameters
az group export --name MyRG --output-file exported-template.json --include-parameter-default-value

# Export specific resources
az resource list --resource-group MyRG --output table
az resource export --ids /subscriptions/{subscription-id}/resourceGroups/MyRG/providers/Microsoft.Storage/storageAccounts/mystorageaccount --output-file storage-template.json

Converting ARM Templates to Bicep

Bicep CLI Conversion

Conversion Commands:
# Install Bicep CLI
az bicep install

# Convert ARM template to Bicep
az bicep decompile --file template.json

# Convert with output file
az bicep decompile --file template.json --outfile template.bicep

# Validate Bicep file
az bicep build --file template.bicep

# Build Bicep to ARM template
az bicep build --file template.bicep --outfile compiled-template.json

Manual Conversion Guidelines

Conversion Best Practices:
  • Parameters: Convert JSON parameters to Bicep parameter syntax
  • Variables: Convert JSON variables to Bicep variable syntax
  • Resources: Convert resource definitions to Bicep resource syntax
  • Outputs: Convert JSON outputs to Bicep output syntax
  • Functions: Update ARM template functions to Bicep equivalents
  • Validation: Validate converted Bicep files

Advanced Template and Bicep Concepts

Template Functions

Common ARM Template Functions:
  • resourceGroup(): Returns information about the current resource group
  • subscription(): Returns information about the current subscription
  • uniqueString(): Creates a deterministic hash string
  • concat(): Combines multiple string values
  • parameters(): Returns a parameter value
  • variables(): Returns a variable value
  • reference(): Returns runtime state of a resource

Bicep Functions

Common Bicep Functions:
  • resourceGroup(): Returns resource group information
  • subscription(): Returns subscription information
  • uniqueString(): Creates deterministic hash string
  • concat(): Combines string values
  • resourceId(): Returns unique identifier of a resource
  • reference(): Returns runtime state of a resource
  • listKeys(): Returns keys for a resource

Advanced Deployment Scenarios

Scenario 1: Multi-Environment Deployment

Situation: Organization needs to deploy the same infrastructure to multiple environments (dev, test, prod) with different configurations.

Solution: Create parameter files for each environment, use Bicep modules for reusability, and implement deployment pipelines for automated deployments.

Scenario 2: Infrastructure Updates

Situation: Organization needs to update existing infrastructure without disrupting services.

Solution: Use incremental deployment mode, implement proper resource dependencies, and use What-If to preview changes before deployment.

Scenario 3: Disaster Recovery

Situation: Organization needs to quickly recreate infrastructure in a different region for disaster recovery.

Solution: Use ARM templates or Bicep files with location parameters, implement cross-region deployment capabilities, and maintain template version control.

Best Practices and Recommendations

Template Development Best Practices

✅ Development Recommendations:
  • Modularity: Break templates into reusable modules
  • Parameterization: Use parameters for customizable values
  • Validation: Add parameter validation and constraints
  • Documentation: Document template purpose and usage
  • Version Control: Use version control for template management
  • Testing: Test templates in non-production environments
  • Security: Avoid hardcoding sensitive values

Deployment Best Practices

Deployment Recommendations:
  • Incremental Mode: Use incremental deployment mode by default
  • What-If Analysis: Always preview changes before deployment
  • Validation: Validate templates before deployment
  • Rollback Plan: Have a rollback strategy for failed deployments
  • Monitoring: Monitor deployment status and resource health
  • Automation: Use CI/CD pipelines for automated deployments
  • Environment Separation: Separate environments with different parameter files

Monitoring and Troubleshooting

Deployment Monitoring

Key Monitoring Areas:
  • Deployment Status: Monitor deployment progress and completion
  • Resource Health: Check resource health after deployment
  • Template Validation: Validate templates before deployment
  • Parameter Validation: Ensure parameter values are correct
  • Dependency Resolution: Monitor resource dependency resolution
  • Error Logs: Review deployment error logs and messages

Common Issues and Solutions

⚠️ Common Problems:
  • Template Validation Errors: Check JSON syntax and resource properties
  • Parameter Validation Failures: Verify parameter values and constraints
  • Resource Conflicts: Check for naming conflicts and resource limits
  • Dependency Issues: Verify resource dependencies and order
  • Permission Errors: Ensure proper RBAC permissions for deployment
  • Quota Exceeded: Check subscription and resource group quotas
  • Network Issues: Verify network connectivity and firewall rules

Exam Preparation Tips

Key Concepts to Remember

  • Template Structure: Schema, parameters, variables, resources, outputs
  • Bicep Advantages: Simplified syntax, type safety, modularity
  • Deployment Methods: Portal, PowerShell, Azure CLI
  • Template Functions: resourceGroup(), uniqueString(), concat(), reference()
  • Deployment Modes: Incremental vs Complete deployment
  • Validation: What-If analysis and template validation
  • Export and Conversion: Exporting templates and converting to Bicep

Practice Questions

Sample Exam Questions:

  1. What is the difference between incremental and complete deployment modes?
  2. How do you deploy a Bicep file using PowerShell?
  3. What are the advantages of using Bicep over ARM templates?
  4. How do you export an existing resource group as an ARM template?
  5. What is the purpose of the What-If deployment feature?
  6. How do you convert an ARM template to a Bicep file?
  7. What are the key components of an ARM template?
  8. How do you validate a template before deployment?
  9. What is the purpose of template parameters?
  10. How do you reference one resource from another in a template?

AZ-104 Success Tip: ARM templates and Bicep files are essential for Infrastructure as Code in Azure. Focus on understanding template structure, deployment methods, and the differences between ARM templates and Bicep. Practice with PowerShell and Azure CLI commands for deployment operations, and understand how to export existing resources and convert between template formats. Pay attention to deployment modes, validation techniques, and common troubleshooting scenarios.

Related Topics

Continue your Azure administration learning journey with these related topics: