使用 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
我稍微提到了通配符。
在公式的索引项中,没有找到通配符的说明。
参考: 安锐思