【问题标题】:How can I pass a comma separated array to a resource in terraform v0.12.0?如何将逗号分隔的数组传递给 terraform v0.12.0 中的资源?
【发布时间】:2019-10-12 07:44:52
【问题描述】:

在以下代码块中,我试图将一组服务器名称传递给 attributes_json 块:

resource "aws_instance" "consul-server" {
    ami = var.consul-server
    instance_type = "t2.nano"
    key_name = var.aws_key_name
    iam_instance_profile = "dna_inst_mgmt"
    vpc_security_group_ids = [
        "${aws_security_group.yutani_consul.id}",
        "${aws_security_group.yutani_ssh.id}"
    ]
        subnet_id = "${aws_subnet.public_1_subnet_us_east_1c.id}"
        associate_public_ip_address = true
      tags = {
        Name = "consul-server${count.index}"
    }

    root_block_device {
        volume_size = "30"
        delete_on_termination = "true"
    }

    connection {
        type = "ssh"
        user = "chef"
        private_key = "${file("${var.aws_key_path}")}"
        timeout = "2m"
        agent = false
        host = self.public_ip
    }

   count = var.consul-server_count

   provisioner "chef" {
         attributes_json = <<-EOF
                {
                    "consul": {
                            "servers": ["${split(",",aws_instance.consul-server[count.index].id)}"]
                      }
                }
                EOF
        use_policyfile = true
        policy_name = "consul_server"
        policy_group = "aws_stage_enc"
        node_name       = "consul-server${count.index}"
        server_url      = var.chef_server_url
        recreate_client = true
        skip_install = true
        user_name       = var.chef_username
        user_key        = "${file("${var.chef_user_key}")}"
       version         = "14"
    }
   }

运行它给我一个错误:

Error: Cycle: aws_instance.consul-server[1], aws_instance.consul-server[0]

(这是在 var.consul-server_count 的变量中声明计数为 2 之后)

谁能告诉我这样做的正确方法是什么?

【问题讨论】:

    标签: terraform hcl terraform0.12+


    【解决方案1】:

    这里有两个问题:(1)如何在 JSON 字符串中插入逗号分隔的列表;以及(2)导致循环依赖错误的原因。

    如何插入列表以生成有效的 JSON 数组

    使用jsonencode

    最干净的方法是根本不使用heredoc,而只使用jsonencode 函数。

    你可以这样做:

    locals {
      arr = ["host1", "host2", "host3"]
    }
    
    output "test" {
      value = jsonencode(
        {
          "consul" = {
            "servers" = local.arr
          }
        })
    }
    

    这会产生输出:

    Outputs:
    
    test = {"consul":{"servers":["host1","host2","host3"]}}
    

    使用连接函数和heredoc

    Chef Provisioner 的文档建议对 JSON 字符串使用 heredoc,因此您也可以这样做:

    locals {
      arr = ["host1", "host2", "host3"]
      sep = "\", \""
    }
    
    output "test" {
      value = <<-EOF
        {
          "consul": {
            "servers": ["${join(local.sep, local.arr)}"]
          }
        }
      EOF
    }
    

    如果我应用它:

    Outputs:
    
    test = {
      "consul": {
        "servers": ["host1", "host2", "host3"]
      }
    }
    

    这里需要注意的一些事情:

    • 您正在尝试加入您的主机,以便它们在 JSON 数组的上下文中成为有效的 JSON。您需要使用"," 加入他们,而不仅仅是逗号。这就是为什么我定义了一个局部变量sep = "\", \""

    • 当您显然需要join 时,您似乎正在尝试split

    循环依赖问题

    报错原因:

    Error: Cycle: aws_instance.consul-server[1], aws_instance.consul-server[0]
    

    是你有循环依赖。考虑这个简化的例子:

    resource "aws_instance" "example" {
      count         = 3
      ami           = "ami-08589eca6dcc9b39c"
      instance_type = "t2.micro"
      user_data     = <<-EOF
        hosts="${join(",", aws_instance.example[count.index].id)}"
      EOF
    }
    

    或者您也可以在那里使用 splat 符号来获得相同的结果,即aws_instance.example.*.id

    Terraform 计划然后产生:

    ▶ terraform012 plan 
    ...
    Error: Cycle: aws_instance.example[2], aws_instance.example[1], aws_instance.example[0]
    

    所以你会得到一个循环错误,因为aws_instance.example.*.id 依赖于正在创建的aws_instance.example,所以资源依赖于它自己。换句话说,您不能在资源本身内部使用资源导出值。

    做什么

    我对 Consul 了解不多,但我还是有点困惑,为什么您需要在 servers 字段中使用 EC2 实例 ID。 Consul 配置不会在那里期待 IP 地址或主机名吗?

    在任何情况下,您可能都需要在此资源之外自己计算主机名,或者作为静态输入参数或您可以通过某种方式计算的东西。我想你最终会得到类似的东西:

    variable "host_names" {
      type    = list
      default = ["myhost1"]
    }
    
    resource "aws_instance" "consul_server" {
      ...
      provisioner "chef" {
        attributes_json = jsonencode(
          {
            "consul" = {
              "servers" = var.host_names
            }
          })
      }
    }
    

    【讨论】:

    • 谢谢 :) 我希望我可以根据我想要部署的实例数量使这个列表变量。嵌套循环会在本地定义中起作用吗?
    • @TyMac。好的。我在那里看到了问题,您得到了循环依赖,因为 AWS 实例 ID 仅在提供商创建实例后才可用。有时间我会用一些建议更新我的答案。
    • @TyMac,大大扩展了。这有帮助吗?
    • 非常感谢!服务器字段的原因听起来像您所猜测的 - Chef 使用实例列表配置领事服务器的“retry_join”选项。我希望将实例名称与主机名、厨师节点名称和 DNS 名称合并,以便“retry_join”字段中的名称在每个地方都显示相同,尤其是在运行“consul members”时。听起来我实际上应该使用“.dns_name”之类的东西而不是“.id”。
    • @TyMac,有一个.public_dns,但它不会有帮助,因为您仍然无法在资源块中引用它。我认为您需要以某种方式预先告诉 Terraform DNS 名称。
    猜你喜欢
    • 1970-01-01
    • 2023-03-30
    • 2016-06-22
    • 2016-02-07
    • 2014-10-12
    • 1970-01-01
    • 2013-09-06
    • 1970-01-01
    • 2014-02-01
    相关资源
    最近更新 更多