如何在Terraform中通过JSON配置结合for_each的data块正确引用资源?
使用JSON配置Terraform Data块并关联Resource的问题解答
问题描述
尝试通过传入JSON配置创建Terraform资源,让resource遍历该JSON配置,但JSON中的部分值需要用于data块的过滤器配置,询问是否可以实现并正确引用data块返回的对应VPC和子网项。
给出的JSON配置示例
[ { "username": "administrator", "password": "password", "domain_name": "test.io", "dns": "[10.10.1.6]", "vpc_name": "Sandbox*", "subnet_name": "*private*", "org_unit": "OU=test,DC=awstest,DC=io" }, { "domain": "awstest.io", "username": "administrator", "password": "password2" } ]
当前Terraform代码
locals { DirectoryServices = jsondecode(var.json) } data "aws_vpc" "this" { for_each = local.DirectoryServices filter { name = "tag:Name" values = [each.value.vpc_name] } } data "aws_subnets" "this" { for_each = local.DirectoryServices filter { name = "tag:Name" values = [each.value.subnet_name] } filter { name = "vpc-id" values = [data.aws_vpc.this.id] } } locals { subnets = slice(data.aws_subnets.this.ids, 0, 2) } resource "aws_directory_service_directory" "connector" { for_each = local.DirectoryServices name = each.value.domain_name password = each.value.password size = each.value.size type = "ADConnector" connect_settings { customer_dns_ips = each.value.dns customer_username = each.value.username subnet_ids = data.aws_subnets.this.ids vpc_id = data.aws_vpc.this.id } }
解答:可以实现,但需修正代码中的关键问题
当前代码存在几个核心问题,导致无法正确关联data块和resource,以下是修正方案和关键说明:
1. 处理JSON字段缺失问题
第二个JSON条目缺少vpc_name、subnet_name等必要字段,直接遍历会报错。需要先过滤出包含所有必要字段的条目,或给缺失字段设置默认值:
locals { # 解析JSON并过滤出包含必要字段的条目,避免报错 DirectoryServices = [ for item in jsondecode(var.json) : item if contains(keys(item), "vpc_name") && contains(keys(item), "subnet_name") && contains(keys(item), "domain_name") && contains(keys(item), "dns") ] # 转换为映射,用唯一的domain_name作为键,方便for_each关联 DirectoryServices_map = { for item in local.DirectoryServices : item.domain_name => item } }
2. 修正data块的引用方式
使用for_each后,data资源会变成映射类型,不能直接用data.aws_vpc.this.id,必须通过each.key关联对应的实例:
data "aws_vpc" "this" { for_each = local.DirectoryServices_map filter { name = "tag:Name" values = [each.value.vpc_name] } } data "aws_subnets" "this" { for_each = local.DirectoryServices_map filter { name = "tag:Name" values = [each.value.subnet_name] } filter { name = "vpc-id" values = [data.aws_vpc.this[each.key].id] # 通过each.key匹配对应的VPC } }
3. 修正resource块的格式和引用
- JSON中的
dns是字符串形式的列表,需要用jsondecode转成Terraform可识别的列表 - 子网切片要针对每个实例单独处理,不能全局切片
- 给缺失的
size字段设置默认值:
resource "aws_directory_service_directory" "connector" { for_each = local.DirectoryServices_map name = each.value.domain_name password = each.value.password size = each.value.size != null ? each.value.size : "Small" # 默认小型实例 type = "ADConnector" connect_settings { customer_dns_ips = jsondecode(each.value.dns) # 转换字符串列表为Terraform列表 customer_username = each.value.username subnet_ids = slice(data.aws_subnets.this[each.key].ids, 0, 2) # 取当前实例的前2个子网 vpc_id = data.aws_vpc.this[each.key].id } }
关键注意事项
- 确保JSON中用于遍历的条目有唯一标识(比如
domain_name),避免for_each键冲突 - 所有依赖字段必须存在或有默认值,否则Terraform会在计划阶段报错
- 通过
each.key关联data和resource实例,保证每个资源对应正确的VPC和子网
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

