main.tf (371 lines of code) (raw):

resource "random_string" "acr_suffix" { length = 8 numeric = true special = false upper = false } resource "azurerm_container_registry" "this" { location = var.location name = coalesce(var.container_registry_name, "cr${random_string.acr_suffix.result}") resource_group_name = var.resource_group_name sku = "Premium" tags = var.tags } resource "azurerm_role_assignment" "acr" { principal_id = azurerm_kubernetes_cluster.this.kubelet_identity[0].object_id scope = azurerm_container_registry.this.id role_definition_name = "AcrPull" skip_service_principal_aad_check = true } resource "azurerm_user_assigned_identity" "aks" { count = length(var.managed_identities.user_assigned_resource_ids) > 0 ? 0 : 1 location = var.location name = coalesce(var.user_assigned_identity_name, "uami-aks") resource_group_name = var.resource_group_name tags = var.tags } resource "azurerm_kubernetes_cluster" "this" { location = var.location name = "aks-${var.name}" resource_group_name = var.resource_group_name automatic_upgrade_channel = "patch" azure_policy_enabled = true dns_prefix = var.name kubernetes_version = var.kubernetes_version local_account_disabled = false node_os_upgrade_channel = "NodeImage" oidc_issuer_enabled = true private_cluster_enabled = true role_based_access_control_enabled = true sku_tier = "Standard" tags = var.tags workload_identity_enabled = true default_node_pool { name = "agentpool" vm_size = "Standard_D16ds_v5" auto_scaling_enabled = true host_encryption_enabled = true max_count = 5 max_pods = 110 min_count = 2 orchestrator_version = var.orchestrator_version os_sku = "Ubuntu" tags = merge(var.tags, var.agents_tags) vnet_subnet_id = azurerm_subnet.aks.id zones = try([for zone in local.regions_by_name_or_display_name[var.location].zones : zone], null) upgrade_settings { max_surge = "10%" } } auto_scaler_profile { balance_similar_node_groups = true } dynamic "azure_active_directory_role_based_access_control" { for_each = var.rbac_aad_admin_group_object_ids != null || var.rbac_aad_azure_rbac_enabled != null || var.rbac_aad_tenant_id != null ? [1] : [] content { admin_group_object_ids = var.rbac_aad_admin_group_object_ids azure_rbac_enabled = var.rbac_aad_azure_rbac_enabled tenant_id = var.rbac_aad_tenant_id } } ## Resources that only support UserAssigned identity { type = "UserAssigned" identity_ids = length(var.user_assigned_managed_identity_resource_ids) > 0 ? var.user_assigned_managed_identity_resource_ids : azurerm_user_assigned_identity.aks[*].id } monitor_metrics { annotations_allowed = try(var.monitor_metrics.annotations_allowed, null) labels_allowed = try(var.monitor_metrics.labels_allowed, null) } network_profile { network_plugin = "azure" load_balancer_sku = "standard" network_plugin_mode = "overlay" network_policy = "calico" outbound_type = "userDefinedRouting" pod_cidr = var.pod_cidr } oms_agent { log_analytics_workspace_id = azurerm_log_analytics_workspace.this.id msi_auth_for_monitoring_enabled = true } depends_on = [azurerm_subnet_route_table_association.this] lifecycle { ignore_changes = [ kubernetes_version ] } } # The following terraform_data is used to trigger the update of the AKS cluster when the kubernetes_version changes # This is necessary because the azurerm_kubernetes_cluster resource ignores changes to the kubernetes_version attribute # because AKS patch versions are upgraded automatically by Azure # The kubernetes_version_keeper and aks_cluster_post_create resources implement a mechanism to force the update # when the minor kubernetes version changes in var.kubernetes_version resource "terraform_data" "kubernetes_version_keeper" { triggers_replace = { version = var.kubernetes_version } } resource "azapi_update_resource" "aks_cluster_post_create" { type = "Microsoft.ContainerService/managedClusters@2024-02-01" body = { properties = { kubernetesVersion = var.kubernetes_version } } resource_id = azurerm_kubernetes_cluster.this.id lifecycle { ignore_changes = all replace_triggered_by = [terraform_data.kubernetes_version_keeper.id] } } resource "azurerm_log_analytics_workspace" "this" { location = var.location name = "log-${var.name}-aks" resource_group_name = var.resource_group_name sku = "PerGB2018" tags = var.tags } resource "azurerm_log_analytics_workspace_table" "this" { for_each = toset(local.log_analytics_tables) name = each.value workspace_id = azurerm_log_analytics_workspace.this.id plan = "Basic" } resource "azurerm_monitor_diagnostic_setting" "aks" { name = "amds-${var.name}-aks" target_resource_id = azurerm_kubernetes_cluster.this.id log_analytics_destination_type = "Dedicated" log_analytics_workspace_id = azurerm_log_analytics_workspace.this.id # Kubernetes API Server enabled_log { category = "kube-apiserver" } # Kubernetes Audit enabled_log { category = "kube-audit" } # Kubernetes Audit Admin Logs enabled_log { category = "kube-audit-admin" } # Kubernetes Controller Manager enabled_log { category = "kube-controller-manager" } # Kubernetes Scheduler enabled_log { category = "kube-scheduler" } #Kubernetes Cluster Autoscaler enabled_log { category = "cluster-autoscaler" } #Kubernetes Cloud Controller Manager enabled_log { category = "cloud-controller-manager" } #guard enabled_log { category = "guard" } #csi-azuredisk-controller enabled_log { category = "csi-azuredisk-controller" } #csi-azurefile-controller enabled_log { category = "csi-azurefile-controller" } #csi-snapshot-controller enabled_log { category = "csi-snapshot-controller" } metric { category = "AllMetrics" } } # required AVM resources interfaces resource "azurerm_management_lock" "this" { count = var.lock != null ? 1 : 0 lock_level = var.lock.kind name = coalesce(var.lock.name, "lock-${var.lock.kind}") scope = azurerm_kubernetes_cluster.this.id notes = var.lock.kind == "CanNotDelete" ? "Cannot delete the resource or its child resources." : "Cannot delete or modify the resource or its child resources." } resource "azurerm_kubernetes_cluster_node_pool" "this" { for_each = tomap({ for pool in local.node_pools : pool.name => pool }) kubernetes_cluster_id = azurerm_kubernetes_cluster.this.id name = each.value.name vm_size = each.value.vm_size auto_scaling_enabled = true max_count = each.value.max_count min_count = each.value.min_count orchestrator_version = each.value.orchestrator_version os_sku = each.value.os_sku tags = var.tags vnet_subnet_id = azurerm_subnet.aks.id zones = each.value.zone == "" ? null : [each.value.zone] depends_on = [azapi_update_resource.aks_cluster_post_create] lifecycle { precondition { condition = can(regex("^[a-z][a-z0-9]{0,11}$", each.value.name)) error_message = "The name must begin with a lowercase letter, contain only lowercase letters and numbers, and be between 1 and 12 characters in length." } } } # These resources allow the use of consistent local data files, and semver versioning data "local_file" "compute_provider" { filename = "${path.module}/data/microsoft.compute_resourceTypes.json" } data "local_file" "locations" { filename = "${path.module}/data/locations.json" } resource "azurerm_virtual_network" "this" { address_space = [var.virtual_network_address_space] location = var.location name = "vnet-${random_string.acr_suffix.result}" resource_group_name = var.resource_group_name tags = var.tags } resource "azurerm_subnet" "aks" { address_prefixes = [var.node_cidr] name = "aks-subnet" resource_group_name = var.resource_group_name virtual_network_name = azurerm_virtual_network.this.name } resource "azurerm_subnet" "firewall" { address_prefixes = [var.firewall_cidr] name = "AzureFirewallSubnet" resource_group_name = var.resource_group_name virtual_network_name = azurerm_virtual_network.this.name } resource "azurerm_public_ip" "this" { allocation_method = "Static" location = var.location name = "fw-pip-${random_string.acr_suffix.result}" resource_group_name = var.resource_group_name sku = "Standard" tags = var.tags } resource "azurerm_firewall" "this" { location = var.location name = "fw-${random_string.acr_suffix.result}" resource_group_name = var.resource_group_name sku_name = "AZFW_VNet" sku_tier = "Standard" dns_proxy_enabled = true tags = var.tags ip_configuration { name = "configuration" public_ip_address_id = azurerm_public_ip.this.id subnet_id = azurerm_subnet.firewall.id } } resource "azurerm_route_table" "this" { location = var.location name = "rt-${random_string.acr_suffix.result}" resource_group_name = var.resource_group_name tags = var.tags route { address_prefix = "0.0.0.0/0" name = "internet" next_hop_in_ip_address = azurerm_firewall.this.ip_configuration[0].private_ip_address next_hop_type = "VirtualAppliance" } route { address_prefix = "${azurerm_public_ip.this.ip_address}/32" name = "internal" next_hop_type = "Internet" } } resource "azurerm_firewall_network_rule_collection" "this" { action = "Allow" azure_firewall_name = azurerm_firewall.this.name name = "aksfwnr-${random_string.acr_suffix.result}" priority = 100 resource_group_name = var.resource_group_name rule { destination_ports = [ "1194", ] name = "apiudp" protocols = [ "UDP", ] destination_addresses = [ "AzureCloud.${var.location}", ] source_addresses = [ "*", ] } rule { destination_ports = [ "9000", ] name = "apitcp" protocols = [ "TCP", ] destination_addresses = [ "AzureCloud.${var.location}", ] source_addresses = [ "*", ] } rule { destination_ports = [ "123", ] name = "time" protocols = [ "UDP", ] destination_fqdns = [ "ntp.ubuntu.com", ] source_addresses = [ "*", ] } rule { destination_ports = [ "443", ] name = "ghcr" protocols = [ "TCP", ] destination_fqdns = [ "ghcr.io", "pkg-containers.githubusercontent.com", ] source_addresses = [ "*", ] } rule { destination_ports = [ "443", ] name = "docker" protocols = [ "TCP", ] destination_fqdns = [ "docker.io", "registry-1.docker.io", "production.cloudflare.docker.com", ] source_addresses = [ "*", ] } } resource "azurerm_firewall_application_rule_collection" "this" { action = "Allow" azure_firewall_name = azurerm_firewall.this.name name = "aksfwar-${random_string.acr_suffix.result}" priority = 100 resource_group_name = var.resource_group_name rule { name = "fqdn" fqdn_tags = ["AzureKubernetesService"] source_addresses = [ "*", ] } } resource "azurerm_subnet_route_table_association" "this" { route_table_id = azurerm_route_table.this.id subnet_id = azurerm_subnet.aks.id depends_on = [ azurerm_firewall_application_rule_collection.this, azurerm_firewall_network_rule_collection.this, ] }