3

I'm not able to loop a list in a dict and keep the key of the original dict.

Bellow an example of my data

vars:
 nginx_users:
    'instance1':
       roles:
          - product
          - nginx_instance
       cert_source: internal-ca
       URL: page-nginx.domain.internal
       port: 8090
       downtime: true
       basic_auth:
          - username1
          - username2
       ip_restriction:
          - '0.0.0.0/0'
    'instance2':
       roles:
          - product
          - nginx_instance
       cert_source: internal-ca
       URL: page2-nginx.domain.internal
       port: 8091
       downtime: true
       basic_auth:
          - username1
          - username2
       ip_restriction:
          - '0.0.0.0/0'
    'instance3':
       roles:
          - product
          - nginx_instance

I need to loop it in a manner that It iterates like this

"msg": [
        "instance1",
        [
            "username1"
        ],
        "instance1",
        [
            "username2"
        ],
       "instance2",
        [
            "username1"
        ],
        "instance2",
        [
            "username2"
        ]
...
    ]

I am using

when: item.value.basic_auth is defined

In a loop which will filter out the items without the desired value/item and return only the "items" that I need to parse, like.

...
loop: "{{ nginx_users | dict2items }}"
when: item.value.basic_auth is defined
...

However from there I don't know how to loop those "items" to get the list of users on a way that it returns the single user per iteration and preserve the key.

I know a work around would be to have use another variable instead of the key so I could just select it. but that wouldn't work for me in this escenario.

I have tried using subelements and lookup but I don't seem to be able narrow down the exact combination also thought about using product too and try and traverse the data. But I wasn't able to use that either.

After reading this:

https://github.com/ansible/ansible/pull/6370

https://github.com/ansible/ansible/pull/4982

I'm starting to think I will have to use Jinja to be able to do this.

This is as close as I have been able to get to what I'm trying to achieve

- name: loop
  debug:
    msg:
    - "{{ item.key }}"
    - "{{ item.value.basic_auth }}"
  loop: "{{ nginx_users | dict2items }}"
  when: item.value.basic_auth is defined

- name: loop
  debug:
    msg:
    - "{{ item.1 }}"
  loop: "{{ lookup('subelements', nginx_users, 'basic_auth', {'skip_missing': True}) }}"

which results on this:

 TASK [loop]
 ************************************************************************ skipping: [nginx-test-01.domain.internal] => (item={'key':
 u'instance1', 'value': {u'roles': [u'product', u'nginx_instance']}})
 ok: [nginx-test-01.domain.internal] => (item={'key': u'username2',
 'value': {u'roles': [u'product', u'nginx_instance'], u'URL':
 u'page-nginx.symplectic.internal', u'ip_restriction': [u'0.0.0.0/0'],
 u'basic_auth': [u'username1', u'username2'], u'port': 8090,
 u'zerodowntime': True, u'cert_source': u'internal-ca'}}) => {
     "msg": [
         "instance1",
         [
             "username1",
             "username2"
         ]
     ]}

 TASK [loop]
 ************************************************************************ ok: [nginx-test-01.domain.internal] => (item=[{u'roles':
 [u'product', u'nginx_instance'], u'URL':
 u'page-nginx.domain.internal', u'ip_restriction': [u'0.0.0.0/0'],
 u'port': 8090, u'downtime': True, u'cert_source':
 u'internal-ca'}, u'username1']) => {
     "msg": [
         "username1"
     ] } ok: [nginx-test-01.domain.internal] => (item=[{u'roles': [u'product', u'nginx_instance'], u'URL':
 u'page-nginx.symplectic.internal', u'ip_restriction': [u'0.0.0.0/0'],
 u'port': 8091, u'downtime': True, u'cert_source':
 u'internal-ca'}, u'username2']) => {
     "msg": [
         "username2"
     ] }

Any thought/suggestions would be appreciated.

UPDATE: I have learned that using loop control inner with and outer variable I can achieve this. But this requires me to use a separated .yml Is there any way to do this on the same .yml file?

1
  • Thanks, I have not run this particular, but it was a generic version of mine, I missed that, thanks...
    – maco1717
    Jul 16 '19 at 22:29
3

Let's simplify the dictionary in the first task and loop the subelements in the second. The tasks below

    - set_fact:
        nginx_users_selected: "{{ nginx_users_selected|
                                  default({})|
                                  combine({item.key: item.value})
                                  }}"
      loop: "{{ nginx_users|
                dict2items|
                json_query('[*].{key: key, value: value.basic_auth}')
                }}"
      when: item.value
    - debug:
        msg: "{{ item.0.key }} {{ item.1 }}"
      loop: "{{ nginx_users_selected|
                dict2items|
                subelements('value')
                }}"

give

    "msg": "instance2 username1"
    "msg": "instance2 username2"
    "msg": "instance1 username1"
    "msg": "instance1 username2"

Then we can create the structure

    - set_fact:
        my_list: "{{ my_list|default([]) +
                     [ item.0.key, [ item.1 ]] }}"
      loop: "{{ nginx_users_selected|
                dict2items|
                subelements('value') }}"
    - debug:
        var: my_list

give

    "my_list": [
        "instance2", 
        [
            "username1"
        ], 
        "instance2", 
        [
            "username2"
        ], 
        "instance1", 
        [
            "username1"
        ], 
        "instance1", 
        [
            "username2"
        ]
    ]
4
  • !Thanks! that worked beautifully, It required me installing "jmespath" but don't care about that. I found another solution using include_tasks and outer and inner loop. this is way better. I still need to understand it tho. So loads reading to do. I would have never found this out. Thanks
    – maco1717
    Jul 18 '19 at 7:40
  • I was wondering is you could recommend any reading material for this, I have had some time not to racap this and looking into this I really really can't head my head around it xD - I have a new requirement similar to this, but I dont know how to use your example as a starting point
    – maco1717
    Nov 5 '19 at 11:39
  • 1
    FWIW. Moving forward I kept examples in GitHub. Start with example 2 then see files in example 20. There is a tutorial and some examples at jmespath.org. Nov 5 '19 at 11:49
  • Thanks you very much!
    – maco1717
    Nov 5 '19 at 12:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.