← Back to list

Infrastructure as Code Mastery: A Production Kubernetes Cluster on Azure

Keep your IaC DRY! A complete guide to deploying a production Kubernetes cluster on Azure with Terraform & Terragrunt.

amoda mendis · 2026-05-20 17:01 · 0 claps · 5.2 min read
#infrastructure-as-code #kubernetes #kubernetes-cluster #terraform #terragrunt
Open on Medium ↗
Wiki topics: RAG · RAG & Retrieval ☁️ · DevOps & Cloud

Infrastructure as Code Mastery: A Production Kubernetes Cluster on Azure

Prerequisites for Today’s work

Before starting the Week 02 project, the following tools and platforms are required:

✔ Azure Account (Free tier is sufficient)
✔ GitHub Account
✔ WSL2 (Windows Subsystem for Linux)
✔ Azure CLI
✔ Terraform & Terraform Version Manager
✔ Terragrunt & Terragrunt Version Manager
✔ kubectl (Kubernetes CLI

What is Terraform and Terragrunt?

Terraform

Terraform is an Infrastructure as Code tool that lets you define both cloud and on-prem resources in human-readable configuration files that you can version, reuse, and share. You can then use a consistent workflow to provision and manage all of your infrastructure throughout its lifecycle.

Terragrunt

Terragrunt is a thin wrapper for Terraform/OpenTofu that provides extra tools for keeping configurations DRY (Don’t Repeat Yourself), managing remote state, and orchestrating infrastructure across multiple environments. It enhances Terraform by enabling module inheritance, reducing code duplication, and simplifying complex tasks.

Reference: https://terragrunt.gruntwork.io/docs/getting-started/overview/

Step-by-Step Process: Building the K8s Cluster on Azure

Step 1 — Create the Project Folder and Open in VS Code

First, create a folder named K8-cluster, then open it with WSL. Run the following commands in the terminal:

cd /mnt/d
cd K8-cluster/

Then open the folder in VS Code:

code .

Next, log in to the Azure CLI:

az login

Step 2 — Create the Root Configuration Files

Inside the K8-cluster folder, create three files : terragrunt.hcl, org.yaml, and empty.yaml

terragrunt.hcl

locals {
  default_yaml_path     = find_in_parent_folders("empty.yaml")
  org                   = yamldecode(file(find_in_parent_folders("org.yaml")))
  subscription          = yamldecode(file(find_in_parent_folders("subscription.yaml")))
  resource_group        = yamldecode(file(find_in_parent_folders("resource_group.yaml")))
  environment           = yamldecode(file(find_in_parent_folders("environment.yaml", local.default_yaml_path)))
}

generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
provider "azurerm" {
  features {}
  subscription_id = "${local.subscription.subscription_id}"
}
EOF
}

remote_state {
  backend = "azurerm"
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite"
  }
  config = {
    subscription_id      = local.subscription.state_subscription
    resource_group_name  = local.subscription.state_resource_group
    storage_account_name = local.subscription.state_storage_account
    container_name       = "terraform-state"
    key                  = "${path_relative_to_include()}/terraform.tfstate"
  }
}

inputs = merge(
    local.org,
    local.subscription,
    local.resource_group,
    local.environment
)

org.yaml

org_name: Default Directory
# Go to your Azure account Directories to find your org name

empty.yaml

{ }

The empty.yaml file acts as a fallback — if any file is not found or an error occurs, those details will be recorded here.

Step 3 — Set Up Azure Remote State (State Lock)

Log in to your Azure account and create a Resource Group named terraform-state. Then create a Storage Account inside this resource group with the following settings:

• Resource Group: terraform-state

• Insert a unique name for the account

• Select your region

• Preferred storage type: Azure Blob Storage or Azure Data Lake Storage Gen 2

Then open the Storage Account and create a container named terraform-state.

Step 4 — Create the Subscription and Resource Group Folder Structure

Inside K8-cluster, create a folder named subscriptions. Inside it, create another folder with your Azure subscription name (e.g., Azure for Students). Inside that, create subscription.yaml:

subscription.yaml

state_subscription : <subscription_id>
state_resource_group : <resource_group_name>
state_storage_account : <storage_account_name>
subscription_id: <subscription_id>

Then create: subscriptions/Azure for Students/resource-groups/terra-v2/resource_group.yaml

resource_group.yaml

location: uksouth
resource_group_name: <insert any name>

Step 5 — Provision the Resource Group

Under terra-v2, create a folder named resourcegroup with three files:

include {
    path = find_in_parent_folders()
}

variables.tf

variable location {}
variable resource_group_name {}

resourcegroup.tf

resource "azurerm_resource_group" "<resource_group_name>" {
  location = var.location
  name     = var.resource_group_name
}

Navigate to the resourcegroup folder and run:

cd subscriptions/Azure\ for\ Students/resource-groups/terra-v2/resourcegroup/
terragrunt apply

After running terragrunt apply, you will see the following output showing the resource group plan and prompting for confirmation:

Terragrunt apply output — Resource Group creation plan

Terragrunt apply output — Resource Group creation plan

Final Folder Structure

Here is the complete folder structure inside the K8-cluster project in VS Code:

VS Code folder structure for the K8-Cluster project

VS Code folder structure for the K8-Cluster project

