Doubt regarding loops and conditionals

Hello,

I have a doubt regarding loops and conditionals that maybe someone can make it clear to me.
In the documentation is stated that:

“When combining Conditionals with a loop, the when: statement is processed separately for each item.”

But if I do this:

  • name: “test loop”
    debug:
    msg: “test”
    loop: “{{nulo}}”
    when: nulo is defined

where “nulo” is an undefined variable. Here the task skips instead of failing. If I read the documentation to the letter it should fail as it would only do the test inside the loop.

Also, this issue:
https://github.com/ansible/ansible/issues/45976

has a slightly different variation of my loop which fails instead of skipping.

My observed behaviour is of course the better one as allows to test the list before running loop, but should I trust it? It is the same in all versions of Ansible?

Thank you for your help. Regards,

Nuno Jordão

Hello,

I have a doubt regarding loops and conditionals that maybe someone can make it clear to me.
In the documentation is stated that:

"When combining Conditionals with a loop, the when: statement is processed separately for each item."

      I am not sure since in your example the when is applied to the
debug, i.e. if when is false the debug task is not run.

But if I do this:

    - name: "test loop"
      debug:
        msg: "test"
      loop: "{{nulo}}"
      when: nulo is defined

      That is an interesting way to loop.

where "nulo" is an undefined variable. Here the task skips instead of failing. If I read the documentation to the letter it should fail as it would only do the test inside the loop.
Also, this issue:
https://github.com/ansible/ansible/issues/45976

has a slightly different variation of my loop which fails instead of skipping.

      The differences are more than slightly. having "null_var" as
the when test is not the same as "null_var is defined."

when: null_var is defined

is testing whether null_var is defined, returning true or false; it
could not care less about its contents though. So, it does not break
when it is not defined.

when: null_var | default(False)

would also work since it also gives an option for when null_var is not
defined. I believe

when: null_var

would work if (1) it is always defined and (2) you set it to true or
false or something. Someone more knowledgeable can check my claims.

Instead of testing whether the variable is defined or not, use the "default"
filter. For example

     - name: "test loop"
       debug:
         msg: "test"
       loop: "{{ nulo|default() }}"

The loop will be skipped if "nulo" is undefined. Test "nulo" separately if
you want the playbook to fail. For example

     - name: Fail when nulo undefined
       fail:
         msg: "Variable nulo undefined"
       when: nulo if undefined

HTH,

  -vlado

Hello,

Thank you for your responses.
Some times I dont like to use the loop: “{{ nulo|default() }}” because it doesn’t say “skipping” and in complex playbook with lots of hosts I like to have that feedback.

I tested several options and and everything works, even the case of the issue 45976 (should have tested that one before). I think that, probably, the behaviour changed, and the documentation should be more clear in this regard… The conditional is not applied only per item but also to the task…

vars:
tstbool: False
tstnull:

tasks:

  • name: “test loop”
    debug:
    msg: “test”
    loop: “{{nulo|default()}}”

  • name: “test loop”
    debug:
    msg: “test”
    loop: “{{nulo}}”
    when: nulo is defined

  • name: “test loop”
    debug:
    msg: “test”
    loop: “{{nulo}}”
    when: nulo|default(False)

  • name: “test loop”
    debug:
    msg: “test”
    loop: “{{nulo}}”
    when: nulo|default()|length > 0

  • name: “test loop”
    debug:
    msg: “test”
    loop: “{{nulo}}”
    when: tstbool

  • name: “test loop”
    debug:
    msg: “test”
    loop: “{{nulo}}”
    when: tstnull

Returns:

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

TASK [test loop] *****************************************************************************************************

TASK [test loop] *****************************************************************************************************
skipping: [localhost]

TASK [test loop] *****************************************************************************************************
skipping: [localhost]

TASK [test loop] *****************************************************************************************************
skipping: [localhost]

TASK [test loop] *****************************************************************************************************
skipping: [localhost]

TASK [test loop] *****************************************************************************************************[DEPRECATION WARNING]: evaluating None as a bare variable, this behaviour will go away and you might need to add

bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle… This feature will be
removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
skipping: [localhost]

PLAY RECAP ***********************************************************************************************************localhost : ok=0 changed=0 unreachable=0 failed=0 skipped=6 rescued=0 ignored=0

The final count for skipped is 6 but the first task doesn’t report skipping

Hello,

Thank you for your responses.
Some times I dont like to use the loop: "{{ nulo|default() }}" because it doesn't say "skipping" and in complex
playbook with lots of hosts I like to have that feedback.

I tested several options and and everything works, even the case of the issue 45976
<https://github.com/ansible/ansible/issues/45976&gt; (should have tested that one before). I think that, probably, the
behaviour changed, and the documentation should be more clear in this regard... The conditional is not applied only per
item but also to the task...

I suppose Ansible determines whether the conditional contains a loop variable. In that case it applies it for every
item. In the other case it evaluates it only once and skips the task when the conditional results in a false value.

The documentation seems not be completely accurate.

Regards
         Racke

Some times I dont like to use the loop: "{{ nulo|default() }}" because it
doesn't say "skipping" and in complex playbook with lots of hosts I like to
have that feedback.

You might want to find a plugin which fits your needs
https://docs.ansible.com/ansible/latest/plugins/callback.html#plugin-list

... I think that, probably, the behaviour changed, and
the documentation should be more clear in this regard... The conditional is
not applied only per item but also to the task...

[...]
    - name: "test loop"
      debug:
        msg: "test"
      loop: "{{nulo}}"
      when: nulo is defined

You're right. "when" is also applied before the loop starts. Let me point to
2 more cases which describe the complexity. The task below

   - debug:
        msg: "test"
      loop: "{{ nulo }}"
      when:
        - nulo is defined
        - item is search('PATTERN')

is skipped because "when" stops evaluating a conjunction after the first
element fails

   skipping: [localhost]

But when the order of the conditions is changed the task

    - debug:
        msg: "test"
      loop: "{{ nulo }}"
      when:
        - item is search('PATTERN')
        - nulo is defined

fails because the evaluation of "item" leads to the evaluation of "nulo"

    fatal: [localhost]: FAILED! => {"msg": "'nulo' is undefined"}

As a result, I argue that "{{ nulo|default() }}" should be preferred in
term of "keep it simple stupid".
https://en.wikipedia.org/wiki/KISS_principle

It's been also discussed here
https://groups.google.com/forum/#!topic/ansible-project/Y1mGC3vrsng

HTH,

  -vlado

Well, after reading the other discussion, I think you are right. The safest is to use the default. The conditional can be unpredictable which was what I was afraid and why I started this thread.
Ansible should avoid these situations. Or maybe provide a warning of potential unsafe situations.

About the plugins, I dont think it is possible to use those in AWX.

Thank you for your input.

Regards,

Nuno Jordão