Skip to content

Azure Terraform - AKS Cluster and Node Pool Configuration

This page covers the configuration of an Azure Kubernetes Service (AKS) cluster and its associated node pools. The AKS setup includes user-assigned managed identities, role assignments, and custom networking configurations.

The reference includes:

  • Creating a user-assigned managed identity for the AKS cluster.
  • Assigning role-based access control (RBAC) roles such as "Network Contributor" and "Private DNS Zone Contributor" to the AKS cluster.
  • Defining the AKS cluster with private endpoints and a custom network profile.
  • Creating a default system node pool with auto-scaling enabled.
  • Configuring an additional node pool for monitoring workloads.

This setup ensures secure access and networking for the AKS cluster, following best practices for scalability and security within a private network.

resource "azurerm_user_assigned_identity" "aks_cluster" {
  name                = "${local.fingerprint}-cluster"
  resource_group_name = local.azure_resource_group
  location            = var.azure_location
}

resource "azurerm_role_assignment" "aks_cluster_network_contributor" {
  scope                = azurerm_virtual_network.this.id
  role_definition_name = "Network Contributor"
  principal_id         = azurerm_user_assigned_identity.aks_cluster.principal_id
}

resource "azurerm_role_assignment" "aks_cluster_private_dns_zone_contributor" {
  scope                = azurerm_private_dns_zone.privatelink_aks.id
  role_definition_name = "Private DNS Zone Contributor"
  principal_id         = azurerm_user_assigned_identity.aks_cluster.principal_id
}

resource "azurerm_role_assignment" "aks_cluster_route_table_contributor" {
  scope                = azurerm_route_table.default.id
  role_definition_name = "Network Contributor"
  principal_id         = azurerm_user_assigned_identity.aks_cluster.principal_id
}

resource "azurerm_kubernetes_cluster" "this" {
  name                = local.fingerprint
  location            = var.azure_location
  resource_group_name = local.azure_resource_group

  azure_policy_enabled                = true
  dns_prefix_private_cluster          = local.fingerprint
  kubernetes_version                  = var.aks_version
  local_account_disabled              = false
  oidc_issuer_enabled                 = true
  private_cluster_enabled             = true
  private_cluster_public_fqdn_enabled = false
  private_dns_zone_id                 = azurerm_private_dns_zone.privatelink_aks.id
  workload_identity_enabled           = true

  sku_tier = "Standard"

  api_server_access_profile {
    authorized_ip_ranges = var.aks_allowlist_cidrs
  }

  auto_scaler_profile {
    balance_similar_node_groups      = false
    expander                         = "random"
    empty_bulk_delete_max            = "10"
    max_graceful_termination_sec     = "600"
    max_node_provisioning_time       = "15m"
    max_unready_percentage           = "45"
    new_pod_scale_up_delay           = "0s"
    max_unready_nodes                = "3"
    scale_down_delay_after_add       = "10m"
    scale_down_delay_after_delete    = "10s"
    scale_down_delay_after_failure   = "3m"
    scale_down_unneeded              = "10m"
    scale_down_unready               = "10m"
    scale_down_utilization_threshold = "0.5"
    scan_interval                    = "10s"
    skip_nodes_with_local_storage    = false
    skip_nodes_with_system_pods      = true
  }

  azure_active_directory_role_based_access_control {
    admin_group_object_ids = var.aks_admin_group_object_ids
    azure_rbac_enabled     = true

    # The property managed is deprecated and will be defaulted to true in v4.0
    # of the AzureRM provider. Until the property is removed it must be
    # specified with true for AKS-managed Entra Integration.
    managed = true
  }


  default_node_pool {
    name = "system"

    node_count = 3
    max_count  = 5
    min_count  = 3

    vnet_subnet_id        = azurerm_subnet.default.id
    enable_node_public_ip = false

    enable_auto_scaling  = true
    orchestrator_version = var.aks_version

    vm_size                = "Standard_D4ds_v5"
    os_disk_size_gb        = 128
    os_disk_type           = "Managed"
    kubelet_disk_type      = "OS"
    type                   = "VirtualMachineScaleSets"
    enable_host_encryption = false
    ultra_ssd_enabled      = false
    os_sku                 = "Ubuntu"
    fips_enabled           = false

    node_labels = {
      "cluster.yellowbrick.io/node_type" : "yb-operator",
      "cluster.yellowbrick.io/hardware_type" : "Standard_D4ds_v5"
    }
  }

  dynamic "linux_profile" {
    for_each = var.admin_server_admin_user != "" && var.admin_server_ssh_public_key_path != "" ? [1] : []
    content {
      admin_username = var.admin_server_admin_user

      ssh_key {
        key_data = file(var.admin_server_ssh_public_key_path)
      }
    }
  }

  identity {
    type = "UserAssigned"
    identity_ids = [
      azurerm_user_assigned_identity.aks_cluster.id
    ]
  }

  network_profile {
    load_balancer_sku = "standard"
    network_plugin    = "kubenet"
    outbound_type     = "userDefinedRouting"
    pod_cidr          = "10.244.0.0/16"
    service_cidr      = "10.0.0.0/16"
    dns_service_ip    = "10.0.0.10"
  }

  storage_profile {
    snapshot_controller_enabled = true
  }

  depends_on = [
    azurerm_role_assignment.aks_cluster_network_contributor,
    azurerm_role_assignment.aks_cluster_private_dns_zone_contributor,
    azurerm_role_assignment.aks_cluster_route_table_contributor,
    azurerm_firewall_policy_rule_collection_group.this
  ]

  tags = local.tags
}

resource "azurerm_kubernetes_cluster_node_pool" "monitoring" {
  name                  = "monitord8v5"
  kubernetes_cluster_id = azurerm_kubernetes_cluster.this.id

  node_count = 1
  max_count  = 30
  min_count  = 0

  vnet_subnet_id        = azurerm_subnet.default.id
  enable_node_public_ip = false

  enable_auto_scaling  = true
  orchestrator_version = var.aks_version

  vm_size                = "Standard_D8_v5"
  os_disk_size_gb        = 128
  os_disk_type           = "Managed"
  kubelet_disk_type      = "OS"
  enable_host_encryption = false
  ultra_ssd_enabled      = false
  os_sku                 = "Ubuntu"
  fips_enabled           = false

  node_labels = {
    "cluster.yellowbrick.io/node_type" : "yb-monitoring-standard",
    "cluster.yellowbrick.io/hardware_type" : "Standard_D8_v5"
  }

  tags = local.tags
}