Infrastructure as Code (IaC) has revolutionized how we manage cloud resources. With Terraform, you can define, provision, and manage your Azure infrastructure using declarative configuration files.
Why Terraform for Azure?
While Azure provides its own IaC solution (Bicep/ARM templates), Terraform offers several advantages:
- Multi-cloud support - Use the same tool across Azure, AWS, and GCP
- Large ecosystem - Extensive provider and module registry
- State management - Track the current state of your infrastructure
- Plan before apply - Preview changes before making them
Setting Up Your Terraform Environment
Install Terraform
# Using Homebrew (macOS/Linux)
brew install terraform
# Verify installation
terraform version
Configure Azure Provider
# providers.tf
terraform {
required_version = ">= 1.6.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.80"
}
}
backend "azurerm" {
resource_group_name = "terraform-state-rg"
storage_account_name = "tfstate${var.environment}"
container_name = "tfstate"
key = "main.tfstate"
}
}
provider "azurerm" {
features {}
subscription_id = var.subscription_id
}
Building Your First Infrastructure
Let’s create a complete web application infrastructure:
# main.tf
resource "azurerm_resource_group" "main" {
name = "webapp-${var.environment}-rg"
location = var.location
tags = local.common_tags
}
resource "azurerm_virtual_network" "main" {
name = "webapp-vnet"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
address_space = ["10.0.0.0/16"]
}
resource "azurerm_subnet" "app" {
name = "app-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.1.0/24"]
}
Using Modules for Reusability
Terraform modules allow you to create reusable components:
# modules/aks/main.tf
resource "azurerm_kubernetes_cluster" "main" {
name = var.cluster_name
location = var.location
resource_group_name = var.resource_group_name
dns_prefix = var.dns_prefix
default_node_pool {
name = "default"
node_count = var.node_count
vm_size = var.vm_size
upgrade_settings {
max_surge = "10%"
}
}
identity {
type = "SystemAssigned"
}
network_profile {
network_plugin = "azure"
network_policy = "azure"
}
}
Call the module from your root configuration:
module "aks" {
source = "./modules/aks"
cluster_name = "myapp-aks-${var.environment}"
location = var.location
resource_group_name = azurerm_resource_group.main.name
dns_prefix = "myapp-${var.environment}"
node_count = var.environment == "prod" ? 5 : 2
vm_size = "Standard_DS2_v2"
}
Managing State with Remote Backend
Always use a remote backend in team environments:
# Create storage account for state
az storage account create \
--name tfstate$RANDOM \
--resource-group terraform-state-rg \
--location eastus \
--sku Standard_LRS \
--encryption-services blob
# Create container
az storage container create \
--name tfstate \
--account-name <storage-account-name>
CI/CD Pipeline Integration
Integrate Terraform with GitHub Actions:
name: 'Terraform'
on:
push:
branches: ['main']
pull_request:
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
- name: Terraform Init
run: terraform init
env:
ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
- name: Terraform Plan
run: terraform plan -out=tfplan
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve tfplan
Best Practices
- Always plan before applying - Use
terraform planto preview changes - Use workspaces - Separate environments (dev, staging, prod)
- Lock provider versions - Pin specific versions for reproducibility
- Enable state locking - Prevent concurrent modifications
- Use
terraform fmt- Keep code consistently formatted
Conclusion
Terraform with Azure provides a powerful, flexible way to manage your cloud infrastructure. By following the patterns outlined here, you can build maintainable, scalable infrastructure that scales with your team’s needs.