Undefined variables and filter subelements

We think we experience an anomalie with undefined variables and usage of subelements:

`

  • name: Enable/disable service
    firewalld:
    zone: “{{ item.0.ZONE }}”
    service: “{{ item.1.SERVICE }}”
    permanent: yes
    immediate: yes
    state: “{{ item.1.STATE }}”
    loop: “{{ FIREWALLD_ZONE_SERVICE|subelements(‘SERVICES’) }}”
    when: FIREWALLD_ZONE_SERVICE is defined

`

FIREWALLD_ZONE_SERVICE is NOT defined. Our expectation is that ansible will skip this task like it does at other tasks without sublements. With subelements in the loop ansible tries to resolve undefined FIREWALLD_ZONE_SERVICE and fails with an error:

`
fatal: […]: FAILED! => {“msg”: “obj must be a list of dicts or a nested dict”}

`

If FIREWALLD_ZONE_SERVICE is defined everything works like expected.

Anyone?

Maybe i was not precise enough. Situation is this:

If the var is defined it looks like this:

`
FIREWALLD_ZONE_SERVICE:

  • ZONE: public
    SERVICES:
  • SERVICE: http
    STATE: enabled
  • SERVICE: https
    STATE: enabled

`

We loop through SERVICES with
`
FIREWALLD_ZONE_SERVICE|subelements(‘SERVICES’)

`

This is fine as long the var is defined. It not we expect that

`
when: FIREWALLD_ZONE_SERVICE is defined

`

prevents the evaluation of the war in the loop.

In another context it works fine:

loop: "{{ FIREWALLD_ZONE|product(FIREWALLD_ZONE.ZONES)|list }}"

`
when: FIREWALLD_ZONE is defined

`

If FIREWALLD_ZONE is not defined the var in the loop is not evaluated.

So the difference is:

If we have ‘subelements’ in the loop ‘when: var is defined’ is not working.
If we have eq. ‘product’ in the loop ‘when: var is defined’ works.

More clear now? :slight_smile:

Could someone spend some thoughts? Maybe it is a misunderstanding on our site…

Use *default* and *skip_missing* subelements. For example

    loop: "{{ FIREWALLD_ZONE_SERVICE|default()|
              subelements('SERVICES', skip_missing=True) }}"

Cheers,

  -vlado

Use default and skip_missing subelements. For example

loop: “{{ FIREWALLD_ZONE_SERVICE|default()|
subelements(‘SERVICES’, skip_missing=True) }}”

Thank you! This is working. Is only needed with ‘subelements’. But why? Anyway - thanks! :slight_smile:

I guess

  - *subelements* filter is executed before *when*, because
  - *when* is evaluated on each iteration, because
  - *when* argument can change on each iteration (e.g. may include *item*).
  - *when* can not be evaluated before *subelements*, because *item* does not
    exist yet.

The loop will fail

    loop: "{{ FIREWALLD_ZONE_SERVICE|subelements('SERVICES') }}"
  when: FIREWALLD_ZONE_SERVICE is defined

  FAILED! => {"msg": "'FIREWALLD_ZONE_SERVICE' is undefined"}

The loop below is skipped when FIREWALLD_ZONE_SERVICE is not defined
(this is not consistent, it should also fail. Source would reveal why)

    loop: "{{ FIREWALLD_ZONE_SERVICE }}"
  when: FIREWALLD_ZONE_SERVICE is defined

,but it will also fail if the evaluation of the *loop* argument
(FIREWALLD_ZONE_SERVICE) before *when* is forced e.g.

    loop: "{{ FIREWALLD_ZONE_SERVICE }}"
  when: item|length > 10 and
        FIREWALLD_ZONE_SERVICE is defined
        
  FAILED! => {"msg": "'FIREWALLD_ZONE_SERVICE' is undefined"}

Cheers,

  -vlado

As a result, *default* is better construct

       loop: "{{ FIREWALLD_ZONE_SERVICE|default() }}"

than *when*

       loop: "{{ FIREWALLD_ZONE_SERVICE }}"
     when: FIREWALLD_ZONE_SERVICE is defined

Thank you for the background explanation! Interesting.