Azure Infrastructure as Code: Using Terraform and ARM Templates
azure

Azure Infrastructure as Code: Using Terraform and ARM Templates

Master Infrastructure as Code in Azure with this comprehensive guide covering Terraform, ARM Templates, state management, module development, and pipeline integration.

March 1, 2024
cleartechy editorial team
5 min read

Azure Infrastructure as Code: A Complete Implementation Guide

Infrastructure as Code (IaC) is essential for managing cloud resources efficiently. This guide covers implementation details for both Terraform and ARM Templates in Azure.

IaC Tools Comparison

Key differences between IaC tools:

FeatureTerraformARM Templates
LanguageHCLJSON/Bicep
State ManagementExternal stateAzure native
Multi-cloudYesAzure only
Learning CurveModerateSteep

Terraform Implementation

Basic Infrastructure Setup

# Provider configuration provider "azurerm" { features {} } # Resource group resource "azurerm_resource_group" "example" { name = "example-resources" location = "East US" tags = { environment = "Production" department = "IT" } } # Virtual network resource "azurerm_virtual_network" "example" { name = "example-network" resource_group_name = azurerm_resource_group.example.name location = azurerm_resource_group.example.location address_space = ["10.0.0.0/16"] subnet { name = "subnet1" address_prefix = "10.0.1.0/24" } subnet { name = "subnet2" address_prefix = "10.0.2.0/24" } } # Storage account resource "azurerm_storage_account" "example" { name = "examplestorageacc" resource_group_name = azurerm_resource_group.example.name location = azurerm_resource_group.example.location account_tier = "Standard" account_replication_type = "LRS" tags = { environment = "Production" } }

Module Development

# modules/webapp/main.tf variable "name" { type = string description = "Name of the web app" } variable "location" { type = string description = "Azure region" } variable "resource_group_name" { type = string description = "Resource group name" } resource "azurerm_app_service_plan" "example" { name = "${var.name}-plan" location = var.location resource_group_name = var.resource_group_name sku { tier = "Standard" size = "S1" } } resource "azurerm_app_service" "example" { name = var.name location = var.location resource_group_name = var.resource_group_name app_service_plan_id = azurerm_app_service_plan.example.id site_config { dotnet_framework_version = "v4.0" scm_type = "LocalGit" } app_settings = { "WEBSITE_NODE_DEFAULT_VERSION" = "10.14.1" } } output "webapp_url" { value = azurerm_app_service.example.default_site_hostname }

ARM Templates Implementation

Resource Deployment

{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "storageAccountName": { "type": "string", "metadata": { "description": "Name of the storage account" } }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location for resources" } } }, "variables": { "storageAccountSku": "Standard_LRS" }, "resources": [ { "type": "Microsoft.Storage/storageAccounts", "apiVersion": "2021-04-01", "name": "[parameters('storageAccountName')]", "location": "[parameters('location')]", "sku": { "name": "[variables('storageAccountSku')]" }, "kind": "StorageV2", "properties": { "supportsHttpsTrafficOnly": true, "minimumTlsVersion": "TLS1_2", "allowBlobPublicAccess": false } } ], "outputs": { "storageAccountId": { "type": "string", "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" } } }

Linked Templates

{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "networkTemplate": { "type": "string", "metadata": { "description": "URI to the network template" } } }, "resources": [ { "type": "Microsoft.Resources/deployments", "apiVersion": "2021-04-01", "name": "networkDeployment", "properties": { "mode": "Incremental", "templateLink": { "uri": "[parameters('networkTemplate')]", "contentVersion": "1.0.0.0" }, "parameters": { "vnetName": { "value": "myVNet" }, "addressPrefix": { "value": "10.0.0.0/16" } } } } ] }

State Management

Terraform State Configuration

# Backend configuration terraform { backend "azurerm" { resource_group_name = "terraform-state-rg" storage_account_name = "terraformstate" container_name = "tfstate" key = "prod.terraform.tfstate" } } # State locking resource "azurerm_storage_account" "state" { name = "terraformstate" resource_group_name = "terraform-state-rg" location = "East US" account_tier = "Standard" account_replication_type = "LRS" blob_properties { versioning_enabled = true } } resource "azurerm_storage_container" "state" { name = "tfstate" storage_account_name = azurerm_storage_account.state.name container_access_type = "private" }

Pipeline Integration

Azure DevOps Pipeline

trigger: branches: include: - main paths: include: - terraform/* variables: - group: terraform-vars stages: - stage: Validate jobs: - job: ValidateAndPlan pool: vmImage: 'ubuntu-latest' steps: - task: TerraformInstaller@0 inputs: terraformVersion: '1.0.0' - task: TerraformTaskV3@3 inputs: provider: 'azurerm' command: 'init' backendServiceArm: '$(AZURE_SUBSCRIPTION)' backendAzureRmResourceGroupName: '$(BACKEND_RG)' backendAzureRmStorageAccountName: '$(BACKEND_SA)' backendAzureRmContainerName: '$(BACKEND_CONTAINER)' backendAzureRmKey: '$(BACKEND_KEY)' - task: TerraformTaskV3@3 inputs: provider: 'azurerm' command: 'plan' environmentServiceNameAzureRM: '$(AZURE_SUBSCRIPTION)' - stage: Apply dependsOn: Validate condition: succeeded() jobs: - deployment: ApplyTerraform environment: 'Production' strategy: runOnce: deploy: steps: - task: TerraformTaskV3@3 inputs: provider: 'azurerm' command: 'apply' environmentServiceNameAzureRM: '$(AZURE_SUBSCRIPTION)' commandOptions: '-auto-approve'

Testing and Validation

Infrastructure Testing

Test TypeToolPurpose
Static AnalysistflintSyntax validation
Security ScanningtfsecSecurity checks
Policy ComplianceCheckovPolicy validation
Integration TestsTerratestResource testing

Best Practices Summary

  1. Code Organization

    • Use modules
    • Implement state management
    • Version control
    • Documentation
  2. Security

    • Secure state storage
    • Use service principals
    • Implement least privilege
    • Regular security scans
  3. Deployment

    • Use pipelines
    • Implement validation
    • Stage deployments
    • Regular testing
  4. Maintenance

    • Version tracking
    • State backups
    • Regular updates
    • Monitoring

Troubleshooting Guide

Common IaC issues and solutions:

  1. State Issues

    • Check backend configuration
    • Verify access permissions
    • Review state locks
    • Backup state files
  2. Deployment Failures

    • Check dependencies
    • Verify credentials
    • Review logs
    • Test locally
  3. Module Problems

    • Check versions
    • Verify inputs
    • Review outputs
    • Test modules

Next Steps

After implementing IaC:

  1. Set up monitoring
  2. Implement compliance checks
  3. Automate testing
  4. Train team members
  5. Document processes

Remember to regularly review and update your IaC implementation to maintain optimal infrastructure management.

azure
infrastructure-as-code
terraform
arm-templates
devops