Issue looping on block module for a set of tasks in Ansible

19,331

TL;DR

'with_items' is not a valid attribute for a Block

The error message says it all: you cannot loop over a block.

If you need to loop over a set of tasks, put them in a separate file and use include_tasks

Implementation (and some good practice...)

Below is an implementation based on your example illustrating the solution.

Since your question and code lacks some precision and since I pointed out some bad practices, please note that:

  • I fixed the looped code to effectively use the filenames you loop on (I inferred it was supposed to the deploy.db file). Note the use of loop_control to disambiguate the variable name in the included file (i.e. db_filename).
  • I made the code idempotent as much as possible by using the ansible module copy in place of shell and dropped the touch phase.
  • I transformed the var names to all lowercase and underscore separator.
  • To make sure the copy task works on all occasion, I replaced the removed tasks with a single making sure the basepath dir exists.
  • I added a unique filter after filenames.split(',') as well as a trim filter on each value to remove possible duplicates and eventual spaces added by error in the coma separated list.
  • I used not keyword and bool filter (for extra security) rather than a bare compare to a boolean False value.

Here is the included file create_db_each.yml

---
- name: Check if file exists
  stat:
    path: "{{ basepath }}/{{ db_filename }}"
  register: currdb

- name: Create the file with "done" line if not present
  copy:
    content: "done"
    dest: "{{ basepath }}/{{ db_filename }}"
  when: not currdb.stat.exists | bool

used in the following create_db.yml playbook

---
- name: "Create my dbs"
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Make sure the base directory exists
      file:
        path: "{{ basepath }}"
        state: directory

    - name: load each db
      include_tasks: "create_db_each.yml"
      when: layer == 'APP'
      loop: "{{ filenames.split(',') | unique | map('trim') }}"
      loop_control:
        loop_var: db_filename

which gives


notes:

  • first run only, run it again on your side to witness it reports OK everywhere
  • see the filenames parameter value to illustrate the use of unique and trim

$ ansible-playbook -e basepath=/tmp/my/base/path -e "filenames='a.bla, b.toto, c , z.txt,a.bla'"  -e layer=APP create_db.yml

PLAY [Create my dbs] ************************************************

TASK [Make sure the base directory exists] **************************
changed: [localhost]

TASK [load each db] *************************************************
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=a.bla)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=b.toto)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=c)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=z.txt)

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

PLAY RECAP **********************************************************
localhost: ok=13   changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ tree /tmp/my/base/path/
/tmp/my/base/path/
├── a.bla
├── b.toto
├── c
└── z.txt

$ for f in /tmp/my/base/path/*; do cat $f; echo; done
done
done
done
done
Share:
19,331
Ashar
Author by

Ashar

Updated on July 05, 2022

Comments

  • Ashar
    Ashar almost 2 years

    I need to check if deploy.db file exists. If it does not exist, then I need to perform a set of tasks for which I'm using block.

    Below is how i run the playbook

    ansible-playbook test.yml -e Layer=APP -e BASEPATH="/logs" -e Filenames="file1,file2,file3"
    

    Below is my complete playbook:

    ---
    - name: "Play 1"
      hosts: localhost
      gather_facts: false
      tasks:
       - name: Construct 
         debug:
            msg: "Run"
       - block:
           - stat: path="{{ BASEPATH }}/deploy.db"
             register: currdb
           - file: path="{{ BASEPATH }}/deploy.db" state=touch recurse=no
             when: currdb.stat.exists == False
           - shell: "echo done>>{{ BASEPATH }}/deploy.db"
             when: currdb.stat.exists == False
         when: Layer == 'APP'
         with_items:
           - "{{ Filenames.split(',') }}" 
    

    I'm getting the below error running the playbook:

    ERROR! 'with_items' is not a valid attribute for a Block
    
    The error appears to be in '/app/test.yml': line 9, column 6, but may
    be elsewhere in the file depending on the exact syntax problem.
    
    The offending line appears to be:
    
       - block:
         ^ here
    

    After researching a bit, I understand that neither with_items nor loop is supported by block module and the solution is to include task file.

    I'm however, not sure how to get that to work. Can you please suggest what tweaks I need to inorder for my playbook to work ?

    Considering i m on the latest version of Ansible are there other solutions.