Changes to when and conditionals in Ansible 2.2

I’m testing existing roles and playbooks against Ansible 2.2, and I’ve noticed that several roles that I have written that take advantage of “when: variable is defined” or “when: variable is undefined” in tasks no longer work. This happens when a variable is used from a previous task, but is empty because maybe the results were empty. Here is an example:

  • name: Find existing rules
    find: path=/etc/software/rules/
    register: existing_rules
  • name: Clean rules
    file: path={{ item }} state=absent
    with_items: “{{ existing_rules.files | map(attribute=‘path’) | list }}”
    when: existing_rules is defined

In this case, there were no existing files, so the variable is empty and I would expect this Clean rules task to be skipped, but instead Ansible errors with something like

FAILED! => {“failed”: true, “msg”: “‘dict object’ has no attribute ‘files’”}

What are the options then to allow for this sort of conditional logic? This definitely worked in Ansible 2.1, but I don’t recall if any DEPRECATED warnings were given.

In 2.1 this would have been a deprecation warning. What you need is something like:

with_items: “{{ existing_rules.files | default() | map(attribute=‘path’) | list }}”

That gives existing_rules.files a default of an empty list.

Also off note here, is that when statements are processed for each iteration of the with_items loop, and not before the with_items. So your with_items needs to process correctly all the time. As such, you could remove when statement altogether as it isn’t doing what you wanted it to do.

Thanks for the info Matt. The Ansible docs, http://docs.ansible.com/ansible/playbooks_conditionals.html, indicate this is should work as before. Specifically,

If a required variable has not been set, you can skip or fail using Jinja2’s test. For example:

tasks:
    - shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
      when: foo is defined

    - fail: msg="Bailing out. this play requires 'bar'"
      when: bar is undefined

This is especially useful in combination with the conditional import of vars files (see below).

Do you know where this behavior is documented? I’d like to update the docs and submit a PR.

If you read a little further down, under the “Loops and Conditionals”[1] this information is present:

Combining when with with_items (see Loops), be aware that the when statement is processed separately for each item… If you need to skip the whole task depending on the loop variable being defined, used the |default filter to provide an empty iterator

[1] http://docs.ansible.com/ansible/playbooks_conditionals.html#loops-and-conditionals

In this case, there were no existing files, so the variable is empty
and I would expect this *Clean rules* task to be skipped

Hmm, isn't the real problem that after the first task, exiting_rules
*isn't* undefined any more?

Add a debug task (- debug: var=existing_rules), and I see things like

  TASK [debug] *******************************************************************
  ok: [localhost] => {
      "existing_rules": {
          "changed": false,
          "examined": 0,
          "files": ,
          "matched": 0,
          "msg": ""
      }
  }

So I think the "Clean rules" tries to do stuff because "when:
existing_rules is defined" evaluates to true.

but instead Ansible errors with something like

> FAILED! => {"failed": true, "msg": "'dict object' has no attribute
> 'files'"}

Hmm, that's not what I get; in either 2.1 or 2.2, I see

  TASK [Clean rules] *************************************************************

and then no further output -- which makes sense, because existing_rules is
defined, and has a zero-length 'files' list. I'm not sure why you're
getting the error you're getting.

Here's my (slightly modified) list of tasks, just to be sure:

    - name: Find existing rules
      find: path=/tmp/foo
      register: existing_rules

    - debug: var=existing_rules

    - name: Clean rules
      debug: msg="{{ item }}"
      with_items: "{{ existing_rules.files | map(attribute='path') | list }}"
      when: existing_rules is defined

/tmp/foo is an empty (but existent) directory.

                                      -Josh (jbs@care.com)

(apologies for the automatic corporate disclaimer that follows)

This email is intended for the person(s) to whom it is addressed and may contain information that is PRIVILEGED or CONFIDENTIAL. Any unauthorized use, distribution, copying, or disclosure by any person other than the addressee(s) is strictly prohibited. If you have received this email in error, please notify the sender immediately by return email and delete the message and any attachments from your system.