アレイから、あるキーが特定の値であるハッシュを取り出す (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メソッドが使用可能。

方法: ワイルドカード指定のindexを使用する。

なお、.ftではなく、.tf.jsonを使用

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 }"
    }
  }
}

内容を見やすくする為に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文で作成したリストを渡して回避した例

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( [ 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の例

selectattr.yml
- 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 | json_query("[?name=='n2'].attr"))[0] }}
  • json_query には sudo pip install jmespath などが必要
$ ansible-playbook -i localhost, -c local selectattr.yml
PLAY [all] *******************************************************************************************************

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

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

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

PLAY RECAP *******************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Chef(というかRuby)の例

select.rb

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