kappa
kappa

Reputation: 1

Ansible loop with array

How can we create a code as below?

- name: TEST1
  set_fact:
    list_a: "{{ list_a + [item.json.SearchResult.resources] }}"
  with_items: 
    - "{{ source_list.results[0] }}"
    - "{{ source_list.results[1] }}"
    - "{{ source_list.results[x] }}"
    ... (unknown how many items in result from API)
  vars:
    list_a: []

source_list.results[x] comes from an API result. The reason why I need to create an array is that the number of API result is maximum 100. But there are over 500 items.

Upvotes: 0

Views: 3606

Answers (2)

Zeitounator
Zeitounator

Reputation: 44615

You are taking this the wrong way. Simply extract the attribute you need from each result using the map(attribute=x) Jinja2 filter.

Since you did not provide an example data structure, I inferred below that:

  • you called your API with ansible.builtin.uri in a loop to get batches of 100 results which are returned as a list in the SearchResult.ressources field
  • you want in the end a flattened list where all resources are at top level
- name: Show my list of single attributes
  ansible.builtin.debug:
    var: "source_list.results
      | map(attribute='json.SearchResult.resources') | flatten"

You actually don't need to set_fact:

  • For a single use, just use the above expression directly in the relevant parameter (e.g. loop or a module param....) or eventually declare this in a var at task level.
  • If you want to reuse this in different parts of your playbook, just declare a var at play level and expand it anywhere once you have called your API and populated the source_list var. In that case, just add a default value to prevent an error if API was not yet called.

Example for the second case above in this pseudo playbook

---
- hosts: localhost
  gather_facts: false

  vars:
    list_a: "{{ source_list.results | d([])
      | map(attribute='json.SearchResult.resources') | flatten }}"

  tasks:
    - name: "This will return an empty list (i.e. [])
        as we did not populate source_list yet"
      ansible.builtin.debug:
        var: list_a

    - name: Call our API and register source_list
      ansible.builtin.uri:
        uri: https://my.api.com/api/v1/some/endpoint
        # [... more parameters here ... ]
      loop: "{{ my_list_of_ressources }}"
      register: source_list

    - name: "This will now return a populated list
        after calling the API and registering source_list"
      ansible.builtin.debug:
        var: list_a

Now, to still give a direct answer to your initial question: you can construct that list iteratively inside a set_fact task. This is definitely not efficient as it involves a task running inside a loop (both unneeded as demonstrated above) and possibly on multiple hosts in your play. But for learning purpose, here it is:

- name: very inefficient way to get the same result as above
  set_fact:
    list_a: "{{ list_a | d([]) + item.SearchResult.resources }}"
  loop: "{{ source_list.results }}"

Upvotes: 2

kappa
kappa

Reputation: 1

After reading @Zeitounator's answer, I realized I was taking this the wrong way. I used the idea in the answer (json query and flatten) and I changed my task to the below which works as expected.

- name: Create ID list from API result data
  set_fact:
    list_a: "{{ source_list | json_query('results[*].json.SearchResult.resources[*].id') | flatten }}"
    
- name: API get again with id
  uri:
    url: "{{ request_url }}/{{ item }}"
    ... 
  register: result_data
  with_items:
    - "{{ list_a }}"

Upvotes: 0

Related Questions