“for_each”值取决于无法确定的资源属性(Terraform)

Avn*_*evy 5 terraform

我有一个 terraform 配置,它需要:

  1. 创建一个 lambda
  2. 调用 lambda
  3. 迭代 lambda 的 json 结果,该结果返回一个数组并为数组中的每个条目创建一个 CloudWatch 事件规则

相关代码如下所示:

Create lambda code...

data "aws_lambda_invocation" "run_lambda" {
  function_name = "${aws_lambda_function.deployed_lambda.function_name}"

  input = <<JSON
  {}
  JSON
  depends_on = [aws_lambda_function.deployed_lambda]
}

resource "aws_cloudwatch_event_rule" "aws_my_cloudwatch_rule" {
  for_each = {for record in jsondecode(data.aws_lambda_invocation.run_lambda.result).entities : record.entityName => record} 
    name = "${each.value.entityName}-event"
    description = "Cloudwatch rule for ${each.value.entityName}"
    schedule_expression = "cron(${each.value.cronExpression})"
}
Run Code Online (Sandbox Code Playgroud)

问题是当我运行它时,我得到:

Error: Invalid for_each argument

  on lambda.tf line 131, in resource "aws_cloudwatch_event_rule" "aws_my_cloudwatch_rule":
 131:   for_each = {for record in jsondecode(data.aws_lambda_invocation.aws_lambda_invocation.result).entities : record.entityName => record}

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.
Run Code Online (Sandbox Code Playgroud)

我已经阅读了很多关于这个问题的帖子,但找不到解决方法。
问题在于 Terraform 在创建 lambda 之前的规划阶段需要知道 lambda 返回的数组的大小。
解决此类任务的最佳方法是什么?
由于它作为 CI/CD 管道的一部分运行,因此我更喜欢不包含“-target”标志的解决方案。

mar*_*iux 8

如果您想在纯 terraform 中解决此问题,目前的解决方法是将您的部署拆分为多个堆栈/阶段(例如,首先使用 lambda 部署一个堆栈,然后使用 lambda 作为数据源的第二个堆栈) 或者正如您已经发现的那样,使用部分部署您的堆栈-target,然后部署完整的堆栈。(depends_on在这种情况下一定要删除 ,因为它会将读取数据源一直推迟到应用阶段。)

另一种选择是使用像terragrunt这样的工具,如果定义了这些模块之间的所有依赖项,则通过以正确的顺序部署一组 terraform 模块来解决部分应用问题。与terragrunt您可以部署在例如单次运行一切terragrunt apply-all。缺点是您仍然无法很好地预览 CI 中的更改以供同行审查。

我建议将其分为两个阶段,因为您可能实际上希望在应用最终更改之前查看这两个阶段。否则,您最终可能会遇到一个设置,其中损坏的 lambda 会破坏您或您的团队未注意到的所有现有 Cloudwatch 规则。


jlo*_*ail 8

一种可能性是重新考虑for_each并在适当的情况下使用count代替。for_each有一些主要的限制。我遇到了类似的问题(对我来说似乎是一个主要错误,但他们说这是一个功能)考虑我正在部署三个虚拟机,并希望将它们绑定到负载均衡器:

resource "aws_instance" "xxx-IIS-004" {

  ami               = var.ami["Windows Server 2019"]
  instance_type     = var.depoy_lowcost ? var.default_instance_type : "m5.2xlarge"
  count             = "3"
  ...
Run Code Online (Sandbox Code Playgroud)

当我尝试使用for_each 时,我得到“for_each”值取决于无法确定的资源属性......或元组错误。

失败:

resource "aws_elb_attachment" "attachments_004" {
  depends_on = [ aws_instance.xxx-IIS-004 ]
  elb        = data.aws_elb.loadBalancer.id
  for_each   = aws_instance.xxx-IIS-004[*]
  instance   = each.value.id
}
Run Code Online (Sandbox Code Playgroud)

作品*

locals {
  att_004 = join("_", aws_instance.xxx-IIS-004[*].id )
}

resource "aws_elb_attachment" "attachments_004" {
  depends_on = [ aws_instance.xxx-IIS-004 ]
  elb        = data.aws_elb.loadBalancer.id
  count      = length( aws_instance.xxx-IIS-004 )
  instance   = split("_", local.att_004)[count.index]
}
Run Code Online (Sandbox Code Playgroud)

  • 这难道不会让您走向类似的“错误:无效的计数参数”(““计数”值取决于在应用之前无法确定的资源属性”)吗?地形 1.0.3。 (5认同)
  • 也许它适用于问题中的特定资源类型,但它当时并不能更广泛地适用于所有资源类型。 (2认同)

小智 5

工作一段时间

  resource "aws_lb_target_group_attachment" "attach-in" {
  for_each = toset([for x in module.webserver-in.instance : x.id])
  target_group_arn  = aws_lb_target_group.tg-in.arn
  target_id         = each.value
  port             = 443
}
Run Code Online (Sandbox Code Playgroud)

完美的工作

locals {
  instance_ids = join("_", [for x in module.webserver.instance : x.id] )
       }

resource "aws_lb_target_group_attachment" "attach-in" 
{
count = length(module.webserver-in.instance)
target_group_arn  = aws_lb_target_group.tg-in.arn
target_id         = split("_", local.instance_ids)[count.index]
port             = 443
}   
Run Code Online (Sandbox Code Playgroud)

  • 再次感谢我的资源 "azurerm_role_assignment" "RoleAssignment" { count = length(var.ResourceIds) range = var.ResourceIds[count.index] role_definition_name = var.RoleTypeprincipal_id = var.AzureAdPrincipalId } (2认同)