From f89a832e6f874496e340248946d22c1ed2e18d6d Mon Sep 17 00:00:00 2001 From: Ghislain Cheng Date: Fri, 26 Sep 2025 02:36:28 +0200 Subject: [PATCH 1/2] chore: enhance VPC endpoint consumer module by transitioning to map-based service handling Signed-off-by: Ghislain Cheng --- modules/vpc-endpoint-consumer/main.tf | 68 +++++++++++++----------- modules/vpc-endpoint-consumer/outputs.tf | 46 ++++++++-------- 2 files changed, 61 insertions(+), 53 deletions(-) diff --git a/modules/vpc-endpoint-consumer/main.tf b/modules/vpc-endpoint-consumer/main.tf index 68db015..5e4b70d 100644 --- a/modules/vpc-endpoint-consumer/main.tf +++ b/modules/vpc-endpoint-consumer/main.tf @@ -27,12 +27,16 @@ locals { # Cluster name for tagging (use provided cluster_name or default) cluster_name_for_tags = var.cluster_name != null ? var.cluster_name : "mpc-cluster" - # Use the VPC endpoint service names provided by partners - # Note: vpc_endpoint_service_name must be provided directly by the partner - # as AWS auto-generates these names when creating VPC endpoint services - vpc_endpoint_service_names = [ - for service in var.party_services : service.vpc_endpoint_service_name - ] + # Convert party_services list to map for for_each usage + party_services_map = { + for service in var.party_services : service.party_id => service + } + + # Create separate map for services that need Kubernetes services + kube_services_map = { + for service in var.party_services : service.party_id => service + if service.create_kube_service + } } @@ -40,17 +44,17 @@ locals { # VPC interface endpoints to connect to partner MPC services # ************************************************************ resource "aws_vpc_endpoint" "party_interface_endpoints" { - count = length(var.party_services) + for_each = local.party_services_map vpc_id = local.vpc_id - service_name = local.vpc_endpoint_service_names[count.index] + service_name = each.value.vpc_endpoint_service_name vpc_endpoint_type = "Interface" - subnet_ids = length(coalesce(var.party_services[count.index].availability_zones, [])) > 0 && var.cluster_name != null ? [ + subnet_ids = length(coalesce(each.value.availability_zones, [])) > 0 && var.cluster_name != null ? [ for subnet_id, subnet in data.aws_subnet.cluster_subnets : subnet_id - if subnet.map_public_ip_on_launch == false && contains(var.party_services[count.index].availability_zones, subnet.availability_zone) + if subnet.map_public_ip_on_launch == false && contains(each.value.availability_zones, subnet.availability_zone) ] : local.subnet_ids security_group_ids = local.security_group_ids - service_region = var.party_services[count.index].region + service_region = each.value.region # DNS options private_dns_enabled = var.private_dns_enabled @@ -61,14 +65,15 @@ resource "aws_vpc_endpoint" "party_interface_endpoints" { tags = merge( var.tags, { - Name = "${var.name_prefix}-${var.party_services[count.index].name}-interface" - "mpc:partner-service" = var.party_services[count.index].name - "mpc:partner-region" = var.party_services[count.index].region + Name = "${var.name_prefix}-${each.value.name}-interface" + "mpc:partner-service" = each.value.name + "mpc:partner-party" = each.key + "mpc:partner-region" = each.value.region "mpc:component" = "partner-interface" "mpc:cluster" = local.cluster_name_for_tags }, - var.party_services[count.index].account_id != null ? { - "mpc:partner-account" = var.party_services[count.index].account_id + each.value.account_id != null ? { + "mpc:partner-account" = each.value.account_id } : {}, ) @@ -92,34 +97,35 @@ resource "kubernetes_namespace" "partner_namespace" { # Create Kubernetes services that route to the VPC interface endpoints # ********************************************************************* resource "kubernetes_service" "party_services" { - count = length([for service in var.party_services : service if service.create_kube_service]) + for_each = local.kube_services_map metadata { - name = "mpc-node-${var.party_services[count.index].party_id}" + name = "mpc-node-${each.key}" namespace = var.create_namespace ? kubernetes_namespace.partner_namespace[0].metadata[0].name : var.namespace annotations = merge({ "mpc.io/connection-type" = "partner-interface" - "mpc.io/partner-service" = var.party_services[count.index].name + "mpc.io/partner-service" = each.value.name + "mpc.io/partner-party" = each.key }, - var.party_services[count.index].account_id != null ? { - "mpc.io/partner-account" = var.party_services[count.index].account_id + each.value.account_id != null ? { + "mpc.io/partner-account" = each.value.account_id } : {}, - var.party_services[count.index].kube_service_config.additional_annotations) + each.value.kube_service_config.additional_annotations) labels = merge({ - "app.kubernetes.io/name" = "kms-${var.party_services[count.index].party_id}-core" - "app.kubernetes.io/instance" = "kms-${var.party_services[count.index].party_id}-core" + "app.kubernetes.io/name" = "kms-${each.key}-core" + "app.kubernetes.io/instance" = "kms-${each.key}-core" "app.kubernetes.io/component" = "mpc-partner-interface" "app.kubernetes.io/part-of" = "mpc-cluster" "mpc.io/partner-service" = "true" - }, var.party_services[count.index].kube_service_config.labels) + }, each.value.kube_service_config.labels) } spec { type = "ExternalName" - external_name = aws_vpc_endpoint.party_interface_endpoints[count.index].dns_entry[0].dns_name - session_affinity = var.party_services[count.index].kube_service_config.session_affinity + external_name = aws_vpc_endpoint.party_interface_endpoints[each.key].dns_entry[0].dns_name + session_affinity = each.value.kube_service_config.session_affinity dynamic "port" { for_each = concat( @@ -143,15 +149,15 @@ resource "kubernetes_service" "party_services" { # Create Route53 private hosted zone records for custom DNS names (in progress,optional) # ************************************************************************************** resource "aws_route53_record" "partner_dns" { - count = var.create_custom_dns_records ? length(var.party_services) : 0 + for_each = var.create_custom_dns_records ? local.party_services_map : {} zone_id = var.private_zone_id - name = "${var.party_services[count.index].name}.${var.dns_domain}" + name = "${each.value.name}.${var.dns_domain}" type = "A" alias { - name = aws_vpc_endpoint.party_interface_endpoints[count.index].dns_entry[0].dns_name - zone_id = aws_vpc_endpoint.party_interface_endpoints[count.index].dns_entry[0].hosted_zone_id + name = aws_vpc_endpoint.party_interface_endpoints[each.key].dns_entry[0].dns_name + zone_id = aws_vpc_endpoint.party_interface_endpoints[each.key].dns_entry[0].hosted_zone_id evaluate_target_health = true } } diff --git a/modules/vpc-endpoint-consumer/outputs.tf b/modules/vpc-endpoint-consumer/outputs.tf index 10b7b7e..dc7ca49 100644 --- a/modules/vpc-endpoint-consumer/outputs.tf +++ b/modules/vpc-endpoint-consumer/outputs.tf @@ -1,38 +1,39 @@ output "vpc_interface_endpoint_ids" { description = "IDs of the created VPC interface endpoints" - value = [for endpoint in aws_vpc_endpoint.party_interface_endpoints : endpoint.id] + value = [for endpoint in values(aws_vpc_endpoint.party_interface_endpoints) : endpoint.id] } output "vpc_interface_endpoint_dns_names" { description = "DNS names of the created VPC interface endpoints" - value = [for endpoint in aws_vpc_endpoint.party_interface_endpoints : endpoint.dns_entry[0].dns_name] + value = [for endpoint in values(aws_vpc_endpoint.party_interface_endpoints) : endpoint.dns_entry[0].dns_name] } output "vpc_interface_endpoint_hosted_zone_ids" { description = "Hosted zone IDs of the created VPC interface endpoints" - value = [for endpoint in aws_vpc_endpoint.party_interface_endpoints : endpoint.dns_entry[0].hosted_zone_id] + value = [for endpoint in values(aws_vpc_endpoint.party_interface_endpoints) : endpoint.dns_entry[0].hosted_zone_id] } output "vpc_interface_endpoint_service_names" { description = "Service names of the created VPC interface endpoints" - value = [for endpoint in aws_vpc_endpoint.party_interface_endpoints : endpoint.service_name] + value = [for endpoint in values(aws_vpc_endpoint.party_interface_endpoints) : endpoint.service_name] } output "partner_service_details" { description = "Detailed information about the partner services and their connections" value = [ - for i, endpoint in aws_vpc_endpoint.party_interface_endpoints : { - service_name = var.party_services[i].name - partner_region = var.party_services[i].region - partner_account_id = var.party_services[i].account_id # Can be null + for party_id, endpoint in aws_vpc_endpoint.party_interface_endpoints : { + party_id = party_id + service_name = local.party_services_map[party_id].name + partner_region = local.party_services_map[party_id].region + partner_account_id = local.party_services_map[party_id].account_id # Can be null vpc_endpoint_service_name = endpoint.service_name vpc_interface_endpoint_id = endpoint.id vpc_interface_dns_name = endpoint.dns_entry[0].dns_name vpc_interface_hosted_zone_id = endpoint.dns_entry[0].hosted_zone_id network_interface_ids = endpoint.network_interface_ids state = endpoint.state - created_kube_service = var.party_services[i].create_kube_service - ports = var.party_services[i].ports + created_kube_service = local.party_services_map[party_id].create_kube_service + ports = local.party_services_map[party_id].ports } ] } @@ -40,21 +41,21 @@ output "partner_service_details" { output "kubernetes_service_names" { description = "Names of the created Kubernetes services for partner connections" value = [ - for i, service in kubernetes_service.party_services : service.metadata[0].name + for service in values(kubernetes_service.party_services) : service.metadata[0].name ] } output "kubernetes_service_namespaces" { description = "Namespaces of the created Kubernetes services for partner connections" value = [ - for service in kubernetes_service.party_services : service.metadata[0].namespace + for service in values(kubernetes_service.party_services) : service.metadata[0].namespace ] } output "kubernetes_service_external_names" { description = "External names (VPC interface endpoint DNS) used by the Kubernetes services" value = [ - for service in kubernetes_service.party_services : service.spec[0].external_name + for service in values(kubernetes_service.party_services) : service.spec[0].external_name ] } @@ -66,7 +67,7 @@ output "namespace_name" { output "custom_dns_records" { description = "Custom DNS records created for the VPC interface endpoints (if enabled)" value = var.create_custom_dns_records ? [ - for i, record in aws_route53_record.partner_dns : { + for record in values(aws_route53_record.partner_dns) : { name = record.name type = record.type zone_id = record.zone_id @@ -81,9 +82,9 @@ output "connection_summary" { total_partners = length(var.party_services) partner_regions = distinct([for service in var.party_services : service.region]) partner_accounts = distinct(compact([for service in var.party_services : service.account_id])) - vpc_interface_endpoints = length(aws_vpc_endpoint.party_interface_endpoints) - kubernetes_services = length(kubernetes_service.party_services) - custom_dns_records = var.create_custom_dns_records ? length(aws_route53_record.partner_dns) : 0 + vpc_interface_endpoints = length(values(aws_vpc_endpoint.party_interface_endpoints)) + kubernetes_services = length(values(kubernetes_service.party_services)) + custom_dns_records = var.create_custom_dns_records ? length(values(aws_route53_record.partner_dns)) : 0 namespace = var.create_namespace && anytrue([for service in var.party_services : service.create_kube_service]) ? kubernetes_namespace.partner_namespace[0].metadata[0].name : var.namespace } } @@ -92,13 +93,14 @@ output "connection_summary" { output "partner_connection_endpoints" { description = "Connection endpoints for applications to use when connecting to partner services" value = { - for i, service in var.party_services : service.name => { + for party_id, service in local.party_services_map : party_id => { # Primary connection methods - vpc_interface_dns = aws_vpc_endpoint.party_interface_endpoints[i].dns_entry[0].dns_name - kubernetes_service_name = service.create_kube_service ? "${service.name}.${var.create_namespace && anytrue([for s in var.party_services : s.create_kube_service]) ? kubernetes_namespace.partner_namespace[0].metadata[0].name : var.namespace}.svc.cluster.local" : null - custom_dns_name = var.create_custom_dns_records ? "${service.name}.${var.dns_domain}" : null + vpc_interface_dns = aws_vpc_endpoint.party_interface_endpoints[party_id].dns_entry[0].dns_name + kubernetes_service_name = service.create_kube_service ? "mpc-node-${party_id}.${var.create_namespace && anytrue([for s in var.party_services : s.create_kube_service]) ? kubernetes_namespace.partner_namespace[0].metadata[0].name : var.namespace}.svc.cluster.local" : null + custom_dns_name = var.create_custom_dns_records ? "party-${party_id}.${var.dns_domain}" : null # Service details + service_name = service.name ports = service.ports partner_region = service.region partner_account = service.account_id # Can be null if not provided @@ -106,7 +108,7 @@ output "partner_connection_endpoints" { connection_type = "vpc-interface" # Advanced connection options - network_interface_ips = aws_vpc_endpoint.party_interface_endpoints[i].network_interface_ids + network_interface_ips = aws_vpc_endpoint.party_interface_endpoints[party_id].network_interface_ids } } } From e15a7d472722f552ea6e8fb634eea36db94c240b Mon Sep 17 00:00:00 2001 From: Ghislain Cheng Date: Fri, 26 Sep 2025 11:59:14 +0200 Subject: [PATCH 2/2] fix: update availability zone references in VPC endpoint modules Signed-off-by: Ghislain Cheng --- modules/vpc-endpoint-consumer/main.tf | 2 +- modules/vpc-endpoint-provider/main.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/vpc-endpoint-consumer/main.tf b/modules/vpc-endpoint-consumer/main.tf index 5e4b70d..a3c49e7 100644 --- a/modules/vpc-endpoint-consumer/main.tf +++ b/modules/vpc-endpoint-consumer/main.tf @@ -51,7 +51,7 @@ resource "aws_vpc_endpoint" "party_interface_endpoints" { vpc_endpoint_type = "Interface" subnet_ids = length(coalesce(each.value.availability_zones, [])) > 0 && var.cluster_name != null ? [ for subnet_id, subnet in data.aws_subnet.cluster_subnets : subnet_id - if subnet.map_public_ip_on_launch == false && contains(each.value.availability_zones, subnet.availability_zone) + if subnet.map_public_ip_on_launch == false && contains(each.value.availability_zones, subnet.availability_zone_id) ] : local.subnet_ids security_group_ids = local.security_group_ids service_region = each.value.region diff --git a/modules/vpc-endpoint-provider/main.tf b/modules/vpc-endpoint-provider/main.tf index 12325b4..22ab18e 100644 --- a/modules/vpc-endpoint-provider/main.tf +++ b/modules/vpc-endpoint-provider/main.tf @@ -130,7 +130,7 @@ data "aws_subnet" "nlb_subnet" { # Get all availability zones from the NLB subnets locals { - availability_zones = [for subnet in data.aws_subnet.nlb_subnet : subnet.availability_zone] + availability_zones = [for subnet in data.aws_subnet.nlb_subnet : subnet.availability_zone_id] } # **************************************************************