From 5228e483b45e15d5adfc7464c43c9ad9e2a40883 Mon Sep 17 00:00:00 2001
From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
Date: Mon, 8 Dec 2025 10:10:35 -0500
Subject: [PATCH 1/7] feat(aks): Multi-AZ and user NAT routing
* Enables multi-AZ for the AKS cluster node pools.
* Routes outbound traffic via the custom NAT gateway resource
* Adds network profile settings for full cilium support
Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
---
terraform/cluster/azure-aks/main.tf | 13 +-
terraform/cluster/azure-aks/test.tftest.hcl | 119 +++++++++++++++---
terraform/cluster/azure-aks/variables.tf | 12 +-
terraform/network/aws-vpc/.terraform.lock.hcl | 1 +
terraform/network/aws-vpc/README.md | 52 ++++----
terraform/network/azure-vnet/README.md | 6 +-
terraform/network/azure-vnet/main.tf | 17 ++-
terraform/network/azure-vnet/test.tftest.hcl | 60 +++++++++
8 files changed, 226 insertions(+), 54 deletions(-)
diff --git a/terraform/cluster/azure-aks/main.tf b/terraform/cluster/azure-aks/main.tf
index cc729630a..ea9a33b65 100644
--- a/terraform/cluster/azure-aks/main.tf
+++ b/terraform/cluster/azure-aks/main.tf
@@ -247,6 +247,7 @@ resource "azurerm_kubernetes_cluster" "main" {
vnet_subnet_id = coalesce(var.vnet_subnet_id, try(data.azurerm_subnet.private[0].id, null))
orchestrator_version = var.kubernetes_version
only_critical_addons_enabled = var.default_node_pool.only_critical_addons_enabled
+ zones = var.default_node_pool.availability_zones
# checkov:skip=CKV_AZURE_226: we are using the managed disk type to reduce costs
os_disk_type = var.default_node_pool.os_disk_type
@@ -275,10 +276,13 @@ resource "azurerm_kubernetes_cluster" "main" {
}
network_profile {
- network_plugin = "azure"
- network_policy = "cilium"
- service_cidr = var.service_cidr
- dns_service_ip = var.dns_service_ip
+ network_plugin = "azure"
+ network_plugin_mode = "overlay"
+ network_policy = "cilium"
+ network_data_plane = "cilium"
+ outbound_type = var.outbound_type
+ service_cidr = var.service_cidr
+ dns_service_ip = var.dns_service_ip
}
oms_agent {
@@ -313,6 +317,7 @@ resource "azurerm_kubernetes_cluster_node_pool" "autoscaled" {
auto_scaling_enabled = true
min_count = var.autoscaled_node_pool.min_count
max_count = var.autoscaled_node_pool.max_count
+ zones = var.autoscaled_node_pool.availability_zones
vnet_subnet_id = coalesce(
var.vnet_subnet_id,
try(data.azurerm_subnet.private[length(local.private_subnets) - 1].id, null)
diff --git a/terraform/cluster/azure-aks/test.tftest.hcl b/terraform/cluster/azure-aks/test.tftest.hcl
index 3a3a6885e..f7b7cdb22 100644
--- a/terraform/cluster/azure-aks/test.tftest.hcl
+++ b/terraform/cluster/azure-aks/test.tftest.hcl
@@ -22,8 +22,18 @@ mock_provider "azurerm" {
id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Network/virtualNetworks/vnet-test/subnets/subnet-test"
}
}
+ mock_resource "azurerm_kubernetes_cluster" {
+ defaults = {
+ kubelet_identity = [{
+ client_id = "44444444-4444-4444-4444-444444444444"
+ object_id = "55555555-5555-5555-5555-555555555555"
+ user_assigned_identity_id = ""
+ }]
+ }
+ }
}
+
# Verifies that the module creates an AKS cluster with minimal configuration,
# ensuring that all default values are correctly applied and only required variables are set.
run "minimal_configuration" {
@@ -35,6 +45,17 @@ run "minimal_configuration" {
kubernetes_version = "1.32"
}
+ override_resource {
+ target = azurerm_kubernetes_cluster.main
+ values = {
+ kubelet_identity = [{
+ client_id = "44444444-4444-4444-4444-444444444444"
+ object_id = "55555555-5555-5555-5555-555555555555"
+ user_assigned_identity_id = ""
+ }]
+ }
+ }
+
assert {
condition = azurerm_kubernetes_cluster.main.name == "windsor-aks-test"
error_message = "Cluster name should default to 'windsor-aks-test' when cluster_name is omitted"
@@ -84,6 +105,21 @@ run "minimal_configuration" {
condition = azurerm_kubernetes_cluster.main.identity[0].type == "SystemAssigned"
error_message = "Cluster should use system-assigned identity by default"
}
+
+ assert {
+ condition = azurerm_kubernetes_cluster.main.network_profile[0].outbound_type == "userAssignedNATGateway"
+ error_message = "Default outbound type should be 'userAssignedNATGateway'"
+ }
+
+ assert {
+ condition = azurerm_kubernetes_cluster.main.network_profile[0].network_plugin_mode == "overlay"
+ error_message = "Network plugin mode should be 'overlay'"
+ }
+
+ assert {
+ condition = azurerm_kubernetes_cluster.main.network_profile[0].network_data_plane == "cilium"
+ error_message = "Network data plane should be 'cilium'"
+ }
}
# Tests a full configuration with all optional variables explicitly set,
@@ -97,13 +133,6 @@ run "full_configuration" {
cluster_name = "test-cluster"
resource_group_name = "test-rg"
kubernetes_version = "1.32"
- user_assigned_identity_ids = [
- "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity-1",
- "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity-2"
- ]
- kubelet_client_id = "test-client-id"
- kubelet_object_id = "test-object-id"
- kubelet_user_assigned_identity_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity-1"
default_node_pool = {
name = "system"
vm_size = "Standard_D2s_v3"
@@ -114,6 +143,7 @@ run "full_configuration" {
max_count = 3
node_count = 1
only_critical_addons_enabled = false
+ availability_zones = ["1", "2", "3"]
}
autoscaled_node_pool = {
enabled = true
@@ -125,11 +155,24 @@ run "full_configuration" {
host_encryption_enabled = true
min_count = 1
max_count = 3
+ availability_zones = ["1", "2"]
}
role_based_access_control_enabled = true
private_cluster_enabled = false
azure_policy_enabled = true
local_account_disabled = false
+ outbound_type = "loadBalancer"
+ }
+
+ override_resource {
+ target = azurerm_kubernetes_cluster.main
+ values = {
+ kubelet_identity = [{
+ client_id = "44444444-4444-4444-4444-444444444444"
+ object_id = "55555555-5555-5555-5555-555555555555"
+ user_assigned_identity_id = ""
+ }]
+ }
}
assert {
@@ -204,32 +247,37 @@ run "full_configuration" {
assert {
condition = azurerm_kubernetes_cluster.main.local_account_disabled == false
- error_message = "Local accounts should be enabled"
+ error_message = "Local accounts should be disabled when explicitly set to false"
+ }
+
+ assert {
+ condition = azurerm_kubernetes_cluster.main.identity[0].type == "SystemAssigned"
+ error_message = "Cluster should use system-assigned identity by default"
}
assert {
- condition = azurerm_kubernetes_cluster.main.identity[0].type == "UserAssigned"
- error_message = "Cluster should use user-assigned identity when IDs are provided"
+ condition = length(azurerm_kubernetes_cluster.main.default_node_pool[0].zones) == 3 && contains(azurerm_kubernetes_cluster.main.default_node_pool[0].zones, "1") && contains(azurerm_kubernetes_cluster.main.default_node_pool[0].zones, "2") && contains(azurerm_kubernetes_cluster.main.default_node_pool[0].zones, "3")
+ error_message = "Default node pool zones should match input value"
}
assert {
- condition = length(azurerm_kubernetes_cluster.main.identity[0].identity_ids) == 2
- error_message = "Cluster should have 2 user-assigned identity IDs"
+ condition = length(azurerm_kubernetes_cluster_node_pool.autoscaled[0].zones) == 2 && contains(azurerm_kubernetes_cluster_node_pool.autoscaled[0].zones, "1") && contains(azurerm_kubernetes_cluster_node_pool.autoscaled[0].zones, "2")
+ error_message = "Autoscaled node pool zones should match input value"
}
assert {
- condition = azurerm_kubernetes_cluster.main.kubelet_identity[0].client_id == "test-client-id"
- error_message = "Kubelet client ID should match input"
+ condition = azurerm_kubernetes_cluster.main.network_profile[0].outbound_type == "loadBalancer"
+ error_message = "Outbound type should match input value"
}
assert {
- condition = azurerm_kubernetes_cluster.main.kubelet_identity[0].object_id == "test-object-id"
- error_message = "Kubelet object ID should match input"
+ condition = azurerm_kubernetes_cluster.main.network_profile[0].network_plugin_mode == "overlay"
+ error_message = "Network plugin mode should be 'overlay'"
}
assert {
- condition = azurerm_kubernetes_cluster.main.kubelet_identity[0].user_assigned_identity_id == "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity-1"
- error_message = "Kubelet user-assigned identity ID should match input"
+ condition = azurerm_kubernetes_cluster.main.network_profile[0].network_data_plane == "cilium"
+ error_message = "Network data plane should be 'cilium'"
}
}
@@ -246,6 +294,17 @@ run "private_cluster" {
kubernetes_version = "1.32"
}
+ override_resource {
+ target = azurerm_kubernetes_cluster.main
+ values = {
+ kubelet_identity = [{
+ client_id = "44444444-4444-4444-4444-444444444444"
+ object_id = "55555555-5555-5555-5555-555555555555"
+ user_assigned_identity_id = ""
+ }]
+ }
+ }
+
assert {
condition = azurerm_kubernetes_cluster.main.private_cluster_enabled == true
error_message = "Private cluster should be enabled"
@@ -264,6 +323,17 @@ run "config_file_created" {
context_path = "/tmp"
}
+ override_resource {
+ target = azurerm_kubernetes_cluster.main
+ values = {
+ kubelet_identity = [{
+ client_id = "44444444-4444-4444-4444-444444444444"
+ object_id = "55555555-5555-5555-5555-555555555555"
+ user_assigned_identity_id = ""
+ }]
+ }
+ }
+
assert {
condition = length(local_file.kube_config) >= 1
error_message = "Kubeconfig file should be generated when context path is provided"
@@ -279,6 +349,17 @@ run "network_configuration" {
dns_service_ip = "10.0.0.10"
}
+ override_resource {
+ target = azurerm_kubernetes_cluster.main
+ values = {
+ kubelet_identity = [{
+ client_id = "44444444-4444-4444-4444-444444444444"
+ object_id = "55555555-5555-5555-5555-555555555555"
+ user_assigned_identity_id = ""
+ }]
+ }
+ }
+
assert {
condition = azurerm_kubernetes_cluster.main.network_profile[0].service_cidr == "10.0.0.0/16"
error_message = "Service CIDR should match input value"
@@ -294,9 +375,11 @@ run "multiple_invalid_inputs" {
command = plan
expect_failures = [
var.kubernetes_version,
+ var.outbound_type,
]
variables {
context_id = "test"
kubernetes_version = "v1.32"
+ outbound_type = "invalid"
}
}
diff --git a/terraform/cluster/azure-aks/variables.tf b/terraform/cluster/azure-aks/variables.tf
index 44e0a5947..b9ddb69a6 100644
--- a/terraform/cluster/azure-aks/variables.tf
+++ b/terraform/cluster/azure-aks/variables.tf
@@ -73,6 +73,7 @@ variable "default_node_pool" {
max_count = number
node_count = number
only_critical_addons_enabled = bool
+ availability_zones = optional(list(string))
})
default = {
name = "system"
@@ -99,6 +100,7 @@ variable "autoscaled_node_pool" {
host_encryption_enabled = bool
min_count = number
max_count = number
+ availability_zones = optional(list(string))
})
default = {
enabled = true
@@ -241,10 +243,14 @@ variable "endpoint_private_access" {
default = false
}
-variable "kubelet_client_id" {
- description = "Client ID of the user-assigned identity to use for the kubelet. If not provided, the cluster will use the system-assigned identity."
+variable "outbound_type" {
+ description = "The outbound (egress) routing method which should be used for this Kubernetes Cluster."
type = string
- default = null
+ default = "userAssignedNATGateway"
+ validation {
+ condition = contains(["loadBalancer", "userDefinedRouting", "managedNATGateway", "userAssignedNATGateway"], var.outbound_type)
+ error_message = "The outbound_type must be one of: loadBalancer, userDefinedRouting, managedNATGateway, userAssignedNATGateway."
+ }
}
variable "kubelet_object_id" {
diff --git a/terraform/network/aws-vpc/.terraform.lock.hcl b/terraform/network/aws-vpc/.terraform.lock.hcl
index aa48e2247..5c0fff4e8 100644
--- a/terraform/network/aws-vpc/.terraform.lock.hcl
+++ b/terraform/network/aws-vpc/.terraform.lock.hcl
@@ -13,6 +13,7 @@ provider "registry.terraform.io/hashicorp/aws" {
"h1:PQ3jzG6VNrfS35adtrBeLnVTnJef3f2t5SUV7XNikgo=",
"h1:QAcpv9yoqEtVaBVyQ3hHsTc558AchV5/8lfAGoqmUkA=",
"h1:QJEljz77aB459tng0v+5xIdV6mkmCM4RZO6ztk3pOEA=",
+ "h1:QJr1C4scuvEslohwPKrBPEjhQRyMT26HzhxZlLjl3cw=",
"h1:RB3r7K1PgJ6S3J0l4u5/nB7G/inM2goPWY+QHesxuGo=",
"h1:UT4pxGbPuANnxyCeDn5/Ybr476EkyxcsYsU+q0iYt/Q=",
"h1:Uv/PPkYgnjKcsesOVWiTHaY/1R5/OgH1UYnXvtu0K58=",
diff --git a/terraform/network/aws-vpc/README.md b/terraform/network/aws-vpc/README.md
index 09a54fdaf..6a0f40bce 100644
--- a/terraform/network/aws-vpc/README.md
+++ b/terraform/network/aws-vpc/README.md
@@ -4,14 +4,14 @@
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >=1.8 |
-| [aws](#requirement\_aws) | 6.18.0 |
+| [aws](#requirement\_aws) | 6.25.0 |
| [random](#requirement\_random) | 3.7.2 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | 6.18.0 |
+| [aws](#provider\_aws) | 6.25.0 |
| [null](#provider\_null) | 3.2.4 |
| [random](#provider\_random) | 3.7.2 |
@@ -23,32 +23,32 @@ No modules.
| Name | Type |
|------|------|
-| [aws_cloudwatch_log_group.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/cloudwatch_log_group) | resource |
-| [aws_default_security_group.default](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/default_security_group) | resource |
-| [aws_eip.nat](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/eip) | resource |
-| [aws_flow_log.main](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/flow_log) | resource |
-| [aws_iam_role.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/iam_role) | resource |
-| [aws_iam_role_policy.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/iam_role_policy) | resource |
-| [aws_internet_gateway.main](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/internet_gateway) | resource |
-| [aws_kms_alias.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/kms_alias) | resource |
-| [aws_kms_key.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/kms_key) | resource |
-| [aws_nat_gateway.main](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/nat_gateway) | resource |
-| [aws_route53_zone.main](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/route53_zone) | resource |
-| [aws_route_table.isolated](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/route_table) | resource |
-| [aws_route_table.private](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/route_table) | resource |
-| [aws_route_table.public](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/route_table) | resource |
-| [aws_route_table_association.isolated](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/route_table_association) | resource |
-| [aws_route_table_association.private](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/route_table_association) | resource |
-| [aws_route_table_association.public](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/route_table_association) | resource |
-| [aws_subnet.isolated](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/subnet) | resource |
-| [aws_subnet.private](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/subnet) | resource |
-| [aws_subnet.public](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/subnet) | resource |
-| [aws_vpc.main](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/resources/vpc) | resource |
+| [aws_cloudwatch_log_group.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/cloudwatch_log_group) | resource |
+| [aws_default_security_group.default](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/default_security_group) | resource |
+| [aws_eip.nat](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/eip) | resource |
+| [aws_flow_log.main](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/flow_log) | resource |
+| [aws_iam_role.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/iam_role_policy) | resource |
+| [aws_internet_gateway.main](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/internet_gateway) | resource |
+| [aws_kms_alias.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/kms_alias) | resource |
+| [aws_kms_key.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/kms_key) | resource |
+| [aws_nat_gateway.main](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/nat_gateway) | resource |
+| [aws_route53_zone.main](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/route53_zone) | resource |
+| [aws_route_table.isolated](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/route_table) | resource |
+| [aws_route_table.private](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/route_table) | resource |
+| [aws_route_table.public](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/route_table) | resource |
+| [aws_route_table_association.isolated](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/route_table_association) | resource |
+| [aws_route_table_association.private](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/route_table_association) | resource |
+| [aws_route_table_association.public](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/route_table_association) | resource |
+| [aws_subnet.isolated](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/subnet) | resource |
+| [aws_subnet.private](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/subnet) | resource |
+| [aws_subnet.public](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/subnet) | resource |
+| [aws_vpc.main](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/resources/vpc) | resource |
| [null_resource.delete_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [random_string.log_group_suffix](https://registry.terraform.io/providers/hashicorp/random/3.7.2/docs/resources/string) | resource |
-| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/data-sources/availability_zones) | data source |
-| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/data-sources/caller_identity) | data source |
-| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/6.18.0/docs/data-sources/region) | data source |
+| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/data-sources/availability_zones) | data source |
+| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/data-sources/caller_identity) | data source |
+| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/6.25.0/docs/data-sources/region) | data source |
## Inputs
diff --git a/terraform/network/azure-vnet/README.md b/terraform/network/azure-vnet/README.md
index 49910754b..0be772f1d 100644
--- a/terraform/network/azure-vnet/README.md
+++ b/terraform/network/azure-vnet/README.md
@@ -4,13 +4,13 @@
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >=1.8 |
-| [azurerm](#requirement\_azurerm) | ~> 4.50.0 |
+| [azurerm](#requirement\_azurerm) | ~> 4.55.0 |
## Providers
| Name | Version |
|------|---------|
-| [azurerm](#provider\_azurerm) | 4.50.0 |
+| [azurerm](#provider\_azurerm) | 4.55.0 |
## Modules
@@ -24,10 +24,12 @@ No modules.
| [azurerm_nat_gateway_public_ip_association.main](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/nat_gateway_public_ip_association) | resource |
| [azurerm_public_ip.nat](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/public_ip) | resource |
| [azurerm_resource_group.main](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource |
+| [azurerm_route_table.private](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/route_table) | resource |
| [azurerm_subnet.isolated](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) | resource |
| [azurerm_subnet.private](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) | resource |
| [azurerm_subnet.public](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) | resource |
| [azurerm_subnet_nat_gateway_association.private](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_nat_gateway_association) | resource |
+| [azurerm_subnet_route_table_association.private](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_route_table_association) | resource |
| [azurerm_virtual_network.main](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_network) | resource |
## Inputs
diff --git a/terraform/network/azure-vnet/main.tf b/terraform/network/azure-vnet/main.tf
index 8fee9e21a..664c7236a 100644
--- a/terraform/network/azure-vnet/main.tf
+++ b/terraform/network/azure-vnet/main.tf
@@ -125,7 +125,22 @@ resource "azurerm_nat_gateway_public_ip_association" "main" {
public_ip_address_id = azurerm_public_ip.nat[count.index].id
}
-# Associate NAT Gateway with private subnet
+resource "azurerm_route_table" "private" {
+ count = var.vnet_zones
+ name = "${var.name}-private-${count.index + 1}-${var.context_id}"
+ location = azurerm_resource_group.main.location
+ resource_group_name = azurerm_resource_group.main.name
+ tags = merge({
+ Name = "${var.name}-private-${count.index + 1}-${var.context_id}"
+ }, local.tags)
+}
+
+resource "azurerm_subnet_route_table_association" "private" {
+ count = var.vnet_zones
+ subnet_id = azurerm_subnet.private[count.index].id
+ route_table_id = azurerm_route_table.private[count.index].id
+}
+
resource "azurerm_subnet_nat_gateway_association" "private" {
count = var.enable_nat_gateway ? var.vnet_zones : 0
subnet_id = azurerm_subnet.private[count.index].id
diff --git a/terraform/network/azure-vnet/test.tftest.hcl b/terraform/network/azure-vnet/test.tftest.hcl
index 9963e5898..75c5e12ed 100644
--- a/terraform/network/azure-vnet/test.tftest.hcl
+++ b/terraform/network/azure-vnet/test.tftest.hcl
@@ -44,6 +44,21 @@ run "minimal_configuration" {
condition = length(azurerm_nat_gateway.main) == 1
error_message = "One NAT Gateway should be created by default"
}
+
+ assert {
+ condition = length(azurerm_route_table.private) == 1
+ error_message = "One route table should be created for private subnets by default"
+ }
+
+ assert {
+ condition = length(azurerm_subnet_route_table_association.private) == 1
+ error_message = "One route table association should be created for private subnets by default"
+ }
+
+ assert {
+ condition = azurerm_route_table.private[0].name == "windsor-vnet-private-1-test"
+ error_message = "Route table name should follow naming convention"
+ }
}
# Tests a full configuration with all optional variables explicitly set.
@@ -100,6 +115,26 @@ run "full_configuration" {
condition = length(azurerm_nat_gateway.main) == 2
error_message = "Two NAT Gateways should be created"
}
+
+ assert {
+ condition = length(azurerm_route_table.private) == 2
+ error_message = "Two route tables should be created for private subnets"
+ }
+
+ assert {
+ condition = length(azurerm_subnet_route_table_association.private) == 2
+ error_message = "Two route table associations should be created for private subnets"
+ }
+
+ assert {
+ condition = azurerm_route_table.private[0].name == "custom-private-1-test"
+ error_message = "First route table name should follow naming convention"
+ }
+
+ assert {
+ condition = azurerm_route_table.private[1].name == "custom-private-2-test"
+ error_message = "Second route table name should follow naming convention"
+ }
}
# Tests NAT Gateway configuration
@@ -191,6 +226,31 @@ run "automatic_subnet_creation" {
condition = azurerm_subnet.public[2].address_prefixes[0] == "10.0.53.0/24"
error_message = "Third public subnet should be 10.0.53.0/24"
}
+
+ assert {
+ condition = length(azurerm_route_table.private) == 3
+ error_message = "Three route tables should be created for private subnets when vnet_zones is 3"
+ }
+
+ assert {
+ condition = length(azurerm_subnet_route_table_association.private) == 3
+ error_message = "Three route table associations should be created for private subnets when vnet_zones is 3"
+ }
+
+ assert {
+ condition = azurerm_route_table.private[0].name == "test-network-private-1-test"
+ error_message = "First route table name should follow naming convention"
+ }
+
+ assert {
+ condition = azurerm_route_table.private[1].name == "test-network-private-2-test"
+ error_message = "Second route table name should follow naming convention"
+ }
+
+ assert {
+ condition = azurerm_route_table.private[2].name == "test-network-private-3-test"
+ error_message = "Third route table name should follow naming convention"
+ }
}
# Tests validation rules for required variables
From 4b38f677e2ac26cda7a9012e09fae24a63c44ff6 Mon Sep 17 00:00:00 2001
From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
Date: Mon, 8 Dec 2025 11:12:39 -0500
Subject: [PATCH 2/7] feat(aks): Leverage OIDC roles for kubelet and workloads
* Enable oidc issuer and workload identity
* Use system-assigned managed identities
* Add required disk manager role (breaks cluster otherwise)
Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
---
terraform/cluster/azure-aks/main.tf | 81 ++++++++++++++++-----
terraform/cluster/azure-aks/test.tftest.hcl | 41 +++++------
terraform/cluster/azure-aks/variables.tf | 28 ++-----
3 files changed, 89 insertions(+), 61 deletions(-)
diff --git a/terraform/cluster/azure-aks/main.tf b/terraform/cluster/azure-aks/main.tf
index cc729630a..e23bdb700 100644
--- a/terraform/cluster/azure-aks/main.tf
+++ b/terraform/cluster/azure-aks/main.tf
@@ -55,9 +55,16 @@ data "azurerm_subnet" "private" {
#-----------------------------------------------------------------------------------------------------------------------
locals {
- kubeconfig_path = "${var.context_path}/.kube/config"
- rg_name = var.resource_group_name == null ? "${var.name}-${var.context_id}" : var.resource_group_name
- cluster_name = var.cluster_name == null ? "${var.name}-${var.context_id}" : var.cluster_name
+ kubeconfig_path = "${var.context_path}/.kube/config"
+ rg_name = var.resource_group_name == null ? "${var.name}-${var.context_id}" : var.resource_group_name
+ cluster_name = var.cluster_name == null ? "${var.name}-${var.context_id}" : var.cluster_name
+ node_resource_group_name = split("/", azurerm_kubernetes_cluster.main.node_resource_group_id)[4]
+ node_pool_names = concat(
+ [var.default_node_pool.name],
+ var.autoscaled_node_pool.enabled ? [var.autoscaled_node_pool.name] : []
+ )
+ # Safely access kubelet identity (may not be available during plan in tests)
+ kubelet_object_id = try(azurerm_kubernetes_cluster.main.kubelet_identity[0].object_id, "00000000-0000-0000-0000-000000000000")
tags = merge({
WindsorContextID = var.context_id
}, var.tags)
@@ -274,6 +281,9 @@ resource "azurerm_kubernetes_cluster" "main" {
vertical_pod_autoscaler_enabled = var.workload_autoscaler_profile.vertical_pod_autoscaler_enabled
}
+ oidc_issuer_enabled = var.oidc_issuer_enabled
+ workload_identity_enabled = var.workload_identity_enabled
+
network_profile {
network_plugin = "azure"
network_policy = "cilium"
@@ -281,22 +291,11 @@ resource "azurerm_kubernetes_cluster" "main" {
dns_service_ip = var.dns_service_ip
}
- oms_agent {
- log_analytics_workspace_id = azurerm_log_analytics_workspace.aks_logs.id
- }
-
+ # Use system-assigned managed identity (Microsoft default and best practice)
+ # AKS automatically creates Contributor role on node RG for control plane
+ # AKS automatically creates Virtual Machine Contributor role on node RG for kubelet
identity {
- type = length(var.user_assigned_identity_ids) > 0 ? "UserAssigned" : "SystemAssigned"
- identity_ids = var.user_assigned_identity_ids
- }
-
- dynamic "kubelet_identity" {
- for_each = var.kubelet_user_assigned_identity_id != null ? [1] : []
- content {
- client_id = var.kubelet_client_id
- object_id = var.kubelet_object_id
- user_assigned_identity_id = var.kubelet_user_assigned_identity_id
- }
+ type = "SystemAssigned"
}
tags = merge({
@@ -330,6 +329,52 @@ resource "azurerm_kubernetes_cluster_node_pool" "autoscaled" {
}, local.tags)
}
+# AKS automatically creates Virtual Machine Contributor role assignment on node resource group for the kubelet identity.
+# However, disk attachment operations require additional permissions beyond Virtual Machine Contributor.
+# Create a custom role with minimal permissions for VMSS disk operations.
+resource "azurerm_role_definition" "aks_kubelet_vmss_disk_manager" {
+ name = "AKS Kubelet VMSS Disk Manager - ${var.context_id}"
+ scope = azurerm_kubernetes_cluster.main.node_resource_group_id
+ description = "Minimal permissions for AKS kubelet identity to manage VMSS disk attachments"
+
+ permissions {
+ actions = concat(
+ [
+ # VMSS virtual machine operations for disk attachment (REQUIRED)
+ "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/read",
+ "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/write",
+ # Core disk operations (REQUIRED for basic disk attachment)
+ "Microsoft.Compute/disks/read",
+ "Microsoft.Compute/disks/write",
+ "Microsoft.Compute/disks/delete",
+ "Microsoft.Compute/disks/beginGetAccess/action",
+ "Microsoft.Compute/disks/endGetAccess/action",
+ # Location/operation queries (may be needed for operation status checks)
+ "Microsoft.Compute/locations/DiskOperations/read",
+ "Microsoft.Compute/locations/vmSizes/read",
+ "Microsoft.Compute/locations/operations/read"
+ ],
+ var.enable_volume_snapshots ? [
+ # Snapshot operations (only included if volume snapshots are enabled)
+ "Microsoft.Compute/snapshots/read",
+ "Microsoft.Compute/snapshots/write",
+ "Microsoft.Compute/snapshots/delete"
+ ] : []
+ )
+ not_actions = []
+ }
+
+ assignable_scopes = [
+ azurerm_kubernetes_cluster.main.node_resource_group_id
+ ]
+}
+
+resource "azurerm_role_assignment" "kubelet_vmss_disk_manager" {
+ scope = azurerm_kubernetes_cluster.main.node_resource_group_id
+ role_definition_id = azurerm_role_definition.aks_kubelet_vmss_disk_manager.role_definition_resource_id
+ principal_id = local.kubelet_object_id
+}
+
resource "local_file" "kube_config" {
content = azurerm_kubernetes_cluster.main.kube_config_raw
filename = local.kubeconfig_path
diff --git a/terraform/cluster/azure-aks/test.tftest.hcl b/terraform/cluster/azure-aks/test.tftest.hcl
index 3a3a6885e..cc3c38ce1 100644
--- a/terraform/cluster/azure-aks/test.tftest.hcl
+++ b/terraform/cluster/azure-aks/test.tftest.hcl
@@ -84,6 +84,16 @@ run "minimal_configuration" {
condition = azurerm_kubernetes_cluster.main.identity[0].type == "SystemAssigned"
error_message = "Cluster should use system-assigned identity by default"
}
+
+ assert {
+ condition = azurerm_kubernetes_cluster.main.oidc_issuer_enabled == true
+ error_message = "OIDC issuer should be enabled by default"
+ }
+
+ assert {
+ condition = azurerm_kubernetes_cluster.main.workload_identity_enabled == true
+ error_message = "Workload Identity should be enabled by default"
+ }
}
# Tests a full configuration with all optional variables explicitly set,
@@ -97,13 +107,8 @@ run "full_configuration" {
cluster_name = "test-cluster"
resource_group_name = "test-rg"
kubernetes_version = "1.32"
- user_assigned_identity_ids = [
- "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity-1",
- "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity-2"
- ]
- kubelet_client_id = "test-client-id"
- kubelet_object_id = "test-object-id"
- kubelet_user_assigned_identity_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity-1"
+ oidc_issuer_enabled = true
+ workload_identity_enabled = true
default_node_pool = {
name = "system"
vm_size = "Standard_D2s_v3"
@@ -208,28 +213,18 @@ run "full_configuration" {
}
assert {
- condition = azurerm_kubernetes_cluster.main.identity[0].type == "UserAssigned"
- error_message = "Cluster should use user-assigned identity when IDs are provided"
- }
-
- assert {
- condition = length(azurerm_kubernetes_cluster.main.identity[0].identity_ids) == 2
- error_message = "Cluster should have 2 user-assigned identity IDs"
- }
-
- assert {
- condition = azurerm_kubernetes_cluster.main.kubelet_identity[0].client_id == "test-client-id"
- error_message = "Kubelet client ID should match input"
+ condition = azurerm_kubernetes_cluster.main.identity[0].type == "SystemAssigned"
+ error_message = "Cluster should use system-assigned identity"
}
assert {
- condition = azurerm_kubernetes_cluster.main.kubelet_identity[0].object_id == "test-object-id"
- error_message = "Kubelet object ID should match input"
+ condition = azurerm_kubernetes_cluster.main.oidc_issuer_enabled == true
+ error_message = "OIDC issuer should be enabled"
}
assert {
- condition = azurerm_kubernetes_cluster.main.kubelet_identity[0].user_assigned_identity_id == "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity-1"
- error_message = "Kubelet user-assigned identity ID should match input"
+ condition = azurerm_kubernetes_cluster.main.workload_identity_enabled == true
+ error_message = "Workload Identity should be enabled"
}
}
diff --git a/terraform/cluster/azure-aks/variables.tf b/terraform/cluster/azure-aks/variables.tf
index 44e0a5947..a2f149115 100644
--- a/terraform/cluster/azure-aks/variables.tf
+++ b/terraform/cluster/azure-aks/variables.tf
@@ -205,12 +205,6 @@ variable "expiration_date" {
default = null
}
-variable "user_assigned_identity_ids" {
- type = list(string)
- description = "User assigned identity IDs for the AKS cluster. If provided, the cluster will use only user-assigned identities."
- default = []
-}
-
variable "soft_delete_retention_days" {
type = number
description = "The number of days to retain the AKS cluster's key vault"
@@ -241,20 +235,14 @@ variable "endpoint_private_access" {
default = false
}
-variable "kubelet_client_id" {
- description = "Client ID of the user-assigned identity to use for the kubelet. If not provided, the cluster will use the system-assigned identity."
- type = string
- default = null
-}
-
-variable "kubelet_object_id" {
- description = "Object ID of the user-assigned identity to use for the kubelet. If not provided, the cluster will use the system-assigned identity."
- type = string
- default = null
+variable "oidc_issuer_enabled" {
+ description = "Enable OIDC issuer for the AKS cluster"
+ type = bool
+ default = true
}
-variable "kubelet_user_assigned_identity_id" {
- description = "Resource ID of the user-assigned identity to use for the kubelet. If not provided, the cluster will use the system-assigned identity."
- type = string
- default = null
+variable "workload_identity_enabled" {
+ description = "Enable Workload Identity for the AKS cluster"
+ type = bool
+ default = true
}
From 3be405255d884bc73ee3846165f68b1983f1c67f Mon Sep 17 00:00:00 2001
From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
Date: Mon, 8 Dec 2025 11:20:44 -0500
Subject: [PATCH 3/7] fmt
Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
---
terraform/cluster/azure-aks/test.tftest.hcl | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/terraform/cluster/azure-aks/test.tftest.hcl b/terraform/cluster/azure-aks/test.tftest.hcl
index cc3c38ce1..ebbe0a5da 100644
--- a/terraform/cluster/azure-aks/test.tftest.hcl
+++ b/terraform/cluster/azure-aks/test.tftest.hcl
@@ -102,12 +102,12 @@ run "full_configuration" {
command = plan
variables {
- context_id = "test"
- name = "windsor-aks"
- cluster_name = "test-cluster"
- resource_group_name = "test-rg"
- kubernetes_version = "1.32"
- oidc_issuer_enabled = true
+ context_id = "test"
+ name = "windsor-aks"
+ cluster_name = "test-cluster"
+ resource_group_name = "test-rg"
+ kubernetes_version = "1.32"
+ oidc_issuer_enabled = true
workload_identity_enabled = true
default_node_pool = {
name = "system"
From b4abe4ee48ad31100d5a5ecba65b2533a33bc3c7 Mon Sep 17 00:00:00 2001
From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
Date: Mon, 8 Dec 2025 12:00:40 -0500
Subject: [PATCH 4/7] Add enable_volume_snapshots var
Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
---
terraform/cluster/azure-aks/test.tftest.hcl | 54 +++++++++++++++++++++
terraform/cluster/azure-aks/variables.tf | 6 +++
2 files changed, 60 insertions(+)
diff --git a/terraform/cluster/azure-aks/test.tftest.hcl b/terraform/cluster/azure-aks/test.tftest.hcl
index ebbe0a5da..ae6a90080 100644
--- a/terraform/cluster/azure-aks/test.tftest.hcl
+++ b/terraform/cluster/azure-aks/test.tftest.hcl
@@ -94,6 +94,16 @@ run "minimal_configuration" {
condition = azurerm_kubernetes_cluster.main.workload_identity_enabled == true
error_message = "Workload Identity should be enabled by default"
}
+
+ assert {
+ condition = contains(azurerm_role_definition.aks_kubelet_vmss_disk_manager.permissions[0].actions, "Microsoft.Compute/snapshots/read")
+ error_message = "Snapshot permissions should be included when enable_volume_snapshots is true (default)"
+ }
+
+ assert {
+ condition = contains(azurerm_role_definition.aks_kubelet_vmss_disk_manager.permissions[0].actions, "Microsoft.Compute/snapshots/write")
+ error_message = "Snapshot write permissions should be included when enable_volume_snapshots is true (default)"
+ }
}
# Tests a full configuration with all optional variables explicitly set,
@@ -135,6 +145,7 @@ run "full_configuration" {
private_cluster_enabled = false
azure_policy_enabled = true
local_account_disabled = false
+ enable_volume_snapshots = true
}
assert {
@@ -226,6 +237,16 @@ run "full_configuration" {
condition = azurerm_kubernetes_cluster.main.workload_identity_enabled == true
error_message = "Workload Identity should be enabled"
}
+
+ assert {
+ condition = contains(azurerm_role_definition.aks_kubelet_vmss_disk_manager.permissions[0].actions, "Microsoft.Compute/snapshots/read")
+ error_message = "Snapshot permissions should be included when enable_volume_snapshots is true"
+ }
+
+ assert {
+ condition = contains(azurerm_role_definition.aks_kubelet_vmss_disk_manager.permissions[0].actions, "Microsoft.Compute/snapshots/write")
+ error_message = "Snapshot write permissions should be included when enable_volume_snapshots is true"
+ }
}
# Tests the private cluster configuration, ensuring that enabling the private_cluster_enabled
@@ -295,3 +316,36 @@ run "multiple_invalid_inputs" {
kubernetes_version = "v1.32"
}
}
+
+# Tests that when enable_volume_snapshots is false, snapshot permissions are not included in the role definition.
+# This verifies the conditional logic that excludes snapshot operations when volume snapshots are disabled.
+run "volume_snapshots_disabled" {
+ command = plan
+
+ variables {
+ context_id = "test"
+ name = "windsor-aks"
+ kubernetes_version = "1.32"
+ enable_volume_snapshots = false
+ }
+
+ assert {
+ condition = !contains(azurerm_role_definition.aks_kubelet_vmss_disk_manager.permissions[0].actions, "Microsoft.Compute/snapshots/read")
+ error_message = "Snapshot read permissions should not be included when enable_volume_snapshots is false"
+ }
+
+ assert {
+ condition = !contains(azurerm_role_definition.aks_kubelet_vmss_disk_manager.permissions[0].actions, "Microsoft.Compute/snapshots/write")
+ error_message = "Snapshot write permissions should not be included when enable_volume_snapshots is false"
+ }
+
+ assert {
+ condition = !contains(azurerm_role_definition.aks_kubelet_vmss_disk_manager.permissions[0].actions, "Microsoft.Compute/snapshots/delete")
+ error_message = "Snapshot delete permissions should not be included when enable_volume_snapshots is false"
+ }
+
+ assert {
+ condition = contains(azurerm_role_definition.aks_kubelet_vmss_disk_manager.permissions[0].actions, "Microsoft.Compute/disks/read")
+ error_message = "Core disk permissions should still be included when enable_volume_snapshots is false"
+ }
+}
diff --git a/terraform/cluster/azure-aks/variables.tf b/terraform/cluster/azure-aks/variables.tf
index a2f149115..ae6203aaa 100644
--- a/terraform/cluster/azure-aks/variables.tf
+++ b/terraform/cluster/azure-aks/variables.tf
@@ -235,6 +235,12 @@ variable "endpoint_private_access" {
default = false
}
+variable "enable_volume_snapshots" {
+ description = "Enable volume snapshot permissions for the kubelet identity. Set to false to use minimal permissions if volume snapshots are not needed."
+ type = bool
+ default = true
+}
+
variable "oidc_issuer_enabled" {
description = "Enable OIDC issuer for the AKS cluster"
type = bool
From cdb8a483d1654da1e0df979e85145b99b1a0005e Mon Sep 17 00:00:00 2001
From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
Date: Mon, 8 Dec 2025 12:02:56 -0500
Subject: [PATCH 5/7] fmt
Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
---
terraform/cluster/azure-aks/test.tftest.hcl | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/terraform/cluster/azure-aks/test.tftest.hcl b/terraform/cluster/azure-aks/test.tftest.hcl
index ae6a90080..5c02fa529 100644
--- a/terraform/cluster/azure-aks/test.tftest.hcl
+++ b/terraform/cluster/azure-aks/test.tftest.hcl
@@ -145,7 +145,7 @@ run "full_configuration" {
private_cluster_enabled = false
azure_policy_enabled = true
local_account_disabled = false
- enable_volume_snapshots = true
+ enable_volume_snapshots = true
}
assert {
@@ -323,9 +323,9 @@ run "volume_snapshots_disabled" {
command = plan
variables {
- context_id = "test"
- name = "windsor-aks"
- kubernetes_version = "1.32"
+ context_id = "test"
+ name = "windsor-aks"
+ kubernetes_version = "1.32"
enable_volume_snapshots = false
}
From 34841d26925f7b5e30a8c10baadfa8981b91d10f Mon Sep 17 00:00:00 2001
From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
Date: Mon, 8 Dec 2025 12:03:34 -0500
Subject: [PATCH 6/7] checkov
Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
---
terraform/cluster/azure-aks/main.tf | 1 +
1 file changed, 1 insertion(+)
diff --git a/terraform/cluster/azure-aks/main.tf b/terraform/cluster/azure-aks/main.tf
index e23bdb700..ae3f9525b 100644
--- a/terraform/cluster/azure-aks/main.tf
+++ b/terraform/cluster/azure-aks/main.tf
@@ -228,6 +228,7 @@ resource "azurerm_kubernetes_cluster" "main" {
resource_group_name = azurerm_resource_group.aks.name
dns_prefix = local.cluster_name
# checkov:skip=CKV_AZURE_339: Kubernetes version is populated from the cloud provider's stable version via Renovate.
+ # checkov:skip=CKV_AZURE_4: Log Analytics workspace is created but diagnostic settings are configured separately or via alternative monitoring solutions
kubernetes_version = var.kubernetes_version
role_based_access_control_enabled = var.role_based_access_control_enabled
automatic_upgrade_channel = var.automatic_upgrade_channel
From 335d36b3828f5d77a3fadfaec7ef95579927f02e Mon Sep 17 00:00:00 2001
From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
Date: Mon, 8 Dec 2025 12:16:12 -0500
Subject: [PATCH 7/7] clean test
Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com>
---
terraform/cluster/azure-aks/test.tftest.hcl | 75 ---------------------
1 file changed, 75 deletions(-)
diff --git a/terraform/cluster/azure-aks/test.tftest.hcl b/terraform/cluster/azure-aks/test.tftest.hcl
index e11e49c3c..e8af7a395 100644
--- a/terraform/cluster/azure-aks/test.tftest.hcl
+++ b/terraform/cluster/azure-aks/test.tftest.hcl
@@ -22,15 +22,6 @@ mock_provider "azurerm" {
id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Network/virtualNetworks/vnet-test/subnets/subnet-test"
}
}
- mock_resource "azurerm_kubernetes_cluster" {
- defaults = {
- kubelet_identity = [{
- client_id = "44444444-4444-4444-4444-444444444444"
- object_id = "55555555-5555-5555-5555-555555555555"
- user_assigned_identity_id = ""
- }]
- }
- }
}
@@ -45,17 +36,6 @@ run "minimal_configuration" {
kubernetes_version = "1.32"
}
- override_resource {
- target = azurerm_kubernetes_cluster.main
- values = {
- kubelet_identity = [{
- client_id = "44444444-4444-4444-4444-444444444444"
- object_id = "55555555-5555-5555-5555-555555555555"
- user_assigned_identity_id = ""
- }]
- }
- }
-
assert {
condition = azurerm_kubernetes_cluster.main.name == "windsor-aks-test"
error_message = "Cluster name should default to 'windsor-aks-test' when cluster_name is omitted"
@@ -187,17 +167,6 @@ run "full_configuration" {
enable_volume_snapshots = true
}
- override_resource {
- target = azurerm_kubernetes_cluster.main
- values = {
- kubelet_identity = [{
- client_id = "44444444-4444-4444-4444-444444444444"
- object_id = "55555555-5555-5555-5555-555555555555"
- user_assigned_identity_id = ""
- }]
- }
- }
-
assert {
condition = azurerm_kubernetes_cluster.main.name == "test-cluster"
error_message = "Cluster name should match input"
@@ -337,17 +306,6 @@ run "private_cluster" {
kubernetes_version = "1.32"
}
- override_resource {
- target = azurerm_kubernetes_cluster.main
- values = {
- kubelet_identity = [{
- client_id = "44444444-4444-4444-4444-444444444444"
- object_id = "55555555-5555-5555-5555-555555555555"
- user_assigned_identity_id = ""
- }]
- }
- }
-
assert {
condition = azurerm_kubernetes_cluster.main.private_cluster_enabled == true
error_message = "Private cluster should be enabled"
@@ -366,17 +324,6 @@ run "config_file_created" {
context_path = "/tmp"
}
- override_resource {
- target = azurerm_kubernetes_cluster.main
- values = {
- kubelet_identity = [{
- client_id = "44444444-4444-4444-4444-444444444444"
- object_id = "55555555-5555-5555-5555-555555555555"
- user_assigned_identity_id = ""
- }]
- }
- }
-
assert {
condition = length(local_file.kube_config) >= 1
error_message = "Kubeconfig file should be generated when context path is provided"
@@ -392,17 +339,6 @@ run "network_configuration" {
dns_service_ip = "10.0.0.10"
}
- override_resource {
- target = azurerm_kubernetes_cluster.main
- values = {
- kubelet_identity = [{
- client_id = "44444444-4444-4444-4444-444444444444"
- object_id = "55555555-5555-5555-5555-555555555555"
- user_assigned_identity_id = ""
- }]
- }
- }
-
assert {
condition = azurerm_kubernetes_cluster.main.network_profile[0].service_cidr == "10.0.0.0/16"
error_message = "Service CIDR should match input value"
@@ -439,17 +375,6 @@ run "volume_snapshots_disabled" {
enable_volume_snapshots = false
}
- override_resource {
- target = azurerm_kubernetes_cluster.main
- values = {
- kubelet_identity = [{
- client_id = "44444444-4444-4444-4444-444444444444"
- object_id = "55555555-5555-5555-5555-555555555555"
- user_assigned_identity_id = ""
- }]
- }
- }
-
assert {
condition = !contains(azurerm_role_definition.aks_kubelet_vmss_disk_manager.permissions[0].actions, "Microsoft.Compute/snapshots/read")
error_message = "Snapshot read permissions should not be included when enable_volume_snapshots is false"