備忘
やりたい事
**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は以下の方法1
方法2が追記した方法で、forループで回しつつif文で要素を選択。結果はlistなので[0]にて最初の要素を取り出し
方法2の方が僅かに読みやすいかもしれませんね。
方法1: ワイルドカード指定のindexを使用する。
例
なお、.ftではなく、.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 }"
}
}
}
内容を見やすくする為に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" ) は、nameが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ディレクトリーは作成されなかった。
追記
なお、リソースからワイルドカードによる取得を行った際、要素のタイプが揃わず?indexがエラーとなるケースがあった。terraformかproviderのバグ?
indexに、for文にて作成したリストを与える事で回避した。
indexにfor文で作成したリストを渡して回避した例
{
"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"
}
以下同様
Chef(というか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
ワイルドカードに少し触れている。
https://www.terraform.io/docs/configuration-0-11/resources.html
公式のindexの項にはワイルドカードの説明は見つけられなかった。
https://www.terraform.io/docs/configuration/functions/index.html
参考: ansible