Last Updated: April 23, 2019
·
43.19K
· mpdehaan

Use 'with_items' with complex arguments to simplify handler logic

In Ansible, you may need to perform multiple actions that notify a common handler. An example of this might be is if you are laying down multiple configuration files for an application and need to restart a service if any of those files change.

Ordinarily you would do this in your 'tasks' section:

 - name: template the first thing and restart on change
   template: src=templates/foo.j2 dest=/etc/splat/foo.conf
   notify: 
      - restart fooserv

- name: template the second thing and restart on change
  template: src=templates/bar.j2 dest=/etc/splat/bar.conf
  notify: 
     - restart fooserv

- # etc for all the things you need to template

This can be simplified by using "with_items" and a single notify statement. If any of the tasks change, the service will be notified in exactly the same way that it needs to restart at the end of the playbook run.

- name:  template everything for fooserv
  template: src=${item.src} dest=${item.dest}
  with_items:
     - { src: 'templates/foo.j2', b: '/etc/splat/foo.conf' }
     - { src: 'templates/bar.j2', b: '/etc/splat/bar.conf' }
  notify: 
     - restart fooserv

Note that since we have tasks that take more than one unique argument, we don't just say "$item" in the 'template:' line, but use with_items with a hash (dictionary) variable. You can also keep it a little shorter by using lists, if you like. This is a stylistic preference:

- name:  template everything for fooserv
  template: src=${item.0} dest=${item.1}
  with_items:
     - [ 'templates/foo.j2', '/etc/splat/foo.conf' ]
     - [ 'templates/bar.j2', '/etc/splat/bar.conf' ]
  notify: 
     - restart fooserv

Of course we could also define the list you were walking over in another file, like a "groupvars/webservers" file to define all the variables needed for the webservers group, or a YAML file loaded from the "varsfiles" directive inside the playbook. Look how this can clean up if we do.

- name: template everything for fooserv
  template: src=${item.src} dest=${item.dest}
  with_items: ${fooserv_template_files}
  notify: 
      - restart fooserv

6 Responses
Add your response

In the 2nd code snippet, shouldn't 'b:' be 'dest:' ?

over 1 year ago ·

That's what I too was wondering. :)
Anyway nice tutorial.

over 1 year ago ·

Variable references are now {{ item }} instead of ${item} as of 1.2 (i believe)

over 1 year ago ·

Also the second form will result in one big list.

This is because you usually want:

- yum: name={{ item }} state=installed
  with_items:
     - alist
     - blist

As such, use the hash form if you want to pass multiple things, the item.0 and item.1 approach is not going to work as above.

over 1 year ago ·

Maybe with a jinja2 filter like regex (I don't know if it is available till) or remove suffix could fix the issue to template in mass

over 1 year ago ·

yes, I was managed to copy files with regex (ansible 1.8.2):

- name: deploy configs
  template: src={{ item }} dest=/var/{{ item | basename }}
  with_fileglob:
  - /path/to/playbooks/configs/roles/rolename/templates/*.xml
over 1 year ago ·