Terraform - 如何在条件运算符中使用有条件创建的资源的输出?

29

如果用户没有提供vpc id,我需要创建一个aws_vpc资源。然后我将使用该VPC创建资源。

现在,在创建aws_vpc资源时,我正在应用条件语句。例如,仅当existing_vpc为false时,才创建VPC:


现在,在创建aws_vpc资源时,我正在应用条件语句。例如,仅当existing_vpc为false时,才创建VPC:
count                = "${var.existing_vpc ? 0 : 1}"

接下来,例如,我必须在VPC中创建节点。如果existing_vpc为true,则使用var.vpc_id,否则使用从aws_vpc资源计算出的VPC ID。

但问题是,如果existing_vpc为true,则aws_vpc将不会创建新的资源,并且三元条件运算符仍然尝试检查是否正在创建aws_vpc资源。如果它没有被创建,terraform会报错。

在使用aws_subnet上的条件运算符时出现错误的示例:

Resource 'aws_subnet.xyz-subnet' not found for variable 'aws_subnet.xyz-subnet.id'

导致错误的代码是:

subnet_id = "${var.existing_vpc ? var.subnet_id : aws_subnet.xyz-subnet.id}"
如果两件事情互相依赖,我们如何创建有条件的资源并根据它们分配其他配置的值呢?

直接回答你的问题是在即将发布的0.12版本中使用null。间接的答案是,看起来代码条件和组织应该重新设计以避免这个问题,因为那个subnet_id的三元运算符似乎应该不同。 - Matt Schuchard
我的服务提供商目前还不支持0.12版。因此,我必须继续使用0.11.x版本。有什么方法可以解决这个问题吗? - Shantanu Deshpande
4个回答

36
你可以按照以下方式访问动态创建的模块和资源。

You can access dynamically created modules and resources as follows

output "vpc_id" {
  value = length(module.vpc) > 0 ? module.vpc[*].id : null
}
  • 如果count = 0,则输出为null
  • 如果count > 0,则输出vpc id列表

如果count = 1,并且您想要接收单个vpc id,则可以指定:

output "vpc_id" {
  value = length(module.vpc) > 0 ? one(module.vpc).id : null
}

“ONE” 函数仅在 Terraform v0.15 及更高版本中可用。 - Ferhat

5
下面的示例演示了如何使用条件运算符来可选地指定资源是否被创建,并展示了处理当资源未被创建时返回输出的方法。这是通过使用一个模块完成的,并使用对象变量的元素作为标志来指示是否应该创建资源。
但是,为了具体回答您的问题,您可以按如下方式使用条件运算符:

    output "module_id" {
       value = var.module_config.skip == true ? null : format("%v",null_resource.null.*.id)
    }

然后在调用 main.tf 中访问输出:

module "use_conditionals" {
    source = "../../scratch/conditionals-modules/m2" # << Change to your directory
    a = module.skipped_module.module_id # Doesn't exist, so might need to handle that.
    b = module.notskipped_module.module_id
    c = module.default_module.module_id
}

以下是完整示例。注意:此示例使用 terraform v0.14.2。


# root/main.tf
provider "null" {}

module "skipped_module" {
    source = "../../scratch/conditionals-modules/m1" # << Change to your directory
    module_config = { 
        skip = true         # explicitly skip this module.
        name = "skipped" 
    }
}

module "notskipped_module" {
    source = "../../scratch/conditionals-modules/m1" # << Change to your directory
    module_config = { 
        skip = false        # explicitly don't skip this module.
        name = "notskipped"
    }
}

module "default_module" {
    source = "../../scratch/conditionals-modules/m1" # << Change to your directory
    # The default position is, don't skip. see m1/variables.tf
}

module "use_conditionals" {
    source = "../../scratch/conditionals-modules/m2" # << Change to your directory
    a = module.skipped_module.module_id
    b = module.notskipped_module.module_id
    c = module.default_module.module_id
}

# root/outputs.tf
output skipped_module_name_and_id {
    value = module.skipped_module.module_name_and_id
}

output notskipped_module_name_and_id {
    value = module.notskipped_module.module_name_and_id
}

output default_module_name_and_id {
    value = module.default_module.module_name_and_id
}

模块


# m1/main.tf
resource "null_resource" "null" {
    count = var.module_config.skip ? 0 : 1 # If skip == true, then don't create the resource.

    provisioner "local-exec" {
        command = <<EOT
        #!/usr/bin/env bash
        echo "null resource, var.module_config.name: ${var.module_config.name}"
EOT
    }
}

# m1/variables.tf
variable "module_config" {
    type = object ({ 
        skip = bool, 
        name = string 
    })
    default = {
        skip = false
        name = "<NAME>"
        }
}

# m1/outputs.tf
output "module_name_and_id" {
   value = var.module_config.skip == true ? "SKIPPED" : format(
      "%s id:%v",
      var.module_config.name, 
      null_resource.null.*.id
   )
}

output "module_id" {
      value = var.module_config.skip == true ? null : format("%v",null_resource.null.*.id)
}


你能确认一下在你的例子中是否有错别字吗?在“module "use_conditionals"”中,你是从'm2'引用的...你是不是想说m1? - HoLengZai

1
当前的答案对于使用更现代版本的terraform非常有帮助,但正如OP在这里所指出的,当您使用terraform < 0.12时,它们无法起作用(如果您和我一样仍在处理这些旧版本,我很抱歉,我感同身受)。
请参阅terraform项目中相关问题,以获取更多信息,了解为什么需要以下内容来处理旧版本。
但为避免链接失效,我将使用OP的示例subnet_id参数,并使用github问题中的答案。
subnet_id = "${element(compact(concat(aws_subnet.xyz-subnet.*.id, list(var.subnet_id))),0)}"

从内到外:

  1. concat 将连接splat输出列表和list(var.subnet_id) -- 根据背景链接“当count = 0时,“splat语法”扩展为空列表”
  2. compact 将删除空项
  3. element 仅在compact接收到空的splat输出时返回您的var.subnet_id。

0
从Terraform v0.15开始,最好的解决方案实际上是:
output "vpc_id" {
  value = one(module.vpc[*].id)
}

这使用splat expression来创建一个ID列表。如果没有创建VPC,列表将为空。然后,one() function将把它转换为列表的第一个元素或null。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接