3

I have a list that I would like to use to see if any of the members of the list are in a second list, and use that in an Ansible when clause. Can this be done in a one-liner?

- name: task include moar ansible
  include: more_tasks.yaml
  when: any_member_of_this_list in some_other_list
Randy
  • 908
  • 12
  • 30

2 Answers2

7

This is quite easy. Ansible introduced some extra Jinja filters, and one of them is intersect, which returns unique elements which are contained in two lists. Since an empty list is falsy and a non-empty list is truthy, there is not more to be done than this:

when: any_member_of_this_list | intersect(some_other_list)
udondan
  • 57,263
  • 20
  • 190
  • 175
2

I'm sure there's probably a way to do it in one play with a lot of fancy jinja2 commands, but you may be less likely to go insane by using your own custom plugin. If you create a 'filter_plugins' directory in the root of the folder you keep your ansible scripts, Ansible will automagically find them. Here's my 'filter_plugins/contains_any.py' file:

# lambda gratefully 'borrowed' from:
# https://stackoverflow.com/questions/10668282/one-liner-to-check-if-at-least-one-item-in-list-exists-in-another-list
contains_any = lambda a, b: any(i in b for i in a)
class FilterModule(object):

    def filters(self):
        return {
            'contains_any': contains_any
        }

In your playbook, you can use the 'set_fact' module to set the the True/False value based on whether there are any matches between the lists. When you pipe 'the_list' into 'contains_any', it automatically sets 'the_list' as the first variable, so you only need to explicitly pass 'the_other_list':

---
- hosts: localhost
  vars:
   the_list: [jane, bill, janet, suzy]
   the_other_list: [jane, steve, bob, sam, turkey]
  tasks:
  - set_fact:
     any_matches: "{{ the_list | contains_any(the_other_list) }}"
  - debug: msg="Success!"
    when: any_matches

Here's the output:

PLAY ***************************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [set_fact] ****************************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
    "msg": "Success!"
}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0   

EDIT: Thanks to this answer for the lambda function

Community
  • 1
  • 1
rumdrums
  • 1,322
  • 2
  • 11
  • 25
  • You only need one fancy jinja filter. :) I think your lambda function does exactly what the `intersect ` filter does. See my other answer. – udondan Jan 30 '16 at 05:20
  • This is a big hammer, but now I know how to do it for even more complicated stuff. Thanks for the answer. – Randy Feb 01 '16 at 16:37
  • @Randy Hah! Yeah, definitely a big hammer, particularly given udondan's one-liner, but it's definitely good to know that building custom filters is quite easy. – rumdrums Feb 01 '16 at 17:52