How to write an infinite loop with Ansible

Voluntarily writing an infinite loop in Ansible is not immediately obvious: both the loop and with_* family of keywords expect some kind of list or other iterable structure which are all inherently finite. It is not possible to write loop: ∞ after all.

Such a loop could be used as a far more flexible alternative to wait_for or until, which can both rapidly grow to become unreadable.

In this short article I will explain how to do it.

My first thought was to reach to import_playbook:

# self.yml
---
- name: Import self
  import_playbok: self.yml

But depending on your exact configuration only two things can happen:

The module import_playbook is static so ansible-playbook will load the file self.yml again and again before trying to make sense of it, ultimately trashing its allowed quota of memory (see § Re-using Ansible artifacts for more explanations). Static re-use of a ressource is thus out of the question and only a dynamic method would work, meaning an include_* module. As include_playbook doesn’t exist there is only one solution: to use the next best thing, that is include_tasks.

Two files are needed because import_tasks can not grok those pesky hosts and gather_facts keywords:

# loop.yml
---
- hosts: localhost
  gather_facts: no
  tasks:
    - include_tasks: loop-task.yml
# loop-task.yml
---
- debug:
    msg: "Hello!"

- include_tasks: loop-task.yml

Let’s run this:

$ ansible-playbook loop.yml
PLAY [localhost] ***************************************************************

TASK [include_tasks] ***********************************************************
included: /bla/bla/loop-task.yml for localhost

TASK [debug] *******************************************************************
ok: [localhost] => 
  msg: Hello!

TASK [include_tasks] ***********************************************************
included: /bla/bla/loop-task.yml for localhost

TASK [debug] *******************************************************************
ok: [localhost] => 
  msg: Hello!
...

Hooray!

If there is a need to put a limit to the number of recursion a simple counter mechanism can be put into place:

# loop-task.yml
---
- name: Increase counter.
  set_fact:
    counter: "{{ counter | default(-1) | int + 1 }}"

- debug:
    msg: "{{ counter | int }}"

- include_tasks: loop-task.yml
  when: 'counter | int < 5'

Changelog