.. _ansible.utils.index_of_lookup:


**********************
ansible.utils.index_of
**********************

**Find the indices of items in a list matching some criteria**


Version added: 1.0.0

.. contents::
   :local:
   :depth: 1


Synopsis
--------
- This plugin returns the indices of items matching some criteria in a list.
- When working with a list of dictionaries, the key to evaluate can be specified.
- **index_of** is also available as a **filter plugin** for convenience.
- Using the parameters below- ``lookup('ansible.utils.index_of', data, test, value, key, fail_on_missing, wantlist``).




Parameters
----------

.. raw:: html

    <table  border=0 cellpadding=0 class="documentation-table">
        <tr>
            <th colspan="1">Parameter</th>
            <th>Choices/<font color="blue">Defaults</font></th>
                <th>Configuration</th>
            <th width="100%">Comments</th>
        </tr>
            <tr>
                <td colspan="1">
                    <div class="ansibleOptionAnchor" id="parameter-"></div>
                    <b>data</b>
                    <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
                    <div style="font-size: small">
                        <span style="color: purple">list</span>
                         / <span style="color: purple">elements=raw</span>
                         / <span style="color: red">required</span>
                    </div>
                </td>
                <td>
                </td>
                    <td>
                    </td>
                <td>
                        <div>A list of items to enumerate and test against.</div>
                </td>
            </tr>
            <tr>
                <td colspan="1">
                    <div class="ansibleOptionAnchor" id="parameter-"></div>
                    <b>fail_on_missing</b>
                    <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
                    <div style="font-size: small">
                        <span style="color: purple">boolean</span>
                    </div>
                </td>
                <td>
                        <ul style="margin: 0; padding: 0"><b>Choices:</b>
                                    <li>no</li>
                                    <li>yes</li>
                        </ul>
                </td>
                    <td>
                    </td>
                <td>
                        <div>When provided a list of dictionaries, fail if the key is missing from one or more of the dictionaries.</div>
                </td>
            </tr>
            <tr>
                <td colspan="1">
                    <div class="ansibleOptionAnchor" id="parameter-"></div>
                    <b>key</b>
                    <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
                    <div style="font-size: small">
                        <span style="color: purple">string</span>
                    </div>
                </td>
                <td>
                </td>
                    <td>
                    </td>
                <td>
                        <div>When the data provided is a list of dictionaries, run the test against this dictionary key.</div>
                        <div>When using a <em>key</em>, the <em>data</em> must only contain dictionaries.</div>
                        <div>See <em>fail_on_missing</em> below to determine the behaviour when the <em>key</em> is missing from a dictionary in the <em>data</em>.</div>
                </td>
            </tr>
            <tr>
                <td colspan="1">
                    <div class="ansibleOptionAnchor" id="parameter-"></div>
                    <b>test</b>
                    <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
                    <div style="font-size: small">
                        <span style="color: purple">string</span>
                         / <span style="color: red">required</span>
                    </div>
                </td>
                <td>
                </td>
                    <td>
                    </td>
                <td>
                        <div>The name of the test to run against the list, a valid jinja2 test or ansible test plugin.</div>
                        <div>Jinja2 includes the following tests <a href='http://jinja.palletsprojects.com/templates/#builtin-tests'>http://jinja.palletsprojects.com/templates/#builtin-tests</a>.</div>
                        <div>An overview of tests included in ansible <a href='https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html'>https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html</a>.</div>
                </td>
            </tr>
            <tr>
                <td colspan="1">
                    <div class="ansibleOptionAnchor" id="parameter-"></div>
                    <b>value</b>
                    <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
                    <div style="font-size: small">
                        <span style="color: purple">raw</span>
                    </div>
                </td>
                <td>
                </td>
                    <td>
                    </td>
                <td>
                        <div>The value used to test each list item against.</div>
                        <div>Not required for simple tests (e.g. <code>true</code>, <code>false</code>, <code>even</code>, <code>odd</code>)</div>
                        <div>May be a <code>string</code>, <code>boolean</code>, <code>number</code>, <code>regular expression</code> <code>dict</code> and so on, depending on the <b>test</b> used.</div>
                </td>
            </tr>
            <tr>
                <td colspan="1">
                    <div class="ansibleOptionAnchor" id="parameter-"></div>
                    <b>wantlist</b>
                    <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
                    <div style="font-size: small">
                        <span style="color: purple">boolean</span>
                    </div>
                </td>
                <td>
                        <ul style="margin: 0; padding: 0"><b>Choices:</b>
                                    <li>no</li>
                                    <li>yes</li>
                        </ul>
                </td>
                    <td>
                    </td>
                <td>
                        <div>When only a single entry in the <em>data</em> is matched, the index of that entry is returned as an integer.</div>
                        <div>If set to <code>True</code>, the return value will always be a list, even if only a single entry is matched.</div>
                        <div>This can also be accomplished using <code>query</code> or <code>q</code> instead of <code>lookup</code>.</div>
                        <div><a href='https://docs.ansible.com/ansible/latest/plugins/lookup.html'>https://docs.ansible.com/ansible/latest/plugins/lookup.html</a></div>
                </td>
            </tr>
    </table>
    <br/>




