使用 Terraform、Ansible 和 Chef,从阵列中提取一个具有特定值的哈希

备忘录

我想做的事情

在terraform中,从以下类似哈希(映射)数组列表中提取name为n2的attr。确保键是唯一的。


array_01:
  - name: n1
    attr: attr1
  - name: n2
    attr: attr2
  - name: n3
    attr: attr3

此外,如果使用Ansible,可以使用selectattr过滤器,而如果使用chef|ruby,则可以使用select方法。

方法2:使用 for 循环进行补充(2023/02)

对于terraform来说,即便用for循环也是可能的。
例如。

locals {
  array_01 = [
    {
      name = "n1",
      attr = "attr1"
    },
    {
      name = "n2",
      attr = "attr2"
    },
    {
      name = "n3",
      attr = "attr3"
    }
  ]
}

output "out1" {
  value = local.array_01[index(local.array_01.*.name, "n2")].attr  # 方法1
}

output "out2" {
  value = [for x in local.array_01 : x.attr if x.name == "n2"][0]  # 方法2
}
$ terraform apply --auto-approve

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

out1 = "attr2"
out2 = "attr2"

以下是方法1:

方法2是通过追加的方式,在for循环中使用if语句选择元素。由于结果是一个列表,因此使用[0]来提取第一个元素。

或许方法2稍微更易读一些。

选项1:使用通配符指定索引。

示:

请提供以下信息的中文翻译。

请使用一种选择。

此外,使用.tf.json而不是.ft。

{
  "locals": {
    "array_01": [
      { "name": "n1", "attr": "attr1" },
      { "name": "n2", "attr": "attr2" },
      { "name": "n3", "attr": "attr3" }
    ]
  },
  "output": {
    "array_01_name": {
      "value": "${ local.array_01.*.name }"
    },
    "n2_attr": {
      "value": "${ local.array_01[ index( local.array_01.*.name, \"n2\" ) ].attr }"
    }
  }
}

为了让内容更易读,使用yq工具将其格式化为yaml,并附上相应内容。

$ yq -y . main.tf.json
locals:
  array_01:
    - name: n1
      attr: attr1
    - name: n2
      attr: attr2
    - name: n3
      attr: attr3
output:
  array_01_name:
    value: ${ local.array_01.*.name }
  n2_attr:
    value: ${ local.array_01[ index( local.array_01.*.name, "n2" ) ].attr }

在这里,local.array_01.*.name 表示 local.array_01 中键为 name: 的值的数组(列表)的元素, index( local.array_01.*.name, “n2” ) 表示名字为 n2 的元素的索引号(从0开始)为1。也可使用 local.array_01[*].name 来表示。

$ terraform apply --auto-approve 

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

array_01_name = [
  "n1",
  "n2",
  "n3",
]
n2_attr = attr2

另外,可能是因为只有local和output,所以不需要进行terraform init,即使进行了terraform init,.terraform目录也不会被创建。

补充说明

此外,在从资源获取基于通配符的条件时,有些情况下,由于元素类型不匹配,会导致索引错误。这是terraform或provider的bug吗?
通过使用for循环创建的列表给予索引,可以避免这个问题。

用for循环将列表传递给index以避免的示例。

{
  "locals": {
    "array_01": [
      {
        "name": "n1",
        "attr": "attr1"
      },
      {
        "name": "n2",
        "attr": "attr2"
      },
      {
        "name": "n3",
        "attr": "attr3"
      }
    ]
  },
  "output": {
    "array_01_name": {
      "value": "${ local.array_01.*.name }"
    },
    "n2_attr": {
      "value": "${ local.array_01[ index( [ for s in local.array_01 : s.name ], \"n2\") ].attr }"
    }
  }
}
$ terraform apply --auto-approve

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

array_01_name = [
  "n1",
  "n2",
  "n3",
]
n2_attr = attr2

Ansible的示例

- hosts: all
  gather_facts: false
  vars:
    array_01:
      - name: n1
        attr: attr1
      - name: n2
        attr: attr2
      - name: n3
        attr: attr3
  tasks:
    - debug: msg={{ ( array_01 | selectattr("name", "equalto", "n2") | list )[0].attr }}

    - debug: msg={{ ( array_01 | selectattr("name", "==", "n2") | list )[0].attr }}

    - debug: msg={{ ( array_01 | selectattr("name", "==", "n2") | list | first ).attr }}

    - debug: msg={{ ( array_01 | json_query("[?name=='n2'].attr"))[0] }}

どこかのバージョンから | list は不要。(ansible 7.0.0, ansible-core 2.14.0にて確認)

json_query には sudo pip install jmespath などが必要

$ ansible-playbook -i localhost, -c local selectattr.yml
PLAY [all] *******************************************************************************************************

TASK [debug] *****************************************************************************************************
ok: [localhost] => {
    "msg": "attr2"
}

以下同様

主厨(或者叫做Ruby)的例子


require 'yaml'

array_01 = YAML.load(<<~EOS)
  - name: n1
    attr: attr1
  - name: n2
    attr: attr2
  - name: n3
    attr: attr3
EOS

p array_01.select { |e| e['name'] == 'n2' }[0]['attr']
$ ruby select.rb 
"attr2"

请参考:terraform

 

我稍微提到了通配符。

在公式的索引项中,没有找到通配符的说明。

参考: 安锐思

 

广告
将在 10 秒后关闭
bannerAds