Search code examples
loopsansible

How to iterate through a list by hosts, not by items?


I have the following Ansible playbook (im using 2.9.21), which is working perfectly fine, just as I want it to work:

- name: Sync package_00 package to the remote hosts
  ansible.builtin.synchronize:
    src: /home/ansible/files/package_00
    dest: /tmp/
    recursive: false
    archive: false
    checksum: true
    delete: false

- name: Sync package_01 package to the remote hosts
  ansible.builtin.synchronize:
    src: /home/ansible/files/package_01
    dest: /tmp/
    recursive: false
    archive: false
    checksum: true
    delete: false

- name: Sync package_02 package to the remote hosts
  ansible.builtin.synchronize:
    src: /home/ansible/files/package_02
    dest: /tmp/
    recursive: false
    archive: false
    checksum: true
    delete: false

It correctly grabs the first task and copies the first file package_00 to all the hosts specified. As soon as it is ready with the first file, it starts the second file.

Now, since I have a huge number of files and the above mentioned example is quite ugly it would be nice to have some loop or with_items solution, but unfortunately I was unable to achieve the exact same functionality as described. Whenever I change to something like this, the execution is completely different:

- name: Sync the packages to the remote host
  ansible.builtin.synchronize:
    src: /home/ansible/files/package_{{ item }}
    dest: /tmp/
    recursive: false
    archive: false
    checksum: true
    delete: false
  loop: "{{ range(0, 100) | list }}"

Now Ansible tries to copy all the files to the first host, and all the files to the second host, and so on.

I have tried to experiment with loop, with_items, serial keyword, but no luck.


Solution

  • With significant overhead, decreased performance and increased runtime, the following minimal example show how to achieve such behavior.

    Inventory example.ini

    [managed]
    remote.example.com
    managed.example.com
    external.example.com
    

    Task file debug.yml

      - name: "Loop included {{ item }}"
        debug:
          msg: "package_{{ ansible_loop.index0 }}"
    

    Playbook main.yml

    ---
    - hosts: managed
      become: false
      gather_facts: false
    
      tasks:
    
        - name: Loop directly
          debug:
            msg: "package_{{ item }}"
          loop: "{{ range(0, 3) | list }}"
    
        - name: Include
          include_tasks:
            file: debug.yml
          loop: "{{ ansible_play_hosts }}"
          loop_control:
            extended: true
    

    Called via

    ansible-playbook --inventory example.ini main.yml
    

    Resulted output and runtime

    PLAY [managed] *****************************************************************************************************
    Friday 04 October 2024  18:46:44 +0200 (0:00:00.027)       0:00:00.027 ********
    
    TASK [Loop directly] ***********************************************************************************************
    ok: [remote.example.com] => (item=0) =>
      msg: package_0
    ok: [remote.example.com] => (item=1) =>
      msg: package_1
    ok: [remote.example.com] => (item=2) =>
      msg: package_2
    ok: [managed.example.com] => (item=0) =>
      msg: package_0
    ok: [managed.example.com] => (item=1) =>
      msg: package_1
    ok: [external.example.com] => (item=0) =>
      msg: package_0
    ok: [managed.example.com] => (item=2) =>
      msg: package_2
    ok: [external.example.com] => (item=1) =>
      msg: package_1
    ok: [external.example.com] => (item=2) =>
      msg: package_2
    Friday 04 October 2024  18:46:44 +0200 (0:00:00.065)       0:00:00.093 ********
    
    TASK [Include] *****************************************************************************************************
    included: debug.yml for remote.example.com, managed.example.com, external.example.com => (item=remote.example.com)
    included: debug.yml for remote.example.com, managed.example.com, external.example.com => (item=managed.example.com)
    included: debug.yml for remote.example.com, managed.example.com, external.example.com => (item=external.example.com)
    Friday 04 October 2024  18:46:45 +0200 (0:00:00.048)       0:00:00.141 ********
    
    TASK [Loop included remote.example.com] ****************************************************************************
    ok: [remote.example.com] =>
      msg: package_0
    ok: [managed.example.com] =>
      msg: package_0
    ok: [external.example.com] =>
      msg: package_0
    Friday 04 October 2024  18:46:45 +0200 (0:00:00.037)       0:00:00.179 ********
    
    TASK [Loop included managed.example.com] ***************************************************************************
    ok: [remote.example.com] =>
      msg: package_1
    ok: [managed.example.com] =>
      msg: package_1
    ok: [external.example.com] =>
      msg: package_1
    Friday 04 October 2024  18:46:45 +0200 (0:00:00.036)       0:00:00.215 ********
    
    TASK [Loop included external.example.com] **************************************************************************
    ok: [remote.example.com] =>
      msg: package_2
    ok: [managed.example.com] =>
      msg: package_2
    ok: [external.example.com] =>
      msg: package_2
    
    PLAY RECAP *********************************************************************************************************
    external.example.com       : ok=7    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    managed.example.com        : ok=7    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    remote.example.com         : ok=7    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    Playbook run took 0 days, 0 hours, 0 minutes, 0 seconds
    Friday 04 October 2024  18:46:45 +0200 (0:00:00.067)       0:00:00.283 ********
    ===============================================================================
    Loop included external.example.com --------------------------------------------------------------------------- 0.07s
    Loop directly ------------------------------------------------------------------------------------------------ 0.07s
    Include ------------------------------------------------------------------------------------------------------ 0.05s
    Loop included remote.example.com ----------------------------------------------------------------------------- 0.04s
    Loop included managed.example.com ---------------------------------------------------------------------------- 0.04s
    

    Please take note that

    "... since I have a huge number of files ... achieve the exact same functionality as described."

    that should be avoided and you should overthink your Use Case.