【问题标题】:terraform - how to use variables inside attributesterraform - 如何在属性中使用变量
【发布时间】:2021-07-06 15:33:00
【问题描述】:

我不确定这是否是正确的方法,但我想使用变量作为属性。

例如,我有一个根据用户输入更改的变量:os_name = ubuntu

我想像下面这样使用这个变量名,

resource "aws_instance" "workerNode" {
  ..................
  ami = data.aws_ami.${var.os_name}.image_id
  ..................
}

以下是数据块的示例,

data "aws_ami" "suse" {
    count  = "${var.os_name == "suse" ? 1 : 0}"
    owners = ["amazon"]
    most_recent = true
    filter {
        name = "name"
        values = ["suse-sles-${var.os_version}-sp*-v????????-hvm-ssd-x86_64"]
    }
}    

结果如下,

  "architecture" = "x86_64"
  "hypervisor" = "xen"
  "id" = "ami-0d3905203a039e3b0"
  "image_id" = "ami-0d3905203a039e3b0"

但是 terraform 不允许我这样做。有什么办法可以做到这一点,或者我必须改变工作流程?

【问题讨论】:

  • data.aws_ami 是地图吗?你能补充一下它的结构吗?
  • @yvesonline 是的。为了您的方便,我添加了更多示例。
  • interpolation syntax 仅适用于字符串,不适用于变量/数据访问。您需要在排序映射中构建您的 AMI,以便通过键访问它们。
  • 问题不在于我如何访问密钥。问题是如何使查询动态化。

标签: terraform terraform-provider-aws


【解决方案1】:

您可以通过使用for_each 指定您的AMI 来使其工作,从而获得可以通过密钥访问的map

我的data.aws_ami.myamis 看起来像这样:

data "aws_ami" "myamis" {
  for_each = toset(["suse", "ubuntu"])

  most_recent = true
  owners      = ["amazon"]

  filter {
    name = "name"
    values = ["${each.value}*"]
  }
}

出于测试目的,我定义了一个变量foo,如下所示:

variable "foo" {
  type    = string
  default = "suse"
}

现在我可以像这样访问 AMI:

$ tf console
> data.aws_ami.myamis[var.foo].image_id
"ami-0ea50c090ba6e85c5"

您可以调整它以满足您对 os_nameos_version 的需求。

【讨论】:

  • 问题是每个操作系统类型的过滤器都不同。对于某些操作系统,我有 3 个过滤器,而对于某些操作系统,我有 5 个。
  • 如果我只有两个操作系统,那么我可以使用条件,但在我的情况下,我有 5 个操作系统,每个操作系统都有 1 个数据块。
  • 这个答案的要点应该是您需要在map 中构建您的AMI,以便您可以通过key 访问它们。这并不是一个完整的答案,因为我们不知道您的完整代码。
【解决方案2】:

我已经通过使用条件表达式解决了这个问题。

我不确定这是否是一种标准的做事方式,但它对我有用。

我试图用嵌套的条件表达式来模拟 if/elif/else。

output "name" {
    value = "${ var.os_name == "ubuntu" ? data.aws_ami.ubuntu[0].image_id : (var.os_name == "redhat" ? data.aws_ami.redhat[0].image_id : (var.os_name == "centos" ? data.aws_ami.suse[0].image_id : data.aws_ami.suse[0].image_id ))}"
}

【讨论】:

    【解决方案3】:

    在不适合使用 for_each 将所有实例收集到单个资源下的情况下(这会隐含地使该资源显示为对象映射),您可以通过编写 @987654321 显式获得类似的结果@表达式构造等效映射:

    locals {
      amis = {
        suse   = data.aws_ami.suse
        ubuntu = data.aws_ami.ubuntu
      }
    }
    

    那你可以参考local.amis["ubuntu"]或者local.amis["suse"](如果需要的话可以用变量替换元素键。


    话虽如此,您的情况似乎确实有一种不同的方法,只需一个 data 块即可:

    locals {
      os_ami_queries = {
        suse = {
          owners = ["amazon"]
          filters = {
            name = ["suse-sles-${var.os_version}-sp*-v????????-hvm-ssd-x86_64"]
          }
        }
        ubuntu = {
          owners = ["amazon"]
          filters = {
            name = ["ubuntu-${var.os_version}-something-something"]
          }
        }
      }
      ami_query = local.os_ami_queries[var.os_name]
    }
    
    data "aws_ami" "selected" {
      owners = local.ami_query.owners
    
      dynamic "filter" {
        for_each = local.ami_query.filters
        content {
          name   = filter.key
          values = filter.value
        }
      }
    }
    

    这种不同的排列会在data "aws_ami" 查找之前执行操作系统选择,因此它可以使用与调用者选择的任何操作系统相关的设置。然后,AMI id 将位于 data.aws_ami.selected.id

    话虽如此,这种方法的缺点是非常间接并使用dynamic 块,所以我会权衡替代方案的可读性,以选择一个对于不熟悉的人来说似乎主观上最容易遵循的方案不熟悉这个配置。没有一个单一的答案,因为在某种程度上这是一个品味问题,所以如果你在一个团队环境中工作,这可能是与同事讨论的事情,看看哪种方法最适合权衡,比如你期望的频率添加和删​​除支持的操作系统与更改结果使用方式的细节。

    【讨论】:

    • 这更有意义。谢谢
    猜你喜欢
    • 1970-01-01
    • 2019-09-22
    • 1970-01-01
    • 2018-10-11
    • 1970-01-01
    • 2021-11-21
    • 2021-09-24
    • 2023-02-22
    • 2019-11-20
    相关资源
    最近更新 更多