Examples
--------

.. code-block:: yaml

    #### Simple examples

    - ansible.builtin.set_fact:
        data:
          - 1
          - 2
          - 3

    - name: Find the index of 2
      ansible.builtin.set_fact:
        indices: "{{ lookup('ansible.utils.index_of', data, 'eq', 2) }}"

    # TASK [Find the index of 2] *************************************************
    # ok: [nxos101] => changed=false
    #   ansible_facts:
    #     indices: '1'

    - name: Find the index of 2, ensure list is returned
      ansible.builtin.set_fact:
        indices: "{{ lookup('ansible.utils.index_of', data, 'eq', 2, wantlist=True) }}"

    # TASK [Find the index of 2, ensure list is returned] ************************
    # ok: [nxos101] => changed=false
    #   ansible_facts:
    #     indices:
    #     - 1

    - name: Find the index of 3 using the long format
      ansible.builtin.set_fact:
        indices: "{{ lookup('ansible.utils.index_of', data=data, test='eq', value=value, wantlist=True) }}"
      vars:
        value: 3

    # TASK [Find the index of 3 using the long format] ***************************
    # ok: [nxos101] => changed=false
    #   ansible_facts:
    #     indices:
    #     - 2

    - name: Find numbers greater than 1, using loop
      debug:
        msg: "{{ data[item] }} is {{ test }} than {{ value }}"
      loop: "{{ lookup('ansible.utils.index_of', data, test, value) }}"
      vars:
        test: '>'
        value: 1

    # TASK [Find numbers great than 1, using loop] *******************************
    # ok: [sw01] => (item=1) =>
    #   msg: 2 is > than 1
    # ok: [sw01] => (item=2) =>
    #   msg: 3 is > than 1

    - name: Find numbers greater than 1, using with
      debug:
        msg: "{{ data[item] }} is {{ params.test }} than {{ params.value }}"
      with_ansible.utils.index_of: "{{ params }}"
      vars:
        params:
          data: "{{ data }}"
          test: '>'
          value: 1

    # TASK [Find numbers greater than 1, using with] *****************************
    # ok: [nxos101] => (item=1) =>
    #   msg: 2 is > than 1
    # ok: [nxos101] => (item=2) =>
    #   msg: 3 is > than 1


    #### Working with lists of dictionaries

    - ansible.builtin.set_fact:
        data:
          - name: sw01.example.lan
            type: switch
          - name: rtr01.example.lan
            type: router
          - name: fw01.example.corp
            type: firewall
          - name: fw02.example.corp
            type: firewall

    - name: Find the index of all firewalls using the type key
      ansible.builtin.set_fact:
        firewalls: "{{ lookup('ansible.utils.index_of', data, 'eq', 'firewall', 'type') }}"

    # TASK [Find the index of all firewalls using the type key] ******************
    # ok: [nxos101] => changed=false
    #   ansible_facts:
    #     firewalls:
    #     - 2
    #     - 3

    - name: Find the index of all firewalls, use in a loop
      debug:
        msg: "The type of {{ device_type }} at index {{ item }} has name {{ data[item].name }}."
      loop: "{{ lookup('ansible.utils.index_of', data, 'eq', device_type, 'type') }}"
      vars:
        device_type: firewall

    # TASK [Find the index of all firewalls, use in a loop, as a filter] *********
    # ok: [nxos101] => (item=2) =>
    #   msg: The type of firewall at index 2 has name fw01.example.corp.
    # ok: [nxos101] => (item=3) =>
    #   msg: The type of firewall at index 3 has name fw02.example.corp.

    - name: Find the index of all devices with a .corp name
      debug:
        msg: "The device named {{ data[item].name }} is a {{ data[item].type }}"
      loop: "{{ lookup('ansible.utils.index_of', data, 'regex', expression, 'name') }}"
      vars:
        expression: '\.corp$'

    # TASK [Find the index of all devices with a .corp name] *********************
    # ok: [nxos101] => (item=2) =>
    #   msg: The device named fw01.example.corp is a firewall
    # ok: [nxos101] => (item=3) =>
    #   msg: The device named fw02.example.corp is a firewall


    #### Working with complex structures from resource modules

    - name: Retrieve the current L3 interface configuration
      cisco.nxos.nxos_l3_interfaces:
        state: gathered
      register: current_l3

    # TASK [Retrieve the current L3 interface configuration] *********************
    # ok: [sw01] => changed=false
    #   gathered:
    #   - name: Ethernet1/1
    #   - name: Ethernet1/2
    #   <...>
    #   - name: Ethernet1/128
    #   - ipv4:
    #     - address: 192.168.101.14/24
    #     name: mgmt0

    - name: Find the indices interfaces with a 192.168.101.xx ip address
      ansible.builtin.set_fact:
        found: "{{ found + entry }}"
      with_indexed_items: "{{ current_l3.gathered }}"
      vars:
        found: []
        ip: '192.168.101.'
        address: "{{ lookup('ansible.utils.index_of', item.1.ipv4 | d([]), 'search', ip, 'address', wantlist=True) }}"
        entry:
          - interface_idx: "{{ item.0 }}"
            address_idxs: "{{ address }}"
      when: address

    # TASK [debug] ***************************************************************
    # ok: [sw01] =>
    #   found:
    #   - address_idxs:
    #     - 0
    #     interface_idx: '128'

    - name: Show all interfaces and their address
      debug:
        msg: "{{ interface.name }} has ip {{ address }}"
      loop: "{{ found|subelements('address_idxs') }}"
      vars:
        interface: "{{ current_l3.gathered[item.0.interface_idx|int] }}"
        address: "{{ interface.ipv4[item.1].address }}"

    # TASK [Show all interfaces and their address] *******************************
    # ok: [nxos101] => (item=[{'interface_idx': '128', 'address_idxs': [0]}, 0]) =>
    #   msg: mgmt0 has ip 192.168.101.14/24


    #### Working with deeply nested data

    - ansible.builtin.set_fact:
        data:
          interfaces:
            interface:
              - config:
                  description: configured by Ansible - 1
                  enabled: true
                  loopback-mode: false
                  mtu: 1024
                  name: loopback0000
                  type: eth
                name: loopback0000
                subinterfaces:
                  subinterface:
                    - config:
                        description: subinterface configured by Ansible - 1
                        enabled: true
                        index: 5
                      index: 5
                    - config:
                        description: subinterface configured by Ansible - 2
                        enabled: false
                        index: 2
                      index: 2
              - config:
                  description: configured by Ansible - 2
                  enabled: false
                  loopback-mode: false
                  mtu: 2048
                  name: loopback1111
                  type: virt
                name: loopback1111
                subinterfaces:
                  subinterface:
                    - config:
                        description: subinterface configured by Ansible - 3
                        enabled: true
                        index: 10
                      index: 10
                    - config:
                        description: subinterface configured by Ansible - 4
                        enabled: false
                        index: 3
                      index: 3


    - name: Find the description of loopback111, subinterface index 10
      debug:
        msg: |-
          {{ data.interfaces.interface[int_idx|int]
              .subinterfaces.subinterface[subint_idx|int]
                .config.description }}
      vars:
        # the values to search for
        int_name: loopback1111
        sub_index: 10
        # retrieve the index in each nested list
        int_idx: |
          {{ lookup('ansible.utils.index_of',
                 data.interfaces.interface,
                     'eq', int_name, 'name') }}
        subint_idx: |
          {{ lookup('ansible.utils.index_of',
                 data.interfaces.interface[int_idx|int].subinterfaces.subinterface,
                     'eq', sub_index, 'index') }}

    # TASK [Find the description of loopback111, subinterface index 10] ************
    # ok: [sw01] =>
    #   msg: subinterface configured by Ansible - 3



Return Values
-------------
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this lookup:

.. raw:: html

    <table border=0 cellpadding=0 class="documentation-table">
        <tr>
            <th colspan="1">Key</th>
            <th>Returned</th>
            <th width="100%">Description</th>
        </tr>
            <tr>
                <td colspan="1">
                    <div class="ansibleOptionAnchor" id="return-"></div>
                    <b>_raw</b>
                    <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
                    <div style="font-size: small">
                      <span style="color: purple">-</span>
                    </div>
                </td>
                <td></td>
                <td>
                            <div>One or more zero-based indicies of the matching list items.</div>
                            <div>See <code>wantlist</code> if a list is always required.</div>
                    <br/>
                </td>
            </tr>
    </table>
    <br/><br/>


Status
------


Authors
~~~~~~~

- Bradley Thornton (@cidrblock)


.. hint::
    Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up.
