如何修复terraform不可预测的实例创建问题



我在运行地形计划和应用时收到以下错误

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也会从这些实例键的形状中看到,它应该只添加每个资源的新实例,而不是重命名/重新编号任何现有实例。

相关内容

  • 没有找到相关文章

最新更新