Skip to main content

Azure Recipes

Production-ready examples combining multiple terraform-az-modules modules. Each recipe is self-contained — copy, adjust the locals, and run.


Recipe 1: AKS + ACR + Key Vault

The most common production stack: a private AKS cluster with a dedicated container registry and secrets managed in Key Vault.

What gets created:

  • Resource Group
  • Virtual Network
  • Key Vault (RBAC-enabled, public access disabled)
  • Azure Container Registry (Premium SKU, encrypted via Key Vault)
  • AKS cluster (private, Azure CNI, connected to ACR)
# versions.tf
terraform {
required_version = ">= 1.6.6"

required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.90.0"
}
}
}

provider "azurerm" {
features {}
}
# main.tf
locals {
name = "myapp"
environment = "prod"
location = "eastus"
}

# ── Resource Group ────────────────────────────────────────────
module "resource_group" {
source = "git::https://github.com/terraform-az-modules/terraform-azurerm-resource-group.git?ref=v1.0.0"

name = local.name
environment = local.environment
location = local.location
}

# ── Virtual Network ───────────────────────────────────────────
module "vnet" {
source = "git::https://github.com/terraform-az-modules/terraform-azurerm-vnet.git?ref=v1.0.0"

name = local.name
environment = local.environment
location = local.location
resource_group_name = module.resource_group.resource_group_name
address_spaces = ["10.0.0.0/16"]
}

# ── Key Vault ─────────────────────────────────────────────────
module "key_vault" {
source = "git::https://github.com/terraform-az-modules/terraform-azurerm-key-vault.git?ref=v1.0.0"

name = local.name
environment = local.environment
location = local.location
resource_group_name = module.resource_group.resource_group_name
sku_name = "standard"
purge_protection_enabled = true
soft_delete_retention_days = 90
enable_rbac_authorization = true
public_network_access_enabled = false
}

# ── Container Registry ────────────────────────────────────────
module "acr" {
source = "git::https://github.com/terraform-az-modules/terraform-azurerm-acr.git?ref=v1.0.0"

name = local.name
environment = local.environment
location = local.location
resource_group_name = module.resource_group.resource_group_name

container_registry_config = {
sku = "Premium"
quarantine_policy_enabled = true
zone_redundancy_enabled = true
}

encryption = true
key_vault_id = module.key_vault.id
}

# ── AKS Cluster ───────────────────────────────────────────────
module "aks" {
source = "git::https://github.com/terraform-az-modules/terraform-azurerm-aks.git?ref=v1.0.0"

name = local.name
environment = local.environment
location = local.location
resource_group_name = module.resource_group.resource_group_name

kubernetes_version = "1.29"
agents_size = "Standard_D2s_v3"
os_disk_size_gb = 50
private_cluster_enabled = true

network_plugin = "azure"
network_policy = "azure"
service_cidr = "10.1.0.0/16"

nodes_subnet_id = module.vnet.vnet_id # replace with a dedicated subnet output

# Grant AKS permission to pull from ACR
acr_enabled = true
acr_id = module.acr.container_registry_id

# Mount Key Vault secrets as Kubernetes secrets
key_vault_secrets_provider_enabled = true
key_vault_id = module.key_vault.id
}
# outputs.tf
output "aks_name" {
value = module.aks.name
}

output "acr_login_server" {
value = module.acr.container_registry_login_server
}

output "key_vault_uri" {
value = module.key_vault.vault_uri
}

output "kube_config" {
value = module.aks.kube_config_raw
sensitive = true
}

Connect kubectl after apply:

terraform apply
terraform output -raw kube_config > ~/.kube/config
kubectl get nodes

Recipe 2: VNet + NSG (network baseline)

A reusable network stack — VNet with an NSG allowing only HTTPS inbound.

# main.tf
locals {
name = "core"
environment = "prod"
location = "westeurope"
}

module "resource_group" {
source = "git::https://github.com/terraform-az-modules/terraform-azurerm-resource-group.git?ref=v1.0.0"

name = local.name
environment = local.environment
location = local.location
}

