Terraform for_each创建aws_lb_target_group_attachment问题求解
问题描述
- 需通过
for_each批量创建AWS负载均衡目标组附件,减少重复编码。aws_lb_target_group_attachment资源仅支持传入单个字符串格式的target_id(对应EC2实例ID),要求实现1个目标组绑定多个实例的效果,例如此场景中1个目标组对应2条附件资源。 - 初始写法直接将
target_ids列表传入target_id参数,执行时抛出类型不匹配错误:
╷ │ Error: Incorrect attribute value type │ │ on dev-alb.tf line 288, in resource "aws_lb_target_group_attachment" "dev_lb_tg": │ 288: target_id = each.value.target_ids │ ├──────────────── │ │ each.value.target_ids is list of string with 2 elements │ │ Inappropriate value for attribute "target_id": string required. ╵
- 临时方案为每个实例单独编写重复的附件资源块,冗余度高,需优化为简洁规范的实现。
实现方案
Terraform不支持for_each直接嵌套遍历,核心解决思路是先将嵌套的「服务-实例列表」结构打平为单层map,再将该map作为for_each的遍历源,这是Terraform官方推荐的多层资源关联处理方式。
具体修改逻辑
- 新增本地值,遍历所有服务配置,将每个服务下的
target_ids列表展开,生成复合唯一键(格式为服务名-实例ID,避免不同服务下相同实例ID引发键冲突),每个键对应的值存储服务名、目标组端口、实例ID三个核心参数。 - 原有目标组资源逻辑无需改动。
- 目标组附件资源直接遍历打平后的单层map,无需嵌套遍历。
完整可用代码
locals { dev_alb_defaults = { vpc_id = local.dev_alb_vpc_id health_check_enabled = true health_check_interval = 30 health_check_timeout = 5 health_check_healthy_threshold = 2 health_check_unhealthy_threshold = 2 health_check_matcher = 200 } dev_alb_targets = { i-am-service = { tg_port = 443 target_ids = ["i-0123456789ab", "i-0123456789bc"] } } # 打平嵌套结构,生成目标组附件专用遍历map dev_alb_tg_attachments = merge([ for service_name, service_conf in local.dev_alb_targets : { for target_id in service_conf.target_ids : "${service_name}-${target_id}" => { service_name = service_name tg_port = service_conf.tg_port target_id = target_id } } ]...) } resource "aws_lb_target_group" "dev_lb_tg" { for_each = local.dev_alb_targets name = "${var.customer}-${var.dev_prefix}-${try(each.value.name, each.key)}-tg" port = try(each.value.tg_port, null) protocol = try(each.value.tg_protocol, null) vpc_id = try(each.value.vpc_id, local.dev_alb_defaults.vpc_id, null) health_check { enabled = try(each.value.health_check_enabled, local.dev_alb_defaults.health_check_enabled, false) path = try(each.value.health_check_path, local.dev_alb_defaults.health_check_path, null) port = try(each.value.health_check_port, each.value.tg_port, null) protocol = try(each.value.health_check_protocol, local.dev_alb_defaults.health_check_protocol, null) interval = try(each.value.health_check_interval, local.dev_alb_defaults.health_check_interval, null) timeout = try(each.value.health_check_timeout, local.dev_alb_defaults.health_check_timeout, null) healthy_threshold = try(each.value.health_check_healthy_threshold, local.dev_alb_defaults.health_check_healthy_threshold, null) unhealthy_threshold = try(each.value.health_check_unhealthy_threshold, local.dev_alb_defaults.health_check_unhealthy_threshold, null) matcher = try(each.value.health_check_matcher, local.dev_alb_defaults.health_check_matcher, null) } } resource "aws_lb_target_group_attachment" "dev_lb_tg" { for_each = local.dev_alb_tg_attachments target_group_arn = aws_lb_target_group.dev_lb_tg[each.value.service_name].arn port = each.value.tg_port target_id = each.value.target_id }
方案特性
- 打平后的map键采用复合格式保证全局唯一,后续新增、删除实例或服务时,Terraform只会精准增删对应的附件资源,不会影响其他已存在资源。
- 无需重复编写多个附件资源块,后续扩展时仅需在对应服务的
target_ids列表中追加实例ID即可,无需修改资源层逻辑。 - 完全符合Terraform参数类型校验要求,执行
terraform plan时可看到预期的1个目标组、2条附件资源的创建计划。
内容的提问来源于stack exchange,提问作者Jelle
相关产品推荐
相关产品推荐

