Learning state
Track this guide
Saved in this browser only. No account required.
Terraform with Azure Master Class
Engineering-Grade Reference Manual for Terraform & Microsoft Azure
A comprehensive guide to Infrastructure as Code with Terraform, focused on Azure cloud resources
Table of Contents
- Terraform Fundamentals
- Azure Provider Setup
- Core Terraform Commands
- Azure Resource Groups
- Azure Virtual Networks
- Azure Virtual Machines
- Azure Storage
- Azure Kubernetes Service (AKS)
- Azure App Service
- Azure Database Services
- State Management
- Modules & Reusability
- Variables & Outputs
- Workspaces & Environments
- Security & Best Practices
Terraform Fundamentals
๐น What is Terraform?
Infrastructure as Code (IaC) tool that allows you to define and provision infrastructure using declarative configuration files.
Key Concepts:
- Providers - Plugins for cloud platforms (Azure, AWS, GCP)
- Resources - Infrastructure components (VMs, networks, databases)
- State - Current state of infrastructure
- Modules - Reusable configuration packages
- Variables - Parameterized configurations
- Outputs - Values exposed after apply
Terraform Workflow:
Write โ Plan โ Apply โ Manage
๐น Installation
โ๏ธ Install Terraform
# macOS (Homebrew)
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
# Verify installation
terraform version
# Enable tab completion
terraform -install-autocomplete
โ๏ธ Install Azure CLI
# macOS
brew install azure-cli
# Login to Azure
az login
# Set subscription
az account set --subscription "Your Subscription Name"
# Verify
az account show
Azure Provider Setup
๐น Provider Configuration
โ๏ธ Basic Provider Setup
# main.tf
terraform {
required_version = ">= 1.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
โ๏ธ Provider with Specific Features
provider "azurerm" {
features {
resource_group {
prevent_deletion_if_contains_resources = true
}
key_vault {
purge_soft_delete_on_destroy = true
recover_soft_deleted_key_vaults = true
}
virtual_machine {
delete_os_disk_on_deletion = true
graceful_shutdown = false
skip_shutdown_and_force_delete = false
}
}
subscription_id = var.subscription_id
tenant_id = var.tenant_id
}
โ๏ธ Authentication Methods
Azure CLI (Recommended for local development):
az login
Service Principal (Recommended for CI/CD):
provider "azurerm" {
features {}
subscription_id = var.subscription_id
client_id = var.client_id
client_secret = var.client_secret
tenant_id = var.tenant_id
}
Managed Identity (For Azure VMs):
provider "azurerm" {
features {}
use_msi = true
}
Core Terraform Commands
๐น Command: terraform init
โ๏ธ What It Does
Initializes Terraform working directory, downloads providers, and sets up backend.
โ๏ธ Syntax Examples
# Basic: Initialize directory
terraform init
# Real-world: Reinitialize with upgrade
terraform init -upgrade
# Production: Initialize with backend config
terraform init -backend-config="storage_account_name=mystorageacct"
# Reconfigure backend
terraform init -reconfigure
# Migrate state
terraform init -migrate-state
โ๏ธ Notes, Tips & Common Mistakes
- Run after changing provider versions
-upgradeupdates providers to latest allowed version- Required before other Terraform commands
- Creates
.terraformdirectory and lock file
๐น Command: terraform plan
โ๏ธ What It Does
Creates execution plan showing what Terraform will do without making changes.
โ๏ธ Syntax Examples
# Basic: Show plan
terraform plan
# Real-world: Save plan to file
terraform plan -out=tfplan
# Production: Plan with variable file
terraform plan -var-file="production.tfvars"
# Plan with specific target
terraform plan -target=azurerm_virtual_machine.example
# Detailed plan output
terraform plan -json
# Destroy plan
terraform plan -destroy
โ๏ธ Notes, Tips & Common Mistakes
- Always run before apply
-outsaves plan for exact apply- Review plan carefully before applying
- Use
-targetfor selective planning (avoid in production)
๐น Command: terraform apply
โ๏ธ What It Does
Applies changes to reach desired state defined in configuration.
โ๏ธ Syntax Examples
# Basic: Apply with confirmation
terraform apply
# Real-world: Auto-approve (CI/CD)
terraform apply -auto-approve
# Production: Apply saved plan
terraform apply tfplan
# Apply with variables
terraform apply -var="environment=production"
# Apply with var file
terraform apply -var-file="production.tfvars"
# Targeted apply
terraform apply -target=azurerm_resource_group.example
โ๏ธ Notes, Tips & Common Mistakes
- Creates/updates/deletes resources
- Use
-auto-approveonly in automation - Apply saved plans for consistency
- Review plan output before confirming
๐น Command: terraform destroy
โ๏ธ What It Does
Destroys all resources managed by Terraform. Use with extreme caution!
โ๏ธ Syntax Examples
# Basic: Destroy with confirmation
terraform destroy
# Real-world: Auto-approve destroy
terraform destroy -auto-approve
# Production: Destroy specific resource
terraform destroy -target=azurerm_virtual_machine.example
# Destroy with var file
terraform destroy -var-file="production.tfvars"
โ๏ธ Notes, Tips & Common Mistakes
- DANGEROUS - permanently deletes resources
- Always verify you're in correct workspace/environment
- Use
-targetto destroy specific resources - Consider
terraform plan -destroyfirst
๐น Command: terraform fmt
โ๏ธ What It Does
Formats Terraform configuration files to canonical style.
โ๏ธ Syntax Examples
# Basic: Format current directory
terraform fmt
# Real-world: Format recursively
terraform fmt -recursive
# Production: Check formatting (CI/CD)
terraform fmt -check
# Show diff
terraform fmt -diff
๐น Command: terraform validate
โ๏ธ What It Does
Validates configuration syntax and internal consistency.
โ๏ธ Syntax Examples
# Basic: Validate configuration
terraform validate
# Real-world: JSON output for CI/CD
terraform validate -json
๐น Command: terraform show
โ๏ธ What It Does
Shows current state or saved plan in human-readable format.
โ๏ธ Syntax Examples
# Basic: Show current state
terraform show
# Real-world: Show saved plan
terraform show tfplan
# Production: JSON output
terraform show -json
๐น Command: terraform output
โ๏ธ What It Does
Displays output values from state file.
โ๏ธ Syntax Examples
# Basic: Show all outputs
terraform output
# Real-world: Show specific output
terraform output resource_group_name
# Production: JSON format
terraform output -json
# Raw output (no quotes)
terraform output -raw public_ip
Azure Resource Groups
๐น Resource Group Basics
โ๏ธ Simple Resource Group
# resource-group.tf
resource "azurerm_resource_group" "example" {
name = "rg-myapp-prod-eastus"
location = "East US"
tags = {
Environment = "Production"
ManagedBy = "Terraform"
Project = "MyApp"
}
}
โ๏ธ Resource Group with Variables
# variables.tf
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
variable "location" {
description = "Azure region"
type = string
default = "East US"
}
variable "project_name" {
description = "Project name"
type = string
}
# resource-group.tf
resource "azurerm_resource_group" "main" {
name = "rg-${var.project_name}-${var.environment}-${replace(lower(var.location), " ", "")}"
location = var.location
tags = {
Environment = var.environment
ManagedBy = "Terraform"
Project = var.project_name
}
}
โ๏ธ Naming Convention Best Practices
Resource Type - Project - Environment - Region
Examples:
rg-myapp-prod-eastus (Resource Group)
vnet-myapp-prod-eastus (Virtual Network)
vm-myapp-web-prod-eastus-01 (Virtual Machine)
st-myapp-prod-eastus (Storage Account - max 24 chars)
kv-myapp-prod-eastus (Key Vault)
Azure Virtual Networks
๐น Virtual Network Configuration
โ๏ธ Basic VNet with Subnets
# network.tf
resource "azurerm_virtual_network" "main" {
name = "vnet-${var.project_name}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
address_space = ["10.0.0.0/16"]
tags = {
Environment = var.environment
}
}
resource "azurerm_subnet" "web" {
name = "subnet-web"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.1.0/24"]
}
resource "azurerm_subnet" "app" {
name = "subnet-app"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.2.0/24"]
}
resource "azurerm_subnet" "data" {
name = "subnet-data"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.3.0/24"]
}
โ๏ธ Network Security Group
resource "azurerm_network_security_group" "web" {
name = "nsg-web-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
security_rule {
name = "AllowHTTP"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "AllowHTTPS"
priority = 110
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "AllowSSH"
priority = 120
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = var.admin_ip
destination_address_prefix = "*"
}
}
resource "azurerm_subnet_network_security_group_association" "web" {
subnet_id = azurerm_subnet.web.id
network_security_group_id = azurerm_network_security_group.web.id
}
โ๏ธ Public IP Address
resource "azurerm_public_ip" "example" {
name = "pip-${var.project_name}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
allocation_method = "Static"
sku = "Standard"
tags = {
Environment = var.environment
}
}
Azure Virtual Machines
๐น Linux Virtual Machine
โ๏ธ Complete VM Configuration
# vm.tf
resource "azurerm_network_interface" "main" {
name = "nic-${var.vm_name}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.web.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.example.id
}
}
resource "azurerm_linux_virtual_machine" "main" {
name = "vm-${var.vm_name}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
size = "Standard_B2s"
admin_username = "azureuser"
network_interface_ids = [
azurerm_network_interface.main.id,
]
admin_ssh_key {
username = "azureuser"
public_key = file("~/.ssh/id_rsa.pub")
}
os_disk {
name = "osdisk-${var.vm_name}"
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts-gen2"
version = "latest"
}
custom_data = base64encode(file("cloud-init.yaml"))
tags = {
Environment = var.environment
}
}
โ๏ธ Cloud-Init Configuration
# cloud-init.yaml
#cloud-config
package_update: true
package_upgrade: true
packages:
- docker.io
- nginx
- git
runcmd:
- systemctl start docker
- systemctl enable docker
- usermod -aG docker azureuser
- systemctl start nginx
- systemctl enable nginx
โ๏ธ VM Scale Set
resource "azurerm_linux_virtual_machine_scale_set" "main" {
name = "vmss-${var.project_name}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
sku = "Standard_B2s"
instances = 3
admin_username = "azureuser"
admin_ssh_key {
username = "azureuser"
public_key = file("~/.ssh/id_rsa.pub")
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts-gen2"
version = "latest"
}
os_disk {
storage_account_type = "Standard_LRS"
caching = "ReadWrite"
}
network_interface {
name = "nic-vmss"
primary = true
ip_configuration {
name = "internal"
primary = true
subnet_id = azurerm_subnet.web.id
load_balancer_backend_address_pool_ids = [
azurerm_lb_backend_address_pool.main.id
]
}
}
automatic_os_upgrade_policy {
disable_automatic_rollback = false
enable_automatic_os_upgrade = true
}
rolling_upgrade_policy {
max_batch_instance_percent = 20
max_unhealthy_instance_percent = 20
max_unhealthy_upgraded_instance_percent = 20
pause_time_between_batches = "PT0S"
}
}
Azure Storage
๐น Storage Account
โ๏ธ Basic Storage Account
resource "azurerm_storage_account" "main" {
name = "st${var.project_name}${var.environment}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
account_tier = "Standard"
account_replication_type = "LRS"
tags = {
Environment = var.environment
}
}
โ๏ธ Storage Account with Advanced Features
resource "azurerm_storage_account" "advanced" {
name = "st${var.project_name}${var.environment}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
account_tier = "Standard"
account_replication_type = "GRS"
account_kind = "StorageV2"
enable_https_traffic_only = true
min_tls_version = "TLS1_2"
allow_nested_items_to_be_public = false
blob_properties {
versioning_enabled = true
delete_retention_policy {
days = 30
}
container_delete_retention_policy {
days = 30
}
}
network_rules {
default_action = "Deny"
ip_rules = [var.admin_ip]
virtual_network_subnet_ids = [azurerm_subnet.app.id]
bypass = ["AzureServices"]
}
tags = {
Environment = var.environment
}
}
โ๏ธ Blob Container
resource "azurerm_storage_container" "data" {
name = "data"
storage_account_name = azurerm_storage_account.main.name
container_access_type = "private"
}
โ๏ธ File Share
resource "azurerm_storage_share" "files" {
name = "fileshare"
storage_account_name = azurerm_storage_account.main.name
quota = 50
}
Azure Kubernetes Service (AKS)
๐น AKS Cluster
โ๏ธ Production-Ready AKS Cluster
resource "azurerm_kubernetes_cluster" "main" {
name = "aks-${var.project_name}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
dns_prefix = "aks-${var.project_name}"
kubernetes_version = "1.27.3"
default_node_pool {
name = "system"
node_count = 3
vm_size = "Standard_D2s_v3"
type = "VirtualMachineScaleSets"
availability_zones = ["1", "2", "3"]
enable_auto_scaling = true
min_count = 3
max_count = 10
vnet_subnet_id = azurerm_subnet.aks.id
upgrade_settings {
max_surge = "33%"
}
}
identity {
type = "SystemAssigned"
}
network_profile {
network_plugin = "azure"
network_policy = "azure"
load_balancer_sku = "standard"
service_cidr = "10.1.0.0/16"
dns_service_ip = "10.1.0.10"
}
azure_active_directory_role_based_access_control {
managed = true
azure_rbac_enabled = true
admin_group_object_ids = [var.admin_group_id]
}
oms_agent {
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
}
tags = {
Environment = var.environment
}
}
# Additional Node Pool
resource "azurerm_kubernetes_cluster_node_pool" "user" {
name = "user"
kubernetes_cluster_id = azurerm_kubernetes_cluster.main.id
vm_size = "Standard_D4s_v3"
node_count = 3
enable_auto_scaling = true
min_count = 3
max_count = 20
vnet_subnet_id = azurerm_subnet.aks.id
node_labels = {
"workload" = "applications"
}
tags = {
Environment = var.environment
}
}
โ๏ธ Container Registry
resource "azurerm_container_registry" "main" {
name = "acr${var.project_name}${var.environment}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
sku = "Premium"
admin_enabled = false
georeplications {
location = "West US"
tags = {}
}
network_rule_set {
default_action = "Deny"
ip_rule {
action = "Allow"
ip_range = var.admin_ip
}
}
}
# Attach ACR to AKS
resource "azurerm_role_assignment" "aks_acr" {
principal_id = azurerm_kubernetes_cluster.main.kubelet_identity[0].object_id
role_definition_name = "AcrPull"
scope = azurerm_container_registry.main.id
skip_service_principal_aad_check = true
}
Azure App Service
๐น App Service Plan & Web App
โ๏ธ Linux App Service
resource "azurerm_service_plan" "main" {
name = "asp-${var.project_name}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
os_type = "Linux"
sku_name = "P1v3"
}
resource "azurerm_linux_web_app" "main" {
name = "app-${var.project_name}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
service_plan_id = azurerm_service_plan.main.id
site_config {
always_on = true
application_stack {
node_version = "18-lts"
}
health_check_path = "/health"
}
app_settings = {
"WEBSITE_NODE_DEFAULT_VERSION" = "18-lts"
"NODE_ENV" = var.environment
"DATABASE_URL" = "@Microsoft.KeyVault(SecretUri=${azurerm_key_vault_secret.db_url.id})"
}
identity {
type = "SystemAssigned"
}
logs {
application_logs {
file_system_level = "Information"
}
http_logs {
file_system {
retention_in_days = 7
retention_in_mb = 35
}
}
}
tags = {
Environment = var.environment
}
}
โ๏ธ Container-Based App Service
resource "azurerm_linux_web_app" "container" {
name = "app-${var.project_name}-container-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
service_plan_id = azurerm_service_plan.main.id
site_config {
always_on = true
application_stack {
docker_image = "${azurerm_container_registry.main.login_server}/myapp"
docker_image_tag = "latest"
}
}
app_settings = {
"DOCKER_REGISTRY_SERVER_URL" = "https://${azurerm_container_registry.main.login_server}"
"DOCKER_REGISTRY_SERVER_USERNAME" = azurerm_container_registry.main.admin_username
"DOCKER_REGISTRY_SERVER_PASSWORD" = azurerm_container_registry.main.admin_password
}
identity {
type = "SystemAssigned"
}
}
Azure Database Services
๐น Azure SQL Database
โ๏ธ SQL Server & Database
resource "azurerm_mssql_server" "main" {
name = "sql-${var.project_name}-${var.environment}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
version = "12.0"
administrator_login = var.sql_admin_username
administrator_login_password = var.sql_admin_password
azuread_administrator {
login_username = var.aad_admin_username
object_id = var.aad_admin_object_id
}
tags = {
Environment = var.environment
}
}
resource "azurerm_mssql_database" "main" {
name = "sqldb-${var.project_name}-${var.environment}"
server_id = azurerm_mssql_server.main.id
collation = "SQL_Latin1_General_CP1_CI_AS"
license_type = "LicenseIncluded"
sku_name = "S1"
zone_redundant = false
short_term_retention_policy {
retention_days = 7
}
long_term_retention_policy {
weekly_retention = "P1W"
monthly_retention = "P1M"
yearly_retention = "P1Y"
week_of_year = 1
}
tags = {
Environment = var.environment
}
}
resource "azurerm_mssql_firewall_rule" "allow_azure" {
name = "AllowAzureServices"
server_id = azurerm_mssql_server.main.id
start_ip_address = "0.0.0.0"
end_ip_address = "0.0.0.0"
}
โ๏ธ PostgreSQL Flexible Server
resource "azurerm_postgresql_flexible_server" "main" {
name = "psql-${var.project_name}-${var.environment}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
version = "15"
administrator_login = var.db_admin_username
administrator_password = var.db_admin_password
storage_mb = 32768
sku_name = "GP_Standard_D2s_v3"
zone = "1"
backup_retention_days = 7
geo_redundant_backup_enabled = false
high_availability {
mode = "ZoneRedundant"
standby_availability_zone = "2"
}
tags = {
Environment = var.environment
}
}
resource "azurerm_postgresql_flexible_server_database" "main" {
name = "myapp"
server_id = azurerm_postgresql_flexible_server.main.id
collation = "en_US.utf8"
charset = "utf8"
}
โ๏ธ Cosmos DB
resource "azurerm_cosmosdb_account" "main" {
name = "cosmos-${var.project_name}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
offer_type = "Standard"
kind = "GlobalDocumentDB"
consistency_policy {
consistency_level = "Session"
max_interval_in_seconds = 5
max_staleness_prefix = 100
}
geo_location {
location = azurerm_resource_group.main.location
failover_priority = 0
}
geo_location {
location = "West US"
failover_priority = 1
}
backup {
type = "Periodic"
interval_in_minutes = 240
retention_in_hours = 8
}
tags = {
Environment = var.environment
}
}
resource "azurerm_cosmosdb_sql_database" "main" {
name = "myapp-db"
resource_group_name = azurerm_resource_group.main.name
account_name = azurerm_cosmosdb_account.main.name
throughput = 400
}
State Management
๐น Remote State with Azure Storage
โ๏ธ Backend Configuration
# backend.tf
terraform {
backend "azurerm" {
resource_group_name = "rg-terraform-state"
storage_account_name = "sttfstate12345"
container_name = "tfstate"
key = "production.terraform.tfstate"
}
}
โ๏ธ Create State Storage (One-time setup)
# Create resource group
az group create --name rg-terraform-state --location eastus
# Create storage account
az storage account create \
--name sttfstate12345 \
--resource-group rg-terraform-state \
--location eastus \
--sku Standard_LRS \
--encryption-services blob
# Create container
az storage container create \
--name tfstate \
--account-name sttfstate12345
โ๏ธ State Locking
Azure Storage automatically provides state locking using blob leases.
โ๏ธ State Commands
# List state resources
terraform state list
# Show specific resource
terraform state show azurerm_resource_group.main
# Move resource in state
terraform state mv azurerm_resource_group.old azurerm_resource_group.new
# Remove resource from state (doesn't delete resource)
terraform state rm azurerm_resource_group.example
# Pull remote state
terraform state pull
# Push local state
terraform state push
Modules & Reusability
๐น Creating Modules
โ๏ธ Module Structure
modules/
โโโ azure-vm/
โโโ main.tf
โโโ variables.tf
โโโ outputs.tf
โโโ README.md
โ๏ธ Module Example: Azure VM
# modules/azure-vm/variables.tf
variable "resource_group_name" {
description = "Resource group name"
type = string
}
variable "location" {
description = "Azure region"
type = string
}
variable "vm_name" {
description = "Virtual machine name"
type = string
}
variable "vm_size" {
description = "VM size"
type = string
default = "Standard_B2s"
}
variable "subnet_id" {
description = "Subnet ID"
type = string
}
variable "admin_username" {
description = "Admin username"
type = string
default = "azureuser"
}
variable "ssh_public_key" {
description = "SSH public key"
type = string
}
# modules/azure-vm/main.tf
resource "azurerm_network_interface" "main" {
name = "nic-${var.vm_name}"
location = var.location
resource_group_name = var.resource_group_name
ip_configuration {
name = "internal"
subnet_id = var.subnet_id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_linux_virtual_machine" "main" {
name = var.vm_name
location = var.location
resource_group_name = var.resource_group_name
size = var.vm_size
admin_username = var.admin_username
network_interface_ids = [
azurerm_network_interface.main.id,
]
admin_ssh_key {
username = var.admin_username
public_key = var.ssh_public_key
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts-gen2"
version = "latest"
}
}
# modules/azure-vm/outputs.tf
output "vm_id" {
description = "Virtual machine ID"
value = azurerm_linux_virtual_machine.main.id
}
output "private_ip" {
description = "Private IP address"
value = azurerm_network_interface.main.private_ip_address
}
โ๏ธ Using Modules
# main.tf
module "web_vm" {
source = "./modules/azure-vm"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
vm_name = "vm-web-prod-01"
vm_size = "Standard_D2s_v3"
subnet_id = azurerm_subnet.web.id
ssh_public_key = file("~/.ssh/id_rsa.pub")
}
module "app_vm" {
source = "./modules/azure-vm"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
vm_name = "vm-app-prod-01"
vm_size = "Standard_D4s_v3"
subnet_id = azurerm_subnet.app.id
ssh_public_key = file("~/.ssh/id_rsa.pub")
}
# Access module outputs
output "web_vm_ip" {
value = module.web_vm.private_ip
}
Variables & Outputs
๐น Variable Types
โ๏ธ Variable Definitions
# variables.tf
# String
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
# Number
variable "instance_count" {
description = "Number of instances"
type = number
default = 2
}
# Bool
variable "enable_monitoring" {
description = "Enable monitoring"
type = bool
default = true
}
# List
variable "allowed_ips" {
description = "Allowed IP addresses"
type = list(string)
default = ["10.0.0.0/8"]
}
# Map
variable "tags" {
description = "Resource tags"
type = map(string)
default = {
ManagedBy = "Terraform"
Project = "MyApp"
}
}
# Object
variable "vm_config" {
description = "VM configuration"
type = object({
size = string
os_disk = string
data_disks = list(number)
})
default = {
size = "Standard_B2s"
os_disk = "Premium_LRS"
data_disks = [128, 256]
}
}
# Sensitive
variable "db_password" {
description = "Database password"
type = string
sensitive = true
}
โ๏ธ Variable Files
# terraform.tfvars (auto-loaded)
environment = "production"
instance_count = 5
enable_monitoring = true
allowed_ips = ["203.0.113.0/24", "198.51.100.0/24"]
tags = {
Environment = "Production"
ManagedBy = "Terraform"
CostCenter = "Engineering"
}
# production.tfvars (load with -var-file)
environment = "production"
location = "East US"
vm_size = "Standard_D4s_v3"
โ๏ธ Output Values
# outputs.tf
output "resource_group_name" {
description = "Resource group name"
value = azurerm_resource_group.main.name
}
output "public_ip_address" {
description = "Public IP address"
value = azurerm_public_ip.example.ip_address
}
output "connection_string" {
description = "Database connection string"
value = azurerm_postgresql_flexible_server.main.fqdn
sensitive = true
}
output "vm_ids" {
description = "Virtual machine IDs"
value = {
web = module.web_vm.vm_id
app = module.app_vm.vm_id
}
}
Workspaces & Environments
๐น Terraform Workspaces
โ๏ธ Workspace Commands
# List workspaces
terraform workspace list
# Create new workspace
terraform workspace new production
# Switch workspace
terraform workspace select production
# Show current workspace
terraform workspace show
# Delete workspace
terraform workspace delete staging
โ๏ธ Using Workspaces in Configuration
locals {
environment = terraform.workspace
vm_sizes = {
dev = "Standard_B2s"
staging = "Standard_D2s_v3"
prod = "Standard_D4s_v3"
}
vm_size = local.vm_sizes[local.environment]
}
resource "azurerm_resource_group" "main" {
name = "rg-myapp-${local.environment}"
location = var.location
tags = {
Environment = local.environment
}
}
Security & Best Practices
โ Security Best Practices
โ๏ธ Use Azure Key Vault for Secrets
resource "azurerm_key_vault" "main" {
name = "kv-${var.project_name}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "premium"
purge_protection_enabled = true
soft_delete_retention_days = 90
network_acls {
default_action = "Deny"
bypass = "AzureServices"
ip_rules = [var.admin_ip]
}
}
resource "azurerm_key_vault_secret" "db_password" {
name = "db-password"
value = var.db_password
key_vault_id = azurerm_key_vault.main.id
}
# Grant App Service access
resource "azurerm_key_vault_access_policy" "app" {
key_vault_id = azurerm_key_vault.main.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = azurerm_linux_web_app.main.identity[0].principal_id
secret_permissions = [
"Get",
"List"
]
}
โ๏ธ Use Managed Identities
resource "azurerm_linux_web_app" "main" {
# ... other config ...
identity {
type = "SystemAssigned"
}
}
# Grant permissions
resource "azurerm_role_assignment" "app_storage" {
scope = azurerm_storage_account.main.id
role_definition_name = "Storage Blob Data Contributor"
principal_id = azurerm_linux_web_app.main.identity[0].principal_id
}
โ๏ธ Enable Diagnostic Logging
resource "azurerm_log_analytics_workspace" "main" {
name = "log-${var.project_name}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
sku = "PerGB2018"
retention_in_days = 30
}
resource "azurerm_monitor_diagnostic_setting" "vm" {
name = "diag-vm"
target_resource_id = azurerm_linux_virtual_machine.main.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
metric {
category = "AllMetrics"
enabled = true
}
}
โ Terraform Best Practices
1. Use Remote State
terraform {
backend "azurerm" {
resource_group_name = "rg-terraform-state"
storage_account_name = "sttfstate"
container_name = "tfstate"
key = "prod.tfstate"
}
}
2. Use Variables for Everything
# Don't hardcode values
resource "azurerm_resource_group" "bad" {
name = "rg-myapp-prod" # Bad
location = "East US" # Bad
}
# Use variables
resource "azurerm_resource_group" "good" {
name = "rg-${var.project_name}-${var.environment}"
location = var.location
}
3. Use Data Sources
data "azurerm_client_config" "current" {}
data "azurerm_resource_group" "existing" {
name = "rg-existing"
}
4. Use Locals for Computed Values
locals {
common_tags = {
Environment = var.environment
ManagedBy = "Terraform"
Project = var.project_name
}
resource_prefix = "${var.project_name}-${var.environment}"
}
5. Use Lifecycle Rules
resource "azurerm_resource_group" "main" {
name = "rg-${var.project_name}"
location = var.location
lifecycle {
prevent_destroy = true
ignore_changes = [tags]
}
}
6. Use Depends_on Sparingly
# Terraform infers dependencies automatically
# Only use depends_on for hidden dependencies
resource "azurerm_role_assignment" "example" {
# ... config ...
depends_on = [azurerm_key_vault_access_policy.app]
}
7. Organize Files Logically
terraform/
โโโ main.tf # Main resources
โโโ variables.tf # Variable definitions
โโโ outputs.tf # Output definitions
โโโ backend.tf # Backend configuration
โโโ providers.tf # Provider configuration
โโโ network.tf # Network resources
โโโ compute.tf # Compute resources
โโโ database.tf # Database resources
โโโ terraform.tfvars # Variable values
โโโ modules/
โโโ vm/
โโโ aks/
โโโ storage/
Quick Reference Card
Essential Commands
# Initialize
terraform init
terraform init -upgrade
# Plan
terraform plan
terraform plan -out=tfplan
terraform plan -var-file="prod.tfvars"
# Apply
terraform apply
terraform apply tfplan
terraform apply -auto-approve
# Destroy
terraform destroy
terraform destroy -target=azurerm_resource_group.example
# Format & Validate
terraform fmt -recursive
terraform validate
# State
terraform state list
terraform state show azurerm_resource_group.main
terraform state mv
terraform state rm
# Workspaces
terraform workspace list
terraform workspace new prod
terraform workspace select prod
# Output
terraform output
terraform output -json
Common Azure Resources
# Resource Group
resource "azurerm_resource_group" "example" {
name = "rg-example"
location = "East US"
}
# Virtual Network
resource "azurerm_virtual_network" "example" {
name = "vnet-example"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
# Subnet
resource "azurerm_subnet" "example" {
name = "subnet-example"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = ["10.0.1.0/24"]
}
# Storage Account
resource "azurerm_storage_account" "example" {
name = "stexample"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}
# AKS Cluster
resource "azurerm_kubernetes_cluster" "example" {
name = "aks-example"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
dns_prefix = "aks-example"
default_node_pool {
name = "default"
node_count = 3
vm_size = "Standard_D2s_v3"
}
identity {
type = "SystemAssigned"
}
}
๐ Terraform with Azure Master Class Complete
This comprehensive guide covers everything from Terraform basics to advanced Azure infrastructure patterns. Use this as your reference for building production-ready infrastructure as code on Microsoft Azure.