はじめに

Ansibleでループ処理をするためにwith_itemsにリストを渡すというのはよくあるが、リストから一部のリストを除外するいい方法について書く。

実行version: Ansible 2.6

リストの除外をする例

SSL自己証明書や正規のSSL証明書をサーバに入れるroleを例にあげたい。

NginxとApacheで以下のような変数を定義し、https: trueとなっている場合はSSL自己証明書を発行したい。

nginx:
  sites:
    - server_name: www.example.com
      https: true
    - server_name: api.example.com
      https: true

ただし次のように追加で定義した場合は正規の証明書をcopyする。

ssl_cert:
  certs:
    - server_name: www.example.com
      local_key_path: /tmp/server.key # Ansible実行サーバにある配布する秘密鍵
      local_crt_path: /tmp/server.crt # Ansible実行サーバにある配布する証明書

これを実現するには、nginx.sitesからssl_cert.certsを引いたリストをwith_itemsに渡さなければいけない。

differenceフィルタ

リストの引き算はdifferenceフィルタを使ってlist1 | difference(list2)で対応できる。

- include_tasks: self_signed_ssl.yml
  with_items:
    - '{{ nginx.sites  | selectattr("https") | map(attribute="server_name") | unique | list | difference(ssl_cert.certs | map(attribute="server_name") | list) }}'
    - '{{ apache.sites | selectattr("https") | map(attribute="server_name") | unique | list | difference(ssl_cert.certs | map(attribute="server_name") | list) }}'

differenceフィルタでも問題ないが、NginxのリストとApacheのリストの両方から正規証明書リストを引かなくてはならず、重複してしまっている。また横に間延びしてしまっている。

when item not in list

このような場合はwhenを使うときれいに書ける。

- include_tasks: self_signed_ssl.yml
  when: item not in (ssl_cert.certs | map(attribute="server_name") | list)
  with_items:
    - '{{ nginx.sites  | selectattr("https") | map(attribute="server_name") | unique | list }}'
    - '{{ apache.sites | selectattr("https") | map(attribute="server_name") | unique | list }}'

これであればwith_itemsにどれだけリストを並べても各ループごとにwhenで除外対象のリストに含まれているかどうかチェックしたうえで実行可否を判断してくれる。