diff --git a/terraform/cluster/azure-aks/main.tf b/terraform/cluster/azure-aks/main.tf index 931b7c28..aab7f188 100644 --- a/terraform/cluster/azure-aks/main.tf +++ b/terraform/cluster/azure-aks/main.tf @@ -28,16 +28,30 @@ provider "azurerm" { } } +#----------------------------------------------------------------------------------------------------------------------- +# Data Sources +#----------------------------------------------------------------------------------------------------------------------- + data "azurerm_client_config" "current" {} +data "azurerm_subnet" "private" { + count = var.vnet_subnet_id == null ? 1 : 0 + name = "private-1-${var.context_id}" + resource_group_name = "${var.vnet_module_name}-${var.context_id}" + virtual_network_name = "${var.vnet_module_name}-${var.context_id}" +} + #----------------------------------------------------------------------------------------------------------------------- # Locals #----------------------------------------------------------------------------------------------------------------------- locals { kubeconfig_path = "${var.context_path}/.kube/config" - rg_name = var.resource_group_name == null ? "windsor-aks-rg-${var.context_id}" : var.resource_group_name - cluster_name = var.cluster_name == null ? "windsor-aks-cluster-${var.context_id}" : var.cluster_name + 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 + tags = merge({ + WindsorContextID = var.context_id + }, var.tags) } #----------------------------------------------------------------------------------------------------------------------- @@ -47,6 +61,9 @@ locals { resource "azurerm_resource_group" "aks" { name = local.rg_name location = var.region + tags = merge({ + Name = local.rg_name + }, local.tags) } #----------------------------------------------------------------------------------------------------------------------- @@ -61,7 +78,7 @@ resource "random_string" "key" { resource "azurerm_key_vault" "key_vault" { # checkov:skip=CKV2_AZURE_32: We are using a public cluster for testing, there is no need for private endpoints. - name = "keyvault-${var.context_id}-${random_string.key.result}" + name = "${var.name}-${var.context_id}-${random_string.key.result}" location = azurerm_resource_group.aks.location resource_group_name = azurerm_resource_group.aks.name tenant_id = data.azurerm_client_config.current.tenant_id @@ -79,6 +96,9 @@ resource "azurerm_key_vault" "key_vault" { default_action = var.network_acls_default_action bypass = "AzureServices" } + tags = merge({ + Name = "${var.name}-${var.context_id}-${random_string.key.result}" + }, local.tags) } resource "azurerm_key_vault_access_policy" "key_vault_access_policy" { @@ -127,7 +147,7 @@ resource "azurerm_key_vault_access_policy" "key_vault_access_policy_disk" { resource "time_static" "expiry" {} resource "azurerm_key_vault_key" "key_vault_key" { - name = "key-${var.context_id}-${random_string.key.result}" + name = "${var.name}-${var.context_id}-${random_string.key.result}" key_vault_id = azurerm_key_vault.key_vault.id key_type = "RSA-HSM" key_size = 2048 @@ -157,7 +177,7 @@ resource "azurerm_key_vault_key" "key_vault_key" { } resource "azurerm_disk_encryption_set" "main" { - name = "des-${var.context_id}-${random_string.key.result}" + name = "${var.name}-${var.context_id}-${random_string.key.result}" resource_group_name = azurerm_resource_group.aks.name location = azurerm_resource_group.aks.location key_vault_key_id = azurerm_key_vault_key.key_vault_key.id @@ -172,28 +192,27 @@ resource "azurerm_disk_encryption_set" "main" { #----------------------------------------------------------------------------------------------------------------------- resource "azurerm_log_analytics_workspace" "aks_logs" { - name = "aks-logs-${var.context_id}" + name = "${var.name}-${var.context_id}" location = azurerm_resource_group.aks.location resource_group_name = azurerm_resource_group.aks.name sku = "PerGB2018" retention_in_days = 30 + tags = merge({ + Name = "${var.name}-${var.context_id}" + }, local.tags) } #----------------------------------------------------------------------------------------------------------------------- # AKS Cluster #----------------------------------------------------------------------------------------------------------------------- -data "azurerm_subnet" "private" { - count = var.vnet_subnet_id == null ? 1 : 0 - name = "${var.context_id}-private-1" - resource_group_name = var.vnet_resource_group_name == null ? "windsor-vnet-rg-${var.context_id}" : var.vnet_resource_group_name - virtual_network_name = var.vnet_name == null ? "windsor-vnet-${var.context_id}" : var.vnet_name -} - resource "azurerm_user_assigned_identity" "cluster" { - name = "${var.context_id}-cluster-identity" + 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" { @@ -226,7 +245,7 @@ resource "azurerm_kubernetes_cluster" "main" { vm_size = var.default_node_pool.vm_size 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 = true + 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 os_disk_type = var.default_node_pool.os_disk_type host_encryption_enabled = var.default_node_pool.host_encryption_enabled @@ -280,6 +299,9 @@ resource "azurerm_kubernetes_cluster" "main" { workload_autoscaler_profile ] } + tags = merge({ + Name = local.cluster_name + }, local.tags) } resource "azurerm_kubernetes_cluster_node_pool" "autoscaled" { @@ -304,10 +326,12 @@ resource "azurerm_kubernetes_cluster_node_pool" "autoscaled" { upgrade_settings ] } + tags = merge({ + Name = var.autoscaled_node_pool.name + }, local.tags) } resource "local_file" "kube_config" { - count = var.context_path != "" ? 1 : 0 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 fb1e90d7..47c82b4c 100644 --- a/terraform/cluster/azure-aks/test.tftest.hcl +++ b/terraform/cluster/azure-aks/test.tftest.hcl @@ -19,16 +19,17 @@ run "minimal_configuration" { variables { context_id = "test" + name = "windsor-aks" } assert { - condition = azurerm_kubernetes_cluster.main.name == "windsor-aks-cluster-test" - error_message = "Cluster name should default to 'windsor-aks-cluster-test' when cluster_name is omitted" + condition = azurerm_kubernetes_cluster.main.name == "windsor-aks-test" + error_message = "Cluster name should default to 'windsor-aks-test' when cluster_name is omitted" } assert { - condition = azurerm_resource_group.aks.name == "windsor-aks-rg-test" - error_message = "Resource group name should default to 'windsor-aks-rg-test' when resource_group_name is omitted" + condition = azurerm_resource_group.aks.name == "windsor-aks-test" + error_message = "Resource group name should default to 'windsor-aks-test' when resource_group_name is omitted" } assert { @@ -74,18 +75,20 @@ run "full_configuration" { variables { context_id = "test" + name = "windsor-aks" cluster_name = "test-cluster" resource_group_name = "test-rg" kubernetes_version = "1.32" default_node_pool = { - name = "system" - vm_size = "Standard_D2s_v3" - os_disk_type = "Managed" - max_pods = 30 - host_encryption_enabled = true - min_count = 1 - max_count = 3 - node_count = 1 + name = "system" + vm_size = "Standard_D2s_v3" + os_disk_type = "Managed" + max_pods = 30 + host_encryption_enabled = true + min_count = 1 + max_count = 3 + node_count = 1 + only_critical_addons_enabled = false } autoscaled_node_pool = { enabled = true @@ -187,6 +190,7 @@ run "private_cluster" { variables { context_id = "test" + name = "windsor-aks" cluster_name = "test-cluster" private_cluster_enabled = true } @@ -197,19 +201,20 @@ run "private_cluster" { } } -# Verifies that no kubeconfig file is generated when context_path is empty, -# preventing unnecessary file creation in the root directory. -run "no_config_files" { +# Verifies that a kubeconfig file is generated, +# ensuring proper cluster access configuration. +run "config_file_created" { command = plan variables { context_id = "test" + name = "windsor-aks" cluster_name = "test-cluster" - context_path = "" + context_path = "/tmp" } assert { - condition = length(local_file.kube_config) == 0 - error_message = "No kubeconfig file should be generated without context path" + condition = length(local_file.kube_config) >= 1 + error_message = "Kubeconfig file should be generated when context path is provided" } } diff --git a/terraform/cluster/azure-aks/variables.tf b/terraform/cluster/azure-aks/variables.tf index a3b65025..f6895d8e 100644 --- a/terraform/cluster/azure-aks/variables.tf +++ b/terraform/cluster/azure-aks/variables.tf @@ -1,6 +1,23 @@ #----------------------------------------------------------------------------------------------------------------------- # Variables #----------------------------------------------------------------------------------------------------------------------- +variable "context_path" { + type = string + description = "The path to the context folder, where kubeconfig is stored" + default = "" +} + +variable "context_id" { + description = "Context ID for the resources" + type = string + default = null +} + +variable "name" { + description = "Name of the resource" + type = string + default = "cluster" +} variable "resource_group_name" { description = "Name of the resource group" @@ -8,16 +25,16 @@ variable "resource_group_name" { default = null } -variable "vnet_resource_group_name" { - description = "Name of the VNET resource group" +variable "cluster_name" { + description = "Name of the AKS cluster" type = string default = null } -variable "vnet_name" { - description = "Name of the VNET" +variable "vnet_module_name" { + description = "Name on the VNET module" type = string - default = null + default = "network" } variable "vnet_subnet_id" { @@ -32,51 +49,35 @@ variable "region" { default = "eastus" } -variable "cluster_name" { - description = "Name of the AKS cluster" - type = string - default = null -} - variable "kubernetes_version" { description = "Version of Kubernetes to use" type = string default = "1.32" } -variable "context_path" { - type = string - description = "The path to the context folder, where kubeconfig is stored" - default = "" -} - -variable "context_id" { - description = "Context ID for the resources" - type = string - default = null -} - variable "default_node_pool" { description = "Configuration for the default node pool" type = object({ - name = string - vm_size = string - os_disk_type = string - max_pods = number - host_encryption_enabled = bool - min_count = number - max_count = number - node_count = number + name = string + vm_size = string + os_disk_type = string + max_pods = number + host_encryption_enabled = bool + min_count = number + max_count = number + node_count = number + only_critical_addons_enabled = bool }) default = { - name = "system" - vm_size = "Standard_D2s_v3" - os_disk_type = "Managed" - max_pods = 30 - host_encryption_enabled = true - min_count = 1 - max_count = 3 - node_count = 1 + name = "system" + vm_size = "Standard_D2s_v3" + os_disk_type = "Managed" + max_pods = 30 + host_encryption_enabled = true + min_count = 1 + max_count = 3 + node_count = 1 + only_critical_addons_enabled = true } } @@ -209,3 +210,9 @@ variable "soft_delete_retention_days" { description = "The number of days to retain the AKS cluster's key vault" default = 7 } + +variable "tags" { + description = "Tags to apply to the resources" + type = map(string) + default = {} +} diff --git a/terraform/network/azure-vnet/main.tf b/terraform/network/azure-vnet/main.tf index 2e7754c4..73902dd6 100644 --- a/terraform/network/azure-vnet/main.tf +++ b/terraform/network/azure-vnet/main.tf @@ -25,8 +25,11 @@ provider "azurerm" { #----------------------------------------------------------------------------------------------------------------------- locals { - vnet_name = var.vnet_name == null ? "windsor-vnet-${var.context_id}" : var.vnet_name - rg_name = var.resource_group_name == null ? "windsor-vnet-rg-${var.context_id}" : var.resource_group_name + vnet_name = var.vnet_name == null ? "${var.name}-${var.context_id}" : var.vnet_name + rg_name = var.resource_group_name == null ? "${var.name}-${var.context_id}" : var.resource_group_name + tags = merge({ + WindsorContextID = var.context_id + }, var.tags) } #----------------------------------------------------------------------------------------------------------------------- @@ -36,6 +39,9 @@ locals { resource "azurerm_resource_group" "main" { name = local.rg_name location = var.region + tags = merge({ + Name = local.rg_name + }, local.tags) } #----------------------------------------------------------------------------------------------------------------------- @@ -47,6 +53,9 @@ resource "azurerm_virtual_network" "main" { address_space = [var.vnet_cidr] location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name + tags = merge({ + Name = local.vnet_name + }, local.tags) } #----------------------------------------------------------------------------------------------------------------------- @@ -56,7 +65,7 @@ resource "azurerm_virtual_network" "main" { # Public subnets resource "azurerm_subnet" "public" { count = length(var.vnet_subnets["public"]) > 0 ? length(var.vnet_subnets["public"]) : var.vnet_zones - name = "${var.context_id}-public-${count.index + 1}" + name = "public-${count.index + 1}-${var.context_id}" resource_group_name = azurerm_resource_group.main.name virtual_network_name = azurerm_virtual_network.main.name address_prefixes = length(var.vnet_subnets["public"]) > 0 ? [var.vnet_subnets["public"][count.index]] : ["${join(".", slice(split(".", var.vnet_cidr), 0, 2))}.${count.index + 1}.0/24"] @@ -65,19 +74,19 @@ resource "azurerm_subnet" "public" { # Private subnets resource "azurerm_subnet" "private" { count = length(var.vnet_subnets["private"]) > 0 ? length(var.vnet_subnets["private"]) : var.vnet_zones - name = "${var.context_id}-private-${count.index + 1}" + name = "private-${count.index + 1}-${var.context_id}" resource_group_name = azurerm_resource_group.main.name virtual_network_name = azurerm_virtual_network.main.name address_prefixes = length(var.vnet_subnets["private"]) > 0 ? [var.vnet_subnets["private"][count.index]] : ["${join(".", slice(split(".", var.vnet_cidr), 0, 2))}.1${count.index + 1}.0/24"] } -# Data subnets -resource "azurerm_subnet" "data" { - count = length(var.vnet_subnets["data"]) > 0 ? length(var.vnet_subnets["data"]) : var.vnet_zones - name = "${var.context_id}-data-${count.index + 1}" +# Isolated subnets +resource "azurerm_subnet" "isolated" { + count = length(var.vnet_subnets["isolated"]) > 0 ? length(var.vnet_subnets["isolated"]) : var.vnet_zones + name = "isolated-${count.index + 1}-${var.context_id}" resource_group_name = azurerm_resource_group.main.name virtual_network_name = azurerm_virtual_network.main.name - address_prefixes = length(var.vnet_subnets["data"]) > 0 ? [var.vnet_subnets["data"][count.index]] : ["${join(".", slice(split(".", var.vnet_cidr), 0, 2))}.2${count.index + 1}.0/24"] + address_prefixes = length(var.vnet_subnets["isolated"]) > 0 ? [var.vnet_subnets["isolated"][count.index]] : ["${join(".", slice(split(".", var.vnet_cidr), 0, 2))}.2${count.index + 1}.0/24"] } #----------------------------------------------------------------------------------------------------------------------- @@ -87,20 +96,26 @@ resource "azurerm_subnet" "data" { # Public IP for NAT Gateway resource "azurerm_public_ip" "nat" { count = var.vnet_zones - name = "${var.context_id}-nat-gw-ip-${count.index + 1}" + name = "${var.name}-${count.index + 1}-${var.context_id}" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name allocation_method = "Static" sku = "Standard" + tags = merge({ + Name = "${var.name}-${count.index + 1}-${var.context_id}" + }, local.tags) } # NAT Gateway resource "azurerm_nat_gateway" "main" { count = var.vnet_zones - name = "${var.context_id}-nat-gw-${count.index + 1}" + name = "${var.name}-${count.index + 1}-${var.context_id}" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name sku_name = "Standard" + tags = merge({ + Name = "${var.name}-${count.index + 1}-${var.context_id}" + }, local.tags) } # Associate public IP with NAT Gateway @@ -116,10 +131,3 @@ resource "azurerm_subnet_nat_gateway_association" "private" { subnet_id = azurerm_subnet.private[count.index].id nat_gateway_id = azurerm_nat_gateway.main[count.index].id } - -# Associate NAT Gateway with data subnet -resource "azurerm_subnet_nat_gateway_association" "data" { - count = var.vnet_zones - subnet_id = azurerm_subnet.data[count.index].id - nat_gateway_id = azurerm_nat_gateway.main[count.index].id -} diff --git a/terraform/network/azure-vnet/outputs.tf b/terraform/network/azure-vnet/outputs.tf index d12453ef..7134121e 100644 --- a/terraform/network/azure-vnet/outputs.tf +++ b/terraform/network/azure-vnet/outputs.tf @@ -14,7 +14,7 @@ # value = azurerm_subnet.private[*].id # } -# output "data_subnet_ids" { -# description = "IDs of created data subnets" -# value = azurerm_subnet.data[*].id +# output "isolated_subnet_ids" { +# description = "IDs of created isolated subnets" +# value = azurerm_subnet.isolated[*].id # } diff --git a/terraform/network/azure-vnet/test.tftest.hcl b/terraform/network/azure-vnet/test.tftest.hcl index 72658c8e..7483ba0c 100644 --- a/terraform/network/azure-vnet/test.tftest.hcl +++ b/terraform/network/azure-vnet/test.tftest.hcl @@ -7,10 +7,11 @@ run "minimal_configuration" { variables { context_id = "test" + name = "windsor-vnet" } assert { - condition = azurerm_resource_group.main.name == "windsor-vnet-rg-test" + condition = azurerm_resource_group.main.name == "windsor-vnet-test" error_message = "Resource group name should follow default naming convention" } @@ -35,8 +36,8 @@ run "minimal_configuration" { } assert { - condition = length(azurerm_subnet.data) == 1 - error_message = "One data subnet should be created by default" + condition = length(azurerm_subnet.isolated) == 1 + error_message = "One isolated subnet should be created by default" } assert { @@ -57,11 +58,12 @@ run "full_configuration" { vnet_zones = 2 vnet_cidr = "10.30.0.0/16" vnet_subnets = { - public = ["10.30.1.0/24", "10.30.2.0/24"] - private = ["10.30.11.0/24", "10.30.12.0/24"] - data = ["10.30.21.0/24", "10.30.22.0/24"] + public = ["10.30.1.0/24", "10.30.2.0/24"] + private = ["10.30.11.0/24", "10.30.12.0/24"] + isolated = ["10.30.21.0/24", "10.30.22.0/24"] } context_id = "test" + name = "custom" } assert { @@ -90,8 +92,8 @@ run "full_configuration" { } assert { - condition = length(azurerm_subnet.data) == 2 - error_message = "Two data subnets should be created" + condition = length(azurerm_subnet.isolated) == 2 + error_message = "Two isolated subnets should be created" } assert { diff --git a/terraform/network/azure-vnet/variables.tf b/terraform/network/azure-vnet/variables.tf index 39ad62e6..a23ab8ab 100644 --- a/terraform/network/azure-vnet/variables.tf +++ b/terraform/network/azure-vnet/variables.tf @@ -1,11 +1,23 @@ # Variables +variable "context_id" { + description = "Context ID for the resources" + type = string + default = null +} + variable "region" { description = "Region for the resources" type = string default = "eastus" } +variable "name" { + description = "Name of the resource" + type = string + default = "network" +} + variable "resource_group_name" { description = "Name of the resource group" type = string @@ -18,12 +30,6 @@ variable "vnet_name" { default = null } -variable "vnet_zones" { - description = "Number of availability zones to create" - type = number - default = 1 -} - variable "vnet_cidr" { description = "CIDR block for VNET" type = string @@ -36,17 +42,24 @@ variable "vnet_subnets" { # example: { # public = ["10.20.1.0/24", "10.20.2.0/24", "10.20.3.0/24"] # private = ["10.20.11.0/24", "10.20.12.0/24", "10.20.13.0/24"] - # data = ["10.20.21.0/24", "10.20.22.0/24", "10.20.23.0/24"] + # isolated = ["10.20.21.0/24", "10.20.22.0/24", "10.20.23.0/24"] # } default = { - public = [] - private = [] - data = [] + public = [] + private = [] + isolated = [] } } -variable "context_id" { - description = "Context ID for the resources" - type = string - default = null +# Only used if vnet_subnets is not defined +variable "vnet_zones" { + description = "Number of availability zones to create" + type = number + default = 1 +} + +variable "tags" { + description = "Tags to apply to the resources" + type = map(string) + default = {} }