Bootstrap FreeKB - Ansible - Loop through nested list using the with_nested parameter
Ansible - Loop through nested list using the with_nested parameter

Updated:   |  Ansible articles

The following parameters can be used to loop through each item in a list.

The with_nested parameter can be used to loop over two unique lists.

---
- hosts: all
  tasks:
    - ansible.builtin.debug:
        msg: "{{ item[0] }} and {{ item[1] }}"
      with_nested:
        - [ 'apple', 'banana' ]
        - [ 'onion', 'pepper' ]
...

 

In this example, the following should be returned.

TASK [debug] 
ok: [server1.example.com] => (item=['apple', 'onion']) => {
    "msg": "apple and onion"
}
ok: [server1.example.com] => (item=['apple', 'pepper']) => {
    "msg": "apple and pepper"
}
ok: [server1.example.com] => (item=['banana', 'onion']) => {
    "msg": "banana and onion"
}
ok: [server1.example.com] => (item=['banana', 'pepper']) => {
    "msg": "banana and pepper"
}

 

Here is a bit of a more practical example.

---
- hosts: all
  tasks:
  - name: append foo and bar to file1.txt and file2.txt
    ansible.builtin.lineinfile:
      path: "{{ item[0] }}"
      line: "{{ item[1] }}"
    with_nested:
    - [ 'file1.txt', 'file2.txt' ]
    - [ 'foo', 'bar' ]
...    

 

It is important to recognize that with_nested is probably not going to do what you think it would do if you have a dictionary that contains a nested list. Take for example the following.

---
- hosts: all
  vars:
    outer:
      - inner:
        - { foo: hello }
        - { foo: world }
  tasks:
  - ansible.builtin.debug:
      var: out
...

 

Running this playbook should return the following. Notice there are two lists, "outer" and "inner".

ok: [server1.example.com] => {
    "outer": [
        {
            "inner": [
                {
                    "foo": "hello"
                },
                {
                    "foo": "world"
                }
            ]
        }
    ]
}

 

Looping over the "outer" list is totally straight forward.

---
- hosts: all
  vars:
    outer:
      - inner:
        - { foo: hello }
        - { foo: world }
  tasks:
  - ansible.builtin.debug:
      msg: "{{ item }}"
    with_items: "{{ outer }}"
...

 

Which should return something like this.

ok: [server1.example.com] => (item={'inner': ['hello', 'world']}) => {
    "msg": {
        "inner": [
            {
                "foo": "hello"
            },
            {
                "foo": "world"
            }
        ]
    }
}

 

Then to just return the key/value pairs in the "inner" list.

---
- hosts: all
  vars:
    outer:
      - inner:
        - { foo: hello }
        - { foo: world }
  tasks:
  - ansible.builtin.debug:
      msg: "{{ item.inner }}"
    with_items: "{{ outer }}"
...

 

Should return something like this.

ok: [server1.example.com] => (item={'inner': [{'foo': 'hello'}, {'foo': 'world'}]}) => {
    "msg": [
        {
            "foo": "hello"
        },
        {
            "foo": "world"
        }
    ]
}

 

But to return a specific item in the "inner" list you'd have to include the index number.

---
- hosts: all
  vars:
    outer:
      - inner:
        - { foo: hello }
        - { foo: world }
  tasks:
  - ansible.builtin.debug:
      msg: "{{ item.inner[0] }}"
    with_items: "{{ outer }}"
...

 

Which returns foo: hello but does not return foo: world.

ok: [server1.example.com] => (item={'inner': [{'foo': 'hello'}, {'foo': 'world'}]}) => {
    "msg": {
        "foo": "hello"
    }
}

 

Or, to just return the value of the "foo" key in index 0.

---
- hosts: all
  vars:
    outer:
      - inner:
        - { foo: hello }
        - { foo: world }
  tasks:
  - ansible.builtin.debug:
      msg: "{{ item.inner[0].foo }}"
    with_items: "{{ outer }}"
...

 

Returns "hello" but does not return "world".

ok: [server1.example.com] => (item={'inner': [{'foo': 'hello'}, {'foo': 'world'}]}) => {
    "msg": "hello"
}

 

But what if you have dynamic data and you can't predict the index number? In this situation, what I typically do is store the "inner" list in its own list.

---
- hosts: all
  vars:
    outer:
      - inner:
        - { foo: hello }
        - { foo: world }
  tasks:
  - ansible.builtin.set_fact:
      inner: "{{ item.inner }}"
    with_items: "{{ outer }}"

  - ansible.builtin.debug:
      msg: "{{ item }}"
    with_items: "{{ inner }}"
...

 

Which lets me loop through the items in the "inner" list without having to include index number. Nice!

ok: [server1.example.com] => (item={'foo': 'hello'}) => {
    "msg": {
        "foo": "hello"
    }
}
ok: [server1.example.com] => (item={'foo': 'world'}) => {
    "msg": {
        "foo": "world"
    }
}

 




Did you find this article helpful?

If so, consider buying me a coffee over at Buy Me A Coffee



Comments


Add a Comment


Please enter c471a4 in the box below so that we can be sure you are a human.