ansiblejinja2nested-loopsansible-2.xansible-filter

Create a dynamic new list by using other lists in Ansible


I've used includ_vars for importing a file which includes some variables as follows:

my_vars:
   - { name: "a", surname: "b", status: "c"}
   - { name: "d", surname: "e", status: "f"}
   - { name: "g", surname: "h", status: "i"}

I'm going to create a new dynamic list by my_vars and use it in get_url task:

- { url: "http://company.com/a/b/c.txt"}
- { url: "http://company.com/d/e/f.txt"}
- { url: "http://company.com/g/h/i.txt"}

- name: "include my_vars"
  include_vars:
    file: "../myvars.yml"

- name: "create new URL list"
  set_fact:
     new_list: "- {url: "http://"http://company.com/{{ my_vars.name }}/{{ my_vars.surname }}/{{ my_vars.status }}.txt
  with_items: {{ my_vars }}

- name: "get desired url"
  get_url:
      url: {{ item.url }}
      dest: /tmp/
  loop: {{ new_list }}

But it doesn't work. How can I create this new iterative list by existing variables for using in other steps?


Solution

  • Join the values

    my_vals: "{{ my_vars|json_query('[].[name, surname, status]')|
                         map('join', '/')|
                         list }}"
    

    gives the list of the fragments

    my_vals:
      - a/b/c
      - d/e/f
      - g/h/i
    

    Use the filter product to combine the fragments. Flatten and join the lists

    my_urls: "{{ ['http://company.com/']|product(my_vals)|
                                         product(['.txt'])|
                                         map('flatten')|
                                         map('join')|
                                         list }}"
    

    gives

    my_urls:
      - http://company.com/a/b/c.txt
      - http://company.com/d/e/f.txt
      - http://company.com/g/h/i.txt
    

    Use the filter community.general.dict_kv if you need the list of the dictionaries

    my_urls_dict: "{{ my_urls|map('community.general.dict_kv', 'url')|
                              list }}"
    

    gives

    my_urls_dict:
      - {url: 'http://company.com/a/b/c.txt'}
      - {url: 'http://company.com/d/e/f.txt'}
      - {url: 'http://company.com/g/h/i.txt'}
    

    Example of a complete playbook

    - hosts: localhost
    
      vars:
        my_vars:
          - {name: "a", surname: "b", status: "c"}
          - {name: "d", surname: "e", status: "f"}
          - {name: "g", surname: "h", status: "i"}
        my_vals: "{{ my_vars|json_query('[].[name, surname, status]')|
                             map('join', '/')|
                             list }}"
        my_urls: "{{ ['http://company.com/']|product(my_vals)|
                                             product(['.txt'])|
                                             map('flatten')|
                                             map('join')|
                                             list }}"
        my_urls_dict: "{{ my_urls|map('community.general.dict_kv', 'url')|
                                  list }}"
    
      tasks:
        - debug:
            var: my_urls
        - debug:
            var: my_urls_dict
    

    Q: "How can I create a loop on objects with status: 'f' ?"

    A: Select the item. For example,

        - debug:
            var: item
          loop: "{{ my_vars|selectattr('status', 'eq', 'f') }}"
    

    gives

    TASK [debug] ************************************************************
    ok: [localhost] => (item={'name': 'd', 'surname': 'e', 'status': 'f'}) => 
      ansible_loop_var: item
      item:
        name: d
        status: f
        surname: e
    

    But this answer seems to be too obvious. You're probably asking how to select such an item when the url is created. There are many options:

    • For example, combine the dictionaries
    my_urls_dict: "{{ my_urls|map('community.general.dict_kv', 'url')|
                              zip(my_vars)|
                              map('combine')|
                              list }}"
    

    gives

    my_urls_dict:
      - {name: a, status: c, surname: b, url: 'http://company.com/a/b/c.txt'}
      - {name: d, status: f, surname: e, url: 'http://company.com/d/e/f.txt'}
      - {name: g, status: i, surname: h, url: 'http://company.com/g/h/i.txt'}
    

    Then, the iteration is as trivial as before.

        - debug:
            var: item
          loop: "{{ my_urls_dict|selectattr('status', 'eq', 'f') }}"
    
    • The next option is the iteration of both lists together
        - debug:
            var: item
          with_together:
            - "{{ my_vars }}"
            - "{{ my_urls }}"
          when: item.0.status == 'f'
    
    • The trivial option would be the concatenation of the url in the loop
        - debug:
            var: url
          loop: "{{ my_vars|selectattr('status', 'eq', 'f') }}"
          vars:
            url: "http://company.com/{{ item.name }}/{{ item.surname }}/{{ item.status }}.txt"