コンテンツにスキップ

ディレクティブ

Playbook Keywords — Ansible Documentation

ループ

ループとblockは併用不可

  tasks:
    - name: block with loop
      loop: "{{ item_list }}"
      block:
        :

こういうのは無理

ERROR! 'loop' is not a valid attribute for a Block

Blocks — Ansible Documentation

Most of what you can apply to a single task (with the exception of loops) can be applied at the block level, so blocks make it much easier to set data or directives common to the tasks.

ループとregisterの併用

    - name: exec command
      ansible.builtin.command: "{{ item }}"
      loop:
        - hostname
        - pwd
        - ls /opt
      register: result_command

    - name: print result
      debug:
        msg: '{{ result_command }}'

を実行すると、以下のようになる。

ok: [localhost] => 
  msg:
    changed: true
    msg: All items completed
    results:
    - ansible_loop_var: item
      changed: true
      cmd:
      - hostname
      delta: '0:00:00.001672'
      end: '2021-04-06 18:54:28.461799'
      failed: false
      invocation:
        module_args:
          ...
      item: hostname
      rc: 0
      start: '2021-04-06 18:54:28.460127'
      stderr: ''
      stderr_lines: []
      stdout: cloud-dev
      stdout_lines:
      - cloud-dev
    - ansible_loop_var: item
      changed: true
      cmd:
      - pwd
      delta: '0:00:00.001590'
      end: '2021-04-06 18:54:28.630725'
      failed: false
      invocation:
        module_args:
          ...
      item: pwd
      rc: 0
      start: '2021-04-06 18:54:28.629135'
      stderr: ''
      stderr_lines: []
      stdout: /home/zaki/src/ansible-sample/file
      stdout_lines:
      - /home/zaki/src/ansible-sample/file
    - ansible_loop_var: item
      changed: true
      cmd:
      - ls
      - /opt
      delta: '0:00:00.001935'
      end: '2021-04-06 18:54:28.798976'
      failed: false
      invocation:
        module_args:
          ...
      item: ls /opt
      rc: 0
      start: '2021-04-06 18:54:28.797041'
      stderr: ''
      stderr_lines: []
      stdout: |-
        cni
        containerd
      stdout_lines:
      - cni
      - containerd

要は、${registerで指定した変数名}.results[...]という配列になる。

ループ変数のカスタム

loop_control.loop_varで指定できる。

    - name: loop
      debug:
        msg: "{{ zzz }}"
      loop:
        - foo
        - bar
        - baz
        - qux
      loop_control:
        loop_var: zzz

{{ item }}は使用不可となる。

ループインデックスの参照

タスク内でループインデックスを参照したい場合は、extended: trueを付加すれば参照できる。

- name: loop index sample
  debug:
    msg:
    - "{{ item }}"
    - "{{ ansible_loop.index }}"
    - "{{ ansible_loop.index0 }}"
  loop:
    - foo
    - bar
    - baz
  loop_control:
    extended: true

実行結果は以下の通り。

ok: [localhost] => (item=foo) => 
  msg:
  - foo
  - '1'
  - '0'
ok: [localhost] => (item=bar) => 
  msg:
  - bar
  - '2'
  - '1'
ok: [localhost] => (item=baz) => 
  msg:
  - baz
  - '3'
  - '2'

使用可能な変数はこちら→ Extended loop variables

ループとinclude併用時にループに関係ないタスクを1回だけ実行することは…

run_once使って出来ないか試したけど予想通り無効。
たぶん無理なので、そういうタスクはループの外で実装しましょう。

  - name: include with loop
    include_tasks: dup-loop-included.yml
    loop: "{{ users }}"
- name: 1回だけ実行したいタスク
  debug:
    msg: zzz
  run_once: true

このタスクはusersの要素のループごとに当然毎回実行される。
ループ内で毎回実行される必要がないのであれば、includeの外で実行しましょう。

when

基本

tasks:
- module:
  vars:
    cond: (bool)
  when:
    - cond

when部分は変数参照の "{{ values }}" の表記は不要で、bool型の条件式・変数をそのまま書けば良い。

条件がint型

  tasks:
    - name: len bool
      debug:
      loop:
        - 1
        - 0
        - -1
      when: item

0の場合はskip。
1, -1は処理対象。

ansible-core 2.11.3で確認

条件がstring型

    - name: str
      debug:
      loop:
        - "a"
        - ""
      when: item

""の場合はskip。
"a"の場合は処理される。

ansible-core 2.11.3で確認 / 2.9だと構文エラー(文字列のみの判定はできないっぽい?何かと比較するなど評価すればもちろんOK)

リストで複数条件

whenにはリスト形式で条件を複数指定できる。リストの場合は「すべての条件がtrueの場合」にタスクは実行。

---
- hosts: localhost
  gather_facts: false
  vars:
    cond1: true
    cond2: false

  tasks:
    - debug:
        msg: "hello"
      when:
        - cond1
        - cond2

この場合スキップされる。

orで複数条件

リスト形式はandになるため、orで条件を羅列して一つの条件式として記述する。

when: cond == "foobar" or cond == "bazqux"

定義されてる場合/されてない場合

foobar_valueが未定義の場合にタスク実行するには以下。

when: foobar_value is not defined

includeとimport

とりあえずリンク

check mode

ansible-playbook実行時のオプションで--check付与でdry runになるが、check_modeを使うことでタスク単位で強制的にチェックモードのon/offができる。

  - name: command sample
    copy:
      src: /home/zaki/tmp.yml
      dest: /var/tmp
      mode: '0644'
    register: result
    check_mode: false

このパラメタをtrueにすると、--checkを付けなくてもチェックモードで動作する。
falseにすると、--checkで動かしてもチェックモードでなく実処理が行われる。

--checkについてはplaybook参照。

ちなみに、古いバージョンのalways_runと機能的には同じ(値は逆)

Note
Prior to version 2.2 only the equivalent of check_mode: no existed. The notation for that was always_run: yes.

https://docs.ansible.com/ansible/2.9/user_guide/playbooks_checkmode.html#enabling-or-disabling-check-mode-for-tasks

changed_when

条件がtrueの場合はタスクの結果に関係なくchanged判定にする。逆にfalseの場合はok判定にする。
タスクの結果をregisterで保持し、その内容を条件式に組み込むのもOK

    - name: command always run and no change
      ansible.builtin.shell:
        cmd: hogehoge
      changed_when: exec_result.stdout != ''
      register: exec_result

hogehogeの実行結果で標準出力が何も無い場合はchangedになる。

until

[Ansible] untilを使って非同期処理が完了するまで次のtaskを待つ - zaki work log

使用例

    - name: install flannel
      shell: kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.yml

      # todo: applyの処理がunchangedの場合も、ansibleではchanged扱いになっている

    - name: wait for Node-Ready
      shell: kubectl get node --no-headers | awk '{print $2}'
      register: node_state
      until: node_state.stdout == "Ready"
      retries: 10
      delay: 5
      changed_when: false
      # 待つだけなので強制no changed

initialize-kubeadm-ansible/deploy_cni.yaml at e48d1a86ef9f4d94853c18246708490f842b8058 · zaki-lknr/initialize-kubeadm-ansible

include_tasksとは併用不可?

async + poll

[Ansible] asyncとpollを使った非同期処理とループの並列実行 - zaki work log