如何检查 Terraform 是不是会创建资源

Posted

技术标签:

【中文标题】如何检查 Terraform 是不是会创建资源【英文标题】:How to check, if Terraform will create resource如何检查 Terraform 是否会创建资源 【发布时间】:2021-10-30 21:31:54 【问题描述】:

我在我的工作流程中使用 Terraform + AWS Lambda,并且我想在每个计划或应用操作期间使用我现有实例的 ARN 调用一些 lambda。我使用这种结构:

locals 
   ...
   centralized_nodes = ...
   tags_map = ...

   backup_map =         
   for node in keys(local.centralized_nodes):
   node => aws_instance.centralized_node[node].arn
   if can(local.tags_map[node]["backup"]) && !(can(aws_instance.centralized_node[node].root_block_device.0.tags["backup"]))
   
   ...

resource "aws_instance" "centralized_node" 
    ...
    for_each                 = local.centralized_nodes
    ...

data "aws_lambda_invocation" "lambda_backup" 
  for_each = local.backup_map  
  function_name = "lambdafunc"
  input = jsonencode(          
        "resources"            = [each.value]         
    )


在我尝试将节点描述对象添加到 centralized_nodes 映射并创建新实例之前,它运行良好。当我添加这个时,terraform 在规划过程中向我显示错误:

Error: Invalid for_each argument
│ 
│   on resources.tf line 223, in data "aws_lambda_invocation" "lambda_backup":
│  223:   for_each = local.backup_map  
│     ├────────────────
│     │ local.backup_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.

现在我正在寻找 terraform 检查资源是否存在或将被创建。我尝试向地图生成器添加参数以排除未创建的实例(在我的工作流程中可以),如下所示:

if can(local.tags_map[node]["backup"]) && !(can(aws_instance.centralized_node[node].root_block_device.0.tags["backup"])) && can(aws_instance.centralized_node[node].arn)

但它不起作用,terraform认为它可以达到未创建实例的ARN值,但稍后。

请帮我找到避免此类错误的方法。

【问题讨论】:

你想达到什么目的?看起来您正在尝试将 Terraform 用于它不打算用于的东西。为什么不在您的脚本中使用 AWS CLI 在运行 Terraform 之前调用 Lambda。保持简单。 我使用基于 ebs 卷标签的 AWS 快照备份功能。如果备份卷标签被破坏,这部分代码应该自动修复它们。此外,我无法使用 terraform 附加这些标签,因为它们包含实例和卷 ID,这就是我使用 Lambda 进行标签管理的原因。 【参考方案1】:

似乎您的local.centralized_nodes 不是由而是自动生成的。如果是这样,你 can't use it 和 for_each

map的keys(或者一组字符串情况下的所有值)必须是已知值,否则你会得到for_each有之前无法确定的依赖的错误信息应用,并且可能需要一个 -target。

您可以尝试将您的代码转换为使用count,因为count 没有这样的限制。但是,count 有其自身的问题,可能会阻止您实现目标。

【讨论】:

【参考方案2】:

我找到了检查实例和卷等资源是否已经存在的唯一方法。它使用 aws_instance 和 aws_instances 数据源。 这是我的解决方案:

locals 
   ...
   centralized_nodes = ...
   tags_map = ...
   instance_name_root_volume_tags_map = 
        for name, value in for id, value in data.aws_instance.instance_id_arn_map : value.tags["Name"] => value:
        name => data.aws_ebs_volume.volume_instance_id_map[value.id].tags
    
   backup_map =         
   for name, value in for id, value in data.aws_instance.instance_id_arn_map : value.tags["Name"] => value:
        name => value.arn
        if !can(local.instance_name_root_volume_tags_map[name]["backup"]) && can(for node, value in local.tags_map: value["Name"] => value[name]["backup"]) )
   
   ...

resource "aws_instance" "centralized_node" 
    ...
    for_each                 = local.centralized_nodes
    ...

data "aws_lambda_invocation" "lambda_backup" 
  for_each = local.backup_map  
  function_name = "lambdafunc"
  input = jsonencode(          
        "resources"            = [each.value]         
    )

data "aws_instances" "existant_instances_array" 
  filter 
    name   = "tag:Name"
    values =  [for tagmap in local.hiera_tags_map : tagmap["Name"]] 
  

data "aws_instance" "instance_id_arn_map" 
  for_each  = toset(data.aws_instances.existant_instances_array.ids)
  instance_id = each.value

data "aws_ebs_volume" "volume_instance_id_map" 
  for_each  = data.aws_instance.instance_id_arn_map
  filter 
      name = "attachment.instance-id"
      values = [each.value.id]
  
  filter 
      name = "attachment.device"
      values = ["/dev/sda1"]
  


【讨论】:

以上是关于如何检查 Terraform 是不是会创建资源的主要内容,如果未能解决你的问题,请参考以下文章

Terraform 在子 AWS 账户中创建资源

terraform 中是不是有任何特定的资源标签可以在 eventbridge 中创建规则

仅在不存在时如何创建 terraform 资源

如何使用 Terraform 为 Azure 资源创建警报

如何在 aws 检查器资源组上多次使用相同的标签

terraform 资源创建 - 这个关键字