module "vnet" {
source = "git::https://github.com/terraform-az-modules/terraform-azurerm-vnet.git?ref=v1.0.0"

name = local.name
environment = local.environment
location = local.location
resource_group_name = module.resource_group.resource_group_name
address_spaces = ["10.10.0.0/16"]
}

module "nsg" {
source = "git::https://github.com/terraform-az-modules/terraform-azurerm-nsg.git?ref=v1.0.0"

name = local.name
environment = local.environment
location = local.location
resource_group_name = module.resource_group.resource_group_name

subnet_association = true
subnet_ids = [] # add your subnet IDs

inbound_rules = [
{
name = "allow-https"
priority = 100
access = "Allow"
protocol = "Tcp"
source_address_prefix = "*"
source_port_range = "*"
destination_address_prefix = "*"
destination_port_range = "443"
},
{
name = "allow-http"
priority = 110
access = "Allow"
protocol = "Tcp"
source_address_prefix = "*"
source_port_range = "*"
destination_address_prefix = "*"
destination_port_range = "80"
},
{
name = "deny-all"
priority = 4096
access = "Deny"
protocol = "*"
source_address_prefix = "*"
source_port_range = "*"
destination_address_prefix = "*"
destination_port_range = "*"
},
]
}
# outputs.tf
output "vnet_id" {
value = module.vnet.vnet_id
}

output "vnet_address_space" {
value = module.vnet.vnet_address_space
}

Variable reference for key modules

resource-group

VariableDefaultDescription
nameShort name, e.g. app
environment""prod, dev, staging
locationnullAzure region
enabledtrueToggle resource creation
resource_lock_enabledfalseEnable CanNotDelete lock
lock_level"CanNotDelete"Lock type

Outputs: resource_group_id, resource_group_name, resource_group_location


vnet

VariableDefaultDescription
resource_group_nameTarget resource group
address_spaces[]CIDR blocks, e.g. ["10.0.0.0/16"]
dns_servers[]Custom DNS (empty = Azure DNS)
enable_ddos_ppfalseCreate a DDoS Protection Plan
enable_network_watcherfalseDeploy Network Watcher

Outputs: vnet_id, vnet_name, vnet_address_space


aks

VariableDefaultDescription
kubernetes_version"1.27.7"Kubernetes version to deploy
agents_size"Standard_D2s_v3"Default node pool VM size
agents_pool_name"nodepool"Default node pool name
os_disk_size_gb50Node OS disk size in GB
private_cluster_enabledtrueMake the API server private
network_plugin"azure"azure or kubenet
network_policy"azure"azure or calico
acr_enabledfalseGrant AKS pull access to an ACR
acr_idnullACR resource ID (when acr_enabled = true)
key_vault_secrets_provider_enabledfalseEnable Key Vault CSI driver
enable_auto_scalingfalseEnable cluster autoscaler
agents_min_countnullMinimum nodes (autoscaler)
agents_max_countnullMaximum nodes (autoscaler)

Outputs: name, id, kube_config_raw, node_resource_group, aks_system_identity_principal_id


key-vault

VariableDefaultDescription
sku_name"standard"standard or premium
purge_protection_enabledtruePrevent permanent deletion
soft_delete_retention_days90Soft-delete window (7–90 days)
enable_rbac_authorizationtrueUse RBAC instead of access policies
public_network_access_enabledfalseBlock public access
enable_private_endpointtrueDeploy a private endpoint
secrets[]List of secrets to create in the vault

Outputs: id, vault_uri


acr

VariableDefaultDescription
container_registry_config.sku"Premium"Basic, Standard, or Premium
admin_enabledfalseEnable admin username/password
public_network_access_enabledfalseBlock public access
encryptiontrueEncrypt registry with a Key Vault key
key_vault_idnullKey Vault ID (when encryption = true)
enable_private_endpointfalseDeploy a private endpoint
georeplications[]List of additional regions for replication

Outputs: container_registry_id, container_registry_login_server, container_registry_identity_principal_id