diff --git a/docs/terraform/cluster/azure-aks.md b/docs/terraform/cluster/azure-aks.md index c7f949ab..06e23dc3 100644 --- a/docs/terraform/cluster/azure-aks.md +++ b/docs/terraform/cluster/azure-aks.md @@ -52,12 +52,6 @@ No modules. | [azurerm_kubernetes_cluster_node_pool.autoscaled](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster_node_pool) | resource | | [azurerm_log_analytics_workspace.aks_logs](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/log_analytics_workspace) | resource | | [azurerm_resource_group.aks](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | -| [azurerm_role_assignment.aks_network_contributor](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | -| [azurerm_role_assignment.aks_vmss_contributor](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | -| [azurerm_role_assignment.azurerm_disk_encryption_set_key_vault_access](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | -| [azurerm_role_assignment.control_plane_managed_identity_operator_on_kubelet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | -| [azurerm_role_assignment.des_reader](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | -| [azurerm_user_assigned_identity.cluster](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/user_assigned_identity) | resource | | [local_file.kube_config](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | | [random_string.key](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | | [time_static.expiry](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/static) | resource | @@ -68,7 +62,6 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [additional\_cluster\_identity\_ids](#input\_additional\_cluster\_identity\_ids) | Additional user assigned identity IDs for the AKS cluster | `list(string)` | `[]` | no | | [auto\_scaler\_profile](#input\_auto\_scaler\_profile) | Configuration for the AKS cluster's auto-scaler |
object({
balance_similar_node_groups = bool
max_graceful_termination_sec = number
scale_down_delay_after_add = string
scale_down_delay_after_delete = string
scale_down_delay_after_failure = string
scan_interval = string
scale_down_unneeded = string
scale_down_unready = string
scale_down_utilization_threshold = string
}) | {
"balance_similar_node_groups": true,
"max_graceful_termination_sec": 600,
"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": "20m",
"scale_down_utilization_threshold": "0.5",
"scan_interval": "10s"
} | no |
| [automatic\_upgrade\_channel](#input\_automatic\_upgrade\_channel) | The automatic upgrade channel for the AKS cluster | `string` | `"stable"` | no |
| [autoscaled\_node\_pool](#input\_autoscaled\_node\_pool) | Configuration for the autoscaled node pool | object({
enabled = bool
name = string
vm_size = string
mode = string
os_disk_type = string
max_pods = number
host_encryption_enabled = bool
min_count = number
max_count = number
}) | {
"enabled": true,
"host_encryption_enabled": true,
"max_count": 3,
"max_pods": 110,
"min_count": 1,
"mode": "User",
"name": "autoscaled",
"os_disk_type": "Managed",
"vm_size": "Standard_D2s_v3"
} | no |
@@ -80,6 +73,9 @@ No modules.
| [dns\_service\_ip](#input\_dns\_service\_ip) | IP address for Kubernetes DNS service | `string` | `"10.96.0.10"` | no |
| [endpoint\_private\_access](#input\_endpoint\_private\_access) | Whether to enable private access to the Kubernetes API server | `bool` | `false` | no |
| [expiration\_date](#input\_expiration\_date) | The expiration date for the AKS cluster's key vault | `string` | `null` | no |
+| [kubelet\_client\_id](#input\_kubelet\_client\_id) | Client ID of the user-assigned identity to use for the kubelet. If not provided, the cluster will use the system-assigned identity. | `string` | `null` | no |
+| [kubelet\_object\_id](#input\_kubelet\_object\_id) | Object ID of the user-assigned identity to use for the kubelet. If not provided, the cluster will use the system-assigned identity. | `string` | `null` | no |
+| [kubelet\_user\_assigned\_identity\_id](#input\_kubelet\_user\_assigned\_identity\_id) | Resource ID of the user-assigned identity to use for the kubelet. If not provided, the cluster will use the system-assigned identity. | `string` | `null` | no |
| [kubernetes\_version](#input\_kubernetes\_version) | Version of Kubernetes to use | `string` | `"1.32"` | no |
| [local\_account\_disabled](#input\_local\_account\_disabled) | Whether to disable local accounts for the AKS cluster | `bool` | `false` | no |
| [name](#input\_name) | Name of the resource | `string` | `"cluster"` | no |
@@ -93,6 +89,7 @@ No modules.
| [sku\_tier](#input\_sku\_tier) | The SKU tier for the AKS cluster | `string` | `"Standard"` | no |
| [soft\_delete\_retention\_days](#input\_soft\_delete\_retention\_days) | The number of days to retain the AKS cluster's key vault | `number` | `7` | no |
| [tags](#input\_tags) | Tags to apply to the resources | `map(string)` | `{}` | no |
+| [user\_assigned\_identity\_ids](#input\_user\_assigned\_identity\_ids) | User assigned identity IDs for the AKS cluster. If provided, the cluster will use only user-assigned identities. | `list(string)` | `[]` | no |
| [vnet\_module\_name](#input\_vnet\_module\_name) | Name on the VNET module | `string` | `"network"` | no |
| [vnet\_subnet\_id](#input\_vnet\_subnet\_id) | ID of the subnet | `string` | `null` | no |
| [workload\_autoscaler\_profile](#input\_workload\_autoscaler\_profile) | Configuration for the AKS cluster's workload autoscaler | object({
keda_enabled = bool
vertical_pod_autoscaler_enabled = bool
}) | {
"keda_enabled": false,
"vertical_pod_autoscaler_enabled": false
} | no |
diff --git a/terraform/cluster/azure-aks/main.tf b/terraform/cluster/azure-aks/main.tf
index 1866192b..e9891848 100644
--- a/terraform/cluster/azure-aks/main.tf
+++ b/terraform/cluster/azure-aks/main.tf
@@ -206,15 +206,6 @@ resource "azurerm_log_analytics_workspace" "aks_logs" {
# AKS Cluster
#-----------------------------------------------------------------------------------------------------------------------
-resource "azurerm_user_assigned_identity" "cluster" {
- name = "${var.name}-${var.context_id}"
- location = var.region
- resource_group_name = azurerm_resource_group.aks.name
- tags = merge({
- Name = "${var.name}-${var.context_id}"
- }, local.tags)
-}
-
resource "azurerm_kubernetes_cluster" "main" {
name = local.cluster_name
location = azurerm_resource_group.aks.location
@@ -246,12 +237,12 @@ 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
- # checkov:skip=CKV_AZURE_226: we are using the managed disk type to reduce costs
+ # 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
host_encryption_enabled = var.default_node_pool.host_encryption_enabled
- # checkov:skip=CKV_AZURE_168: This is set in the variable by default to 50
+ # checkov:skip=CKV_AZURE_168: This is set in the variable by default to 50
max_pods = var.default_node_pool.max_pods
temporary_name_for_rotation = "rotate"
}
@@ -285,17 +276,17 @@ resource "azurerm_kubernetes_cluster" "main" {
}
identity {
- type = "UserAssigned"
- identity_ids = concat(
- [azurerm_user_assigned_identity.cluster.id],
- var.additional_cluster_identity_ids
- )
+ type = length(var.user_assigned_identity_ids) > 0 ? "UserAssigned" : "SystemAssigned"
+ identity_ids = var.user_assigned_identity_ids
}
- kubelet_identity {
- client_id = azurerm_user_assigned_identity.cluster.client_id
- object_id = azurerm_user_assigned_identity.cluster.principal_id
- user_assigned_identity_id = azurerm_user_assigned_identity.cluster.id
+ 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
+ }
}
tags = merge({
@@ -330,33 +321,3 @@ resource "local_file" "kube_config" {
content = azurerm_kubernetes_cluster.main.kube_config_raw
filename = local.kubeconfig_path
}
-
-resource "azurerm_role_assignment" "aks_vmss_contributor" {
- scope = azurerm_resource_group.aks.id
- role_definition_name = "Virtual Machine Contributor"
- principal_id = azurerm_user_assigned_identity.cluster.principal_id
-}
-
-resource "azurerm_role_assignment" "azurerm_disk_encryption_set_key_vault_access" {
- scope = azurerm_key_vault.key_vault.id
- role_definition_name = "Key Vault Crypto Service Encryption User"
- principal_id = azurerm_user_assigned_identity.cluster.principal_id
-}
-
-resource "azurerm_role_assignment" "aks_network_contributor" {
- scope = azurerm_resource_group.aks.id
- role_definition_name = "Network Contributor"
- principal_id = azurerm_user_assigned_identity.cluster.principal_id
-}
-
-resource "azurerm_role_assignment" "des_reader" {
- scope = azurerm_disk_encryption_set.main.id
- role_definition_name = "Reader"
- principal_id = azurerm_user_assigned_identity.cluster.principal_id
-}
-
-resource "azurerm_role_assignment" "control_plane_managed_identity_operator_on_kubelet" {
- scope = azurerm_user_assigned_identity.cluster.id
- role_definition_name = "Managed Identity Operator"
- principal_id = azurerm_user_assigned_identity.cluster.principal_id
-}
diff --git a/terraform/cluster/azure-aks/test.tftest.hcl b/terraform/cluster/azure-aks/test.tftest.hcl
index 475bb70d..b0fc91bd 100644
--- a/terraform/cluster/azure-aks/test.tftest.hcl
+++ b/terraform/cluster/azure-aks/test.tftest.hcl
@@ -66,6 +66,11 @@ run "minimal_configuration" {
condition = azurerm_kubernetes_cluster.main.local_account_disabled == false
error_message = "Local accounts should be enabled by default"
}
+
+ assert {
+ condition = azurerm_kubernetes_cluster.main.identity[0].type == "SystemAssigned"
+ error_message = "Cluster should use system-assigned identity by default"
+ }
}
# Tests a full configuration with all optional variables explicitly set,
@@ -79,6 +84,13 @@ 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"
@@ -181,6 +193,31 @@ run "full_configuration" {
condition = azurerm_kubernetes_cluster.main.local_account_disabled == false
error_message = "Local accounts should be enabled"
}
+
+ 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"
+ }
+
+ assert {
+ condition = azurerm_kubernetes_cluster.main.kubelet_identity[0].object_id == "test-object-id"
+ error_message = "Kubelet object ID should match input"
+ }
+
+ 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"
+ }
}
# Tests the private cluster configuration, ensuring that enabling the private_cluster_enabled
diff --git a/terraform/cluster/azure-aks/variables.tf b/terraform/cluster/azure-aks/variables.tf
index dde63e97..1033f120 100644
--- a/terraform/cluster/azure-aks/variables.tf
+++ b/terraform/cluster/azure-aks/variables.tf
@@ -199,9 +199,9 @@ variable "expiration_date" {
default = null
}
-variable "additional_cluster_identity_ids" {
+variable "user_assigned_identity_ids" {
type = list(string)
- description = "Additional user assigned identity IDs for the AKS cluster"
+ description = "User assigned identity IDs for the AKS cluster. If provided, the cluster will use only user-assigned identities."
default = []
}
@@ -234,3 +234,21 @@ variable "endpoint_private_access" {
type = bool
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 "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
+}