てくなべ (tekunabe)

ansible / network / automation

[Ansible] ループにリストの変数を渡すときの(私が)忘れがちな挙動差分

■ はじめに

ループするためにリストの変数を渡すときに、以下の2パターンを特に区別なく使ってしまうことがあります(私が)。

  • pattern1
- name: pattern1
    debug:
    msg: "{{ item }}"
    with_items: "{{ test_list }}"  # ここにリストの変数を渡す
  • pattern2
- name: pattern2
    debug:
    msg: "{{ item }}"
    with_items:
      - "{{ test_list }}"   # ここにリストの変数を渡す

どちらも結果は同じなのですが、内部の挙動が異なります。

この記事では、簡単な例で違いを説明します。


■ 動作確認

Playbook

以下のパターンのタスクを準備します。

pattern 説明
pattern1 with_items に直接リストを指定
pattern2 with_items の最初の要素にリストを指定
pattern3 loop に直接リストを指定
pattern4 loop の最初の要素にリストを指定

この中で一つだけ結果が異なるパターンがあります。

- hosts: localhost
  gather_facts: no

  vars:
    test_list:   # ループに使うリストの変数
      - a
      - b
      - c

  tasks:
    - name: pattern1
      debug:
        msg: "{{ item }}"
      with_items: "{{ test_list }}"
    
    - name: pattern2
      debug:
        msg: "{{ item }}"
      with_items:
        - "{{ test_list }}"

    - name: pattern3
      debug:
        msg: "{{ item }}"
      loop: "{{ test_list }}"

    - name: pattern4
      debug:
        msg: "{{ item }}"
      loop:
        - "{{ test_list }}"

実行結果

実行して確認します。

$ ansible-playbook -i localhost, looplist.yml

PLAY [localhost] ************************************************************

TASK [pattern1] ************************************************************
ok: [localhost] => (item=a) => {
    "msg": "a"
}
ok: [localhost] => (item=b) => {
    "msg": "b"
}
ok: [localhost] => (item=c) => {
    "msg": "c"
}

TASK [pattern2] ************************************************************
ok: [localhost] => (item=a) => {
    "msg": "a"
}
ok: [localhost] => (item=b) => {
    "msg": "b"
}
ok: [localhost] => (item=c) => {
    "msg": "c"
}

TASK [pattern3] ************************************************************
ok: [localhost] => (item=a) => {
    "msg": "a"
}
ok: [localhost] => (item=b) => {
    "msg": "b"
}
ok: [localhost] => (item=c) => {
    "msg": "c"
}

TASK [pattern4] ************************************************************
ok: [localhost] => (item=[u'a', u'b', u'c']) => {
    "msg": [
        "a",
        "b",
        "c"
    ]
}

PLAY RECAP ******************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0

解説

pattern1 -3 が一次元のリストとしてループしたのに対して、pattern4 だけ二次元のリストとしてループしました。

pattern4 では、リスト0番目の要素に ["a", "b", "c"] というリストが入っている状態です。

patterm 2 も構造上は二次元のリストですが、with_times がループ時に事前に、一次元のリストに flatten します。その結果、pattern1 や 3 と同じ結果になります。


■ まとめ

with_items を利用したループにおいて、同じ結果でも内部の挙動が異なる点をご紹介しました。

loop との違いや、リストが意図した通りに処理されているかは意識したほうが良いと思いました。