我在运行地形计划和应用时收到以下错误
on main.tf line 517, in resource "aws_lb_target_group_attachment" "ecom-tga":
│ 517: for_each = local.service_instance_map
│ ├────────────────
│ │ local.service_instance_map will be known only after apply
│
│ The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will
│ be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.
我的配置文件如下
variable "instance_count" {
type = string
default = 3
}
variable "service-names" {
type = list
default = ["valid","jsc","test"]
}
locals {
helper_map = {for idx, val in setproduct(var.service-names, range(var.instance_count)):
idx => {service_name = val[0]}
}
}
resource "aws_instance" "ecom-validation-service" {
for_each = local.helper_map
ami = data.aws_ami.ecom.id
instance_type = "t3.micro"
tags = {
Name = "${each.value.service_name}-service"
}
vpc_security_group_ids = [data.aws_security_group.ecom-sg[each.value.service_name].id]
subnet_id = data.aws_subnet.ecom-subnet[each.value.service_name].id
}
data "aws_instances" "ecom-instances" {
for_each = toset(var.service-names)
instance_tags = {
Name = "${each.value}-service"
}
instance_state_names = ["running", "stopped"]
depends_on = [
aws_instance.ecom-validation-service
]
}
locals {
service_instance_map = merge([for env, value in data.aws_instances.ecom-instances:
{
for id in value.ids:
"${env}-${id}" => {
"service-name" = env
"id" = id
}
}
]...)
}
resource "aws_lb_target_group_attachment" "ecom-tga" {
for_each = local.service_instance_map
target_group_arn = aws_lb_target_group.ecom-nlb-tgp[each.value.service-name].arn
port = 80
target_id = each.value.id
depends_on = [aws_lb_target_group.ecom-nlb-tgp]
}
由于我将count作为var传递,其值为3,我认为terraform将进行预测,因为它需要创建9个实例。但它似乎并没有,并且抛出了无法预测的错误。
我们是否必须通过为实例计数预测或本地service_instance_map提供一些默认值来绕过这一点?
尝试了尝试功能,但仍然没有运气
Error: Invalid for_each argument
│
│ on main.tf line 527, in resource "aws_lb_target_group_attachment" "ecom-tga":
│ 527: for_each = try(local.service_instance_map,[])
│ ├────────────────
│ │ local.service_instance_map will be known only after apply
│
│ The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will
│ be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.
我的要求发生了变化,现在我必须在该地区的3个子网中创建3个实例。我改变了当地人如下但相同的预测问题
locals {
merged_subnet_svc = try(flatten([
for service in var.service-names : [
for subnet in aws_subnet.ecom-private.*.id : {
service = service
subnet = subnet
}
]
]), {})
variable "azs" {
type = list(any)
default = ["ap-south-1a", "ap-south-1b", "ap-south-1c"]
}
variable "private-subnets" {
type = list(any)
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}
resource "aws_instance" "ecom-instances" {
for_each = {
for svc in local.merged_subnet_svc : "${svc.service}-${svc.subnet}" => svc
}
ami = data.aws_ami.ecom.id
instance_type = "t3.micro"
tags = {
Name = "ecom-${each.value.service}-service"
}
vpc_security_group_ids = [aws_security_group.ecom-sg[each.value.service].id]
subnet_id = each.value.subnet
}
}
在您的配置中,您已经声明data "aws_instances" "ecom-instances"
依赖于aws_instance.ecom-validation-service
。由于第一次运行时其他对象还不存在,Terraform必须等到应用步骤读取data.aws_instances.ecom-instances
,否则它将无法满足您声明的依赖关系,因为aws_instance.ecom-validation-service
还不存在。
为了避免您在这里看到的错误消息,您需要确保for_each
只引用Terraform在实际创建任何对象之前会知道的值。因为EC2只在创建实例后才分配实例id,所以将EC2实例id用作for_each
实例密钥的一部分是不正确的。
此外,data "aws_instances"
块不需要在此处检索实例信息,因为resource "aws_instance" "ecom-validation-service"
块已经提供了相关的实例信息。
话虽如此,让我们从您的输入变量开始,重新构建,同时确保我们只根据规划期间知道的值构建实例键。你所拥有的变量基本上保持不变;我只是稍微调整了一下类型约束,以匹配我们使用每一个的方式:
variable "instance_count" {
type = string
default = 3
}
variable "service_names" {
type = set(string)
default = ["valid", "jsc", "test"]
}
我从您的示例的其余部分了解到,您打算为var.service_names
的每个不同元素创建var.instance_count
实例。您的setproduct
生成所有这些组合也很好,但我将对其进行调整,为实例分配包括服务名称的唯一密钥:
locals {
instance_configs = tomap({
for pair in setproduct(var.service_names, range(var.instance_count)) :
"${pair[0]}${pair[1]}" => {
service_name = pair[0]
}
})
}
这将产生如下数据结构:
{
valid0 = { service_name = "valid" }
valid1 = { service_name = "valid" }
valid2 = { service_name = "valid" }
jsc0 = { service_name = "jsc" }
jsc1 = { service_name = "jsc" }
jsc2 = { service_name = "jsc" }
test0 = { service_name = "test" }
test1 = { service_name = "test" }
test2 = { service_name = "test" }
}
这与for_each
期望的形状相匹配,因此我们可以直接使用它来声明九个aws_instance
实例:
resource "aws_instance" "ecom-validation-service" {
for_each = local.instance_configs
instance_type = "t3.micro"
ami = data.aws_ami.ecom.id
subnet_id = data.aws_subnet.ecom-subnet[each.value.service_name].id
vpc_security_group_ids = [
data.aws_security_group.ecom-sg[each.value.service_name].id,
]
tags = {
Name = "${each.value.service_name}-service"
Service = each.value_service_name
}
}
到目前为止,这与您分享的内容基本相同。但这正是我将朝着完全不同的方向前进的地方:与其现在尝试使用单独的数据资源读回声明的实例,不如直接从aws_instance.ecom-validation-service
资源中收集相同的数据。Terraform配置通常最好管理特定对象或读取它,而不是同时管理和读取,因为这样,必要的依赖关系顺序会自动显示为引用。
请注意,我在每个实例上都包含了一个额外的标记Service
,以提供一种更方便的方式来获取服务名称。如果你不能做到这一点,那么你可以通过从Name
标签中修剪-service
后缀来获得相同的信息,但我更喜欢尽可能保持直接。
当时的目标似乎是每个实例都有一个aws_lb_target_group_attachment
实例,每个实例都根据服务名称连接到适当的目标组。因为aws_instance
资源设置了for_each
,所以其他表达式中的aws_instance.ecom-validation-service
是对象的映射,其中键与var.instance_configs
中的键相同。这意味着该值也与for_each
的要求兼容,因此我们可以直接使用它来声明目标组附件:
resource "aws_lb_target_group_attachment" "ecom-tga" {
for_each = aws_instance.ecom-validation-service
target_group_arn = aws_lb_target_group.ecom-nlb-tgp[each.value.tags.Service].arn
port = 80
target_id = each.value.id
}
为了查找合适的目标组ARN,我依靠前面额外的Service
标签来轻松地确定每个实例属于哪个服务。each.value.id
在这里起作用,因为each.value
始终是一个aws_instance
对象,它导出该id
属性。
这样做的结果是两组实例,每个实例的密钥都与local.instance_configs
:中的密钥相匹配
aws_instance.ecom-validation-service["valid0"]
aws_instance.ecom-validation-service["valid1"]
aws_instance.ecom-validation-service["valid2"]
aws_instance.ecom-validation-service["jsc0"]
aws_instance.ecom-validation-service["jsc1"]
aws_instance.ecom-validation-service["jsc2"]
aws_lb_target_group_attachment.ecom-tga["valid0"]
aws_lb_target_group_attachment.ecom-tga["valid1"]
aws_lb_target_group_attachment.ecom-tga["valid2"]
aws_lb_target_group_attachment.ecom-tga["jsc0"]
aws_lb_target_group_attachment.ecom-tga["jsc1"]
aws_lb_target_group_attachment.ecom-tga["jsc2"]
请注意,所有这些密钥只包含配置中直接指定的信息,而不包含远程系统决定的任何信息。这意味着我们要避免";无效的for_each参数"错误,即使每个实例仍然具有适当的唯一密钥。如果稍后要向var.service_names
添加新元素或增加var.instance_count
,Terraform也会从这些实例键的形状中看到,它应该只添加每个资源的新实例,而不是重命名/重新编号任何现有实例。