K8-cluster/
├── terragrunt.hcl
├── org.yaml
├── empty.yaml
└── subscriptions/
    └── Azure for Students/
        ├── subscription.yaml
        └── resource-groups/
            └── terra-v2/
                ├── resource_group.yaml
                ├── resourcegroup/
                │   ├── terragrunt.hcl
                │   ├── variables.tf
                │   └── resourcegroup.tf
                ├── networks/
                │   └── cluster-network/
                │       ├── terragrunt.hcl
                │       ├── variables.tf
                │       ├── virtual-network.tf
                │       └── subnet.tf
                └── aks/
                    └── K8-cluster/
                        ├── terragrunt.hcl
                        ├── variables.tf
                        ├── data.tf
                        └── cluster.tf

Step 6 — (Optional) Create an Azure Storage Account

To create any additional resource, follow the same pattern. For example, to create a storage account:

K8-tf-bucket.tf

resource "azurerm_storage_account" "k8s-workshop" {
  name                     = "workshopbucket"
  resource_group_name      = var.resource_group_name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "GRS"

  tags = {
    environment = "staging"
  }
}

variables.tf

variable location {}
variable resource_group_name {}

terragrunt.hcl

include {
    path = find_in_parent_folders()
}
cd subscriptions/Azure\ for\ Students/resource-groups/terra-v2/buckets/K8-tf-bucket
terragrunt apply

Step 7 — Create the Cluster Network

Before creating the Kubernetes cluster, we need to set up the virtual network with subnets. Under terra-v2, create networks/cluster-network with these files:

virtual-network.tf

resource "azurerm_virtual_network" "vnet" {
  name                = "cluster-vnet-${var.resource_group_name}"
  address_space       = ["10.3.0.0/16"]
  location            = var.location
  resource_group_name = var.resource_group_name
}

subnet.tf

resource "azurerm_subnet" "cluster_subnet" {
  name                                      = "cluster-subnet-${var.resource_group_name}"
  resource_group_name                       = var.resource_group_name
  virtual_network_name                      = azurerm_virtual_network.vnet.name
  address_prefixes                          = ["10.3.1.0/24"]
  private_endpoint_network_policies         = "Enabled"
}

Add the same terragrunt.hcl and variables.tf as before. Then run:

cd subscriptions/Azure\ for\ Students/resource-groups/terra-v2/networks/cluster-network
terragrunt apply

Terraform will plan to create 2 resources (the VNet and subnet). Type yes to confirm:

Terragrunt apply output — Virtual Network and Subnet creation plan

Terragrunt apply output — Virtual Network and Subnet creation plan

Step 8 — Create the Kubernetes Cluster

Under terra-v2, create aks/K8-cluster with these files:

data.tf

data "azurerm_virtual_network" "cluster_vnet" {
  name                = "cluster-vnet-${var.resource_group_name}"
  resource_group_name = var.resource_group_name
}

data "azurerm_subnet" "cluster_subnet" {
  name                 = "cluster-subnet-${var.resource_group_name}"
  virtual_network_name = "cluster-vnet-${var.resource_group_name}"
  resource_group_name  = var.resource_group_name
}

cluster.tf

resource "azurerm_kubernetes_cluster" "k8s-ws-cluster" {
  name                        = "k8s-ws-cluster-${var.resource_group_name}"
  resource_group_name         = var.resource_group_name
  location                    = var.location
  dns_prefix                  = var.resource_group_name
  kubernetes_version          = var.kubernetes_version

  automatic_upgrade_channel   = "patch"
  image_cleaner_interval_hours = 48

  private_cluster_enabled = false

  default_node_pool {
    name                        = "system"
    node_count                  = 1
    vm_size                     = var.instance_type
    vnet_subnet_id              = data.azurerm_subnet.cluster_subnet.id
    temporary_name_for_rotation = "systemtemp"
    orchestrator_version        = var.kubernetes_version
    upgrade_settings {
      drain_timeout_in_minutes      = 0
      max_surge                     = "10%"
      node_soak_duration_in_minutes = 0
    }
  }

  identity {
    type = "SystemAssigned"
  }
}

Add the same terragrunt.hcl and variables.tf, then run:

cd subscriptions/Azure\ for\ Students/resource-groups/terra-v2/aks/K8-cluster/
terragrunt apply

Step 9 — Connect the Cluster to Your Local Terminal

Go to your Kubernetes cluster in the Azure Portal and click the Connect option at the top. A panel will open with commands. Copy and run them in your terminal, appending — admin to the second command:

Azure CLI commands to connect the AKS cluster to your local terminal

Azure CLI commands to connect the AKS cluster to your local terminal

Note: The image above shows the actual terminal commands used to set the subscription and retrieve credentials with — admin flag.

# Verify kubectl is installed
kubectl version

# List all pods
kubectl get pods

# List all resources
kubectl get all

# Apply a manifest
kubectl apply -f pod.yaml

Now it’s done , and Enjoy it by doing it real. Thankyou for being with me.

GitHub Repository

The complete project with all configurations used in this setup is available at:

[embed]GitHub - tharinduk001/terragrunt-azure: Repo for provision resources on Azure using Terragrunt Repo for provision resources on Azure using Terragrunt - GitHub - tharinduk001/terragrunt-azure: Repo for provision…github.com


메타데이터
post_id
57afdd587ef8
slug
infrastructure-as-code-mastery-a-production-kubernetes-cluster-on-azure-57afdd587ef8
url
https://medium.com/@amodarashmika2003/infrastructure-as-code-mastery-a-production-kubernetes-cluster-on-azure-57afdd587ef8
canonical_url
https://medium.com/@amodarashmika2003/infrastructure-as-code-mastery-a-production-kubernetes-cluster-on-azure-57afdd587ef8
author_url
https://medium.com/@amodarashmika2003
status
ok
fetched_at
2026-06-09 15:37:30