Comissar
Comissar

Reputation: 53

How can I traverse nested lists in Ansible?

I have a list of devices, each of which has a varying list of attributes that must be created on the devices, one at a time. Is there a way to build a nested set of loops to do this? The with_nested construct applies every attribute to every device; I need only a single attribute per device per call.

This playbook demonstrates what I have tried (Ansible 2.7.1, Python 2.7.1):

- name: Test nested list traversal
  hosts: localhost
  connection: local
  vars:
    Stuff:
      - Name: DeviceA
        Info: AInfo
        Values:
          - ValueA1
          - ValueA2
      - Name: DeviceB
        Info: BInfo
        Values:
          - ValueB1
          - ValueB2
          - ValueB3

  tasks:
  - name: Loop test
    debug:  
      msg: "{{ item.Name }},{{ item.Info }}, {{ item.Values }}"
    with_items: 
      - "{{ Stuff }}"

What I get: (One call per device, containing all attributes at once)

ok: [localhost] => (item={u'Info': u'AInfo', u'Values': [u'ValueA1', u'ValueA2'], u'Name': u'DeviceA'}) =>
  msg: DeviceA,AInfo, [u'ValueA1', u'ValueA2']
ok: [localhost] => (item={u'Info': u'BInfo', u'Values': [u'ValueB1', u'ValueB2', u'ValueB3'], u'Name': u'DeviceB'}) =>
  msg: DeviceB,BInfo, [u'ValueB1', u'ValueB2', u'ValueB3']

What I want (each msg represents a separate operation to be performed on the device with just that one attribute)

msg: DeviceA, AInfo, ValueA1
msg: DeviceA, AInfo, ValueA2
msg: DeviceB, BInfo, ValueB1
msg: DeviceB, BInfo, ValueB2
msg: DeviceB, BInfo, ValueB3

Upvotes: 1

Views: 11868

Answers (1)

larsks
larsks

Reputation: 312650

You can get what you want using the subelements filter:

---
- hosts: localhost
  gather_facts: false
  vars:
    Stuff:
      - Name: DeviceA
        Info: AInfo
        Values:
          - ValueA1
          - ValueA2
      - Name: DeviceB
        Info: BInfo
        Values:
          - ValueB1
          - ValueB2
          - ValueB3
  tasks:
    - debug:
        msg: "{{ item.0.Name }}, {{ item.0.Info }}, {{ item.1 }}"
      loop: "{{ Stuff|subelements('Values') }}"
      loop_control:
        label: "{{ item.0.Name }}"

Running the above playbook gets you:

PLAY [localhost] ******************************************************************************************************************************************************************************

TASK [debug] **********************************************************************************************************************************************************************************
ok: [localhost] => (item=DeviceA) => {
    "msg": "DeviceA, AInfo, ValueA1"
}
ok: [localhost] => (item=DeviceA) => {
    "msg": "DeviceA, AInfo, ValueA2"
}
ok: [localhost] => (item=DeviceB) => {
    "msg": "DeviceB, BInfo, ValueB1"
}
ok: [localhost] => (item=DeviceB) => {
    "msg": "DeviceB, BInfo, ValueB2"
}
ok: [localhost] => (item=DeviceB) => {
    "msg": "DeviceB, BInfo, ValueB3"
}

PLAY RECAP ************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0   

Upvotes: 16

Related Questions