pythonloopsansibleconditional-statements

Nested loops using list and dict in ansible


I have 2 types of input data in ansible. Firs is a list of strings:

- '1.1.1.1'
- '2.2.2.2'

Second is a list of dicts.

- {'name': 'obj1', 'addr': '1.1.1.1'}
- {'name': 'objx', 'addr': 'x.x.x.x'}

I don't know in advance if items of first list are in the addr filed of the second list. Therefore I have to perform evaluation and for this I have to use nested loops. I need to loop through the first list and loop through the second list and then perform conditional check if item of first list is equal to item.addr of the second list. However I have no clue how to distinguish items of the first list and items of the second list in the ansible conditions.

In python I would achieve similar thing by using following expression:

for add in my_list: 
    for obj in my_list2: 
        if add == obj['addr']: 
            new_list.append([obj])

In ansible it should be something like this:

- set_fact:
    new_list: "{{ new_list }} + [ {'name': '{{ item_second_list.name }}', 'address': '{{ item_second_list.addr }}'} ]"
  when: item_first_list == item_second_list.addr
  with_list: first_list
  with_list: second_list

Solution

  • It is an error to have two identical keys in a YAML dictionary. In other words, you can't use with_list twice like that: it should generate an error, or if not, everything but the final one will be ignored. For the most part, Ansible doesn't directly support nested loops.

    It looks like you want to produce a list of addresses that are contained in a list of dictionaries. That is, given:

    list1:
      - 1.1.1.1
      - 2.2.2.2
      - 3.3.3.3
    
    list2:
      - {'name': 'obj1', 'addr': '1.1.1.1'}
      - {'name': 'objx', 'addr': '2.2.2.2'}
    

    You want to produce a new list:

    list3:
      - 1.1.1.1
      - 2.2.2.2
    

    We can do that without a nested list by asking for all those addresses in list2, and then selecting only those that are in list1:

    ---
    - hosts: localhost
      gather_facts: false
      vars:
        list1:
          - 1.1.1.1
          - 2.2.2.2
          - 3.3.3.3
    
        list2:
          - name: obj1
            addr: 1.1.1.1
          - name: obj2
            addr: 2.2.2.2
    
      tasks:
        - set_fact:
            list3: "{{ (list3|default([])) + [item.addr] }}"
          when: item.addr in list1
          loop: "{{ list2 }}"
    
        - debug:
            var: list3
    

    This will output:

    [...]
    
    TASK [debug] *************************************************************************
    ok: [localhost] => {
        "list3": [
            "1.1.1.1",
            "2.2.2.2"
        ]
    }