Ansible: [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}.

Created on 8 Mar 2017  ·  31Comments  ·  Source: ansible/ansible

ISSUE TYPE

  • Bug Report
COMPONENT NAME
ANSIBLE VERSION
$ ansible --version
ansible 2.3.0 (devel a8910e78ca) last updated 2017/03/08 02:46:53 (GMT -500)
  config file =
  configured module search path = Default w/o overrides
  python version = 2.7.10 (default, Jul 30 2016, 19:40:32) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)]
CONFIGURATION


Stock ansible.cfg

OS / ENVIRONMENT


Management host macOS 10.12.3

SUMMARY


Task I've been using for over 7 months started throwing a warning.

STEPS TO REPRODUCE

- name: install powershell
  win_chocolatey:
    name: '{{ item }}'
    state: 'present'
    upgrade: True
  with_items:
    - "powershell"
  register: check_powershell5
  when: "{{ ansible_PSVersionTable.Major|int < 5 }}"
  tags: win_powershell
EXPECTED RESULTS


Task would run without a warning

ACTUAL RESULTS

TASK [win_powershell : install powershell] *************************************
 [WARNING]: when statements should not include jinja2 templating delimiters
such as {{ }} or {% %}. Found: {{ ansible_PSVersionTable.Major|int < 5 }}
affects_2.3 bug

Most helpful comment

I honestly don't see how you can write a meaningful playbook without triggering this new warning message. The whole point of Ansible is that things can be modularised into roles. If you want to stop this warning from being triggered you have no chance unless you are writing flat playbooks with ugly when expressions.

Examples that are seemingly not able to be worked around:

defaults

auth_service       : none
auth_service_aws   : aws
auth_service_is_aws: '{{ (auth_service == auth_service_aws) | bool }}'

meta

dependencies:

  - { role: apollo/auth/aws, when: auth_service_is_aws }

throws warnings....

And finally (this is really difficult to work around!)

Parent Role - defaults

bootstrap_ping : true

Parent Role - meta

dependencies:

  - { role: apollo/platform/ping, ping: '{{ bootstrap_ping }}' }

Child Role - tasks

- name: execute

  when: ping

  win_ping:

I seriously hope I am being stupid and am missing something simple either that or this change gets reverted in the next release.

All 31 comments

This is expected. The warning was recently added to the 2.3 release purposefully.

As the warning states, you should not use jinja2 delimiters in a when statement. Instead it should read:

when: ansible_PSVersionTable.Major|int < 5

If you have further questions please feel free to use the mailing list.

How to workaround this warning if the when expression is stored in a variable (for whatever reason, e.g. due to it being used mutlitple times in different plays).

e.g.: mycondition: ansible_PSVersionTable.Major|int < 5 to be used as when: mycondition.
-> This is not working because mycondition doesn't get evaluated/expanded and is treated as a String.

What works is to declare mycondition: "{{ ansible_PSVersionTable.Major|int < 5 }}" or when: "{{ mycondition }}" but both will result in the above mentioned WARNING.

This works for me

  when: "(ansible_PSVersionTable.Major|int < 5) and ('Microsoft Windows 7' in ansible_distribution)"

with

$ ansible --version
ansible 2.4.0 (devel 53c52cf65f) last updated 2017/03/30 07:29:39 (GMT -500)
  config file =
  configured module search path = [u'/Users/tanner/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  python version = 2.7.10 (default, Jul 30 2016, 19:40:32) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)]

That works for me too. But now move the condition to a variable and try to use the variable instead.

ansible_PSVersionTable is a variable from a custom fact I have installed on all Window computers.

Try the following and I assume it won't work (given that the condition is true):

mycond: "(ansible_PSVersionTable.Major|int < 5) and ('Microsoft Windows 7' in ansible_distribution)"
...
when: mycond

In that case you would need the "{{ ... }}" - its only the 'when' conditional where the "{{ .... }}" is implied, I think.

also might want to make use of | bool filter to ensure var is stored as a boolean.

That's what I'm saying... you will either need the "{{ ... }}" within the variable declaration or the when condition as it won't work (correctly) otherwise. And then you will get the mentioned WARNING.

You can use something like:

- set_fact: content1="{{ var1.stdout }}" content2="{{ var2.stdout }}" and compare that.

possibly a dup of #23578

I'm also a bit confused by this. I understand we can now do myvar instead of "{{ myvar }}" or {{ myvar }} but there are certain uses cases where this still isn't enough. E.g.

when: myvar.stdout != ansible_date_time.date

^ This will NOT return the current date (YYYY-MM-DD). In this instance I have no way to use anything else but the jinja2 delimiters with something like:

- set_fact:
    ansible_date: "{{ ansible_date_time.date }}"

Another use case is, e.g.

- name: "TEST: The files directory should have correct ownership"
  shell: stat -c %U:%G {{ files_dir }}
  register: styles_dir_ownership
  changed_when: styles_dir_ownership.stdout != "{{ user }}:{{ apache_user }}"
  failed_when: styles_dir_ownership.stdout != "{{ user }}:{{ apache_user }}"

I have no way to simply do user:apache_user because I need to pass a colon and thus have to resort to e.g.

- set_fact:
    expected_ownership: "{{ user }}:{{ apache_user }}"

So my question is: how can I both account for those 2 use cases if jinja2 delimiters are deprecated starting with Ansible 2.3? Is set_fact now the preferred option?

Adding another use case:

consider the following variable:

to_be_removed_users:
  - name: 'adm'
    remove: False 
  - name: 'ftp'
    remove: True
  - name: 'games'
    remove: False  
  - name: 'gopher'
    remove: True
  - name: 'operator'
    remove: False
  - name: 'uucp'
    remove: True

then the following tasks generate the warning. Not sure how to avoid it as item.name is the result of iteration on the list

```
name: Get users list
getent:
database: passwd
tags:
- userdel

  • name: Make sure the following users are removed
    user:
    name: "{{ item.name }}"
    state: absent
    remove: "{{item.remove}}"
    with_items: '{{ to_be_removed_users }}'
    when: getent_passwd.{{ item.name }} is defined
    tags:

    • userdel

      ```

@PhilEv , you can do something like this to avoid that problem (note you dont need the true check):

```---

  • hosts:

    • localhost

      tasks:

    • set_fact:

      COLOURS:

      RED: True

      BLUE: True

      YELLOW: False

- name: Favourite colours!
  command: echo "I like {{ item }}"
  # when: COLOURS.{{item}} is defined and COLOURS.{{item}}
  when: item in COLOURS and COLOURS[item]
  with_items:
    - "GREEN"
    - "BLUE"
    - "PURPLE"
    - "YELLOW"```

We're seeing these warnings as well, and removing the jinja delimiters from when seems to break this:

- name: Check if redis is installed
  shell: redis-server --version || /bin/true
  register: redis_is_installed

- include: redis_build.yml
  when: "redis_is_installed.stdout.find('v={{ redis_version }}') == -1"

Any suggestions for an alternative way of writing this?

@jsuter

when: "redis_is_installed.stdout.find('v=' ~ redis_version) == -1"

@sivel Awesome, thank you. What is the tilde doing specifically in this case? I can't find the docs on it and would love to read up.

@jsuter http://jinja.pocoo.org/docs/dev/templates/#other-operators

~
Converts all operands into strings and concatenates them.

{{ "Hello " ~ name ~ "!" }} would return (assuming name is set to 'John') Hello John!.

Thanks @streetster as posted in ansible group (thanks @sivel and Josh)

when: getent_passwd[item['name']] is defined
was all good for my case

I honestly don't see how you can write a meaningful playbook without triggering this new warning message. The whole point of Ansible is that things can be modularised into roles. If you want to stop this warning from being triggered you have no chance unless you are writing flat playbooks with ugly when expressions.

Examples that are seemingly not able to be worked around:

defaults

auth_service       : none
auth_service_aws   : aws
auth_service_is_aws: '{{ (auth_service == auth_service_aws) | bool }}'

meta

dependencies:

  - { role: apollo/auth/aws, when: auth_service_is_aws }

throws warnings....

And finally (this is really difficult to work around!)

Parent Role - defaults

bootstrap_ping : true

Parent Role - meta

dependencies:

  - { role: apollo/platform/ping, ping: '{{ bootstrap_ping }}' }

Child Role - tasks

- name: execute

  when: ping

  win_ping:

I seriously hope I am being stupid and am missing something simple either that or this change gets reverted in the next release.

Color me confused as well. Everything else in Ansible, as far as I can see, uses Jinja delimeters. What's the difference between "changed_when" and the rest? Speaking as someone who is learning Ansible, it makes things harder to understand.

Please see above PR. I will probably get fish slapped pretty hard but if it is affecting enough people please show your support and maybe the warnings will get removed (either by this way or another).

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

Instead of saying what not to do it should say WHAT TO DO! We are not magicians. Thanks.

I get this warning all over the place now. I have a generic role that is 'extended' in specialized roles by specifying some variables when defining this generic as a dependency like this in the meta/main.yml:

dependencies:
  - role: generic_role
    _configuration_needed: "{{custom_configuration_needed}}"

The custom_configuration_needed is then a default or can be overridden per-host or per-group when including the specialized role.

Then, in the generic role I have steps like this:

- name: "configure package if necessary"
  include: configure.yml
  when: _configuration_needed

I now however, in every single step in the configure.yml in the 'base' generic role executed for that specialized role, get warnings like this:

   [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{custom_configuration_needed}}
 ```

This is due to the implicit 'when' clause that's added to every step in that included file, but I'm not sure how to solve this-one. The `_configuration_needed` isn't the only case, it's just the one spamming most warnings right now. Now for this `_configuration_needed` I tried defining the dependency like this:

dependencies:

  • role: generic_role
    _configuration_needed: custom_configuration_needed
    ```

This however doesn't work, since when I debug print that _configuration_needed variable, I see this "custom_configuration_needed" as (a non-empty) string, which always evaluates to "True". This is certainly not expected or wanted behavior and I have no idea how to remove these warnings.

If there is a working solution for preventing these warnings in this case, which also have the expected behavior, please share - bur right now, I can't find any way to do this.

Also, for ansible 3, please consider making jinja templating consistent across the board. Everywhere or nowhere {{}}, but this is plain silly.

I can't eveluate a dynamic variable without the {{}}, I can't get this working witout the curly brackets:

vars:
    host_name: ['foo', 'bar']
    foo_wwn_2: "This exist"

  tasks:
    - debug:
        var: "{{item.1}}_wwn_{{item.0}}"
     when: ({{item.1 + '_wwn_' + item.0}}) is defined
      with_nested:
        - ['1', '2']
        - "{{host_name}}"

Without the brackets it's just a string which will be evaluated as TRUE.

It's really dissapointing to see that you want to remove the only way to evaluate variable within the when .

@bartmeuris in your example, that warning will no longer show up in ansible v2.4 which is soon to be released

@MichalTaratuta you should be using the following instead:

when: vars[item.1 ~ '_wnn_' ~ item.0] is defined

The fact that your example worked before was unintentional, and is not best practice for building a variable name to check.

What about when testing for the presence of a string, part of which contains a variable?

vars:
  acct:
    name: example_name

tasks:
  - name: Emergency Account | Gather list of usernames
    ios_command:
      commands: show run | i ^username
    register: ios_usernames

  - name: Emergency Account | Remove non-emergency account users
    ios_config:
      lines:
        - no {{ item }}
    with_flattened:
      - "{{ ios_usernames.stdout_lines }}"
    when: '"username {{ acct.name }} privilege 0 secret" not in item'
    no_log: True

It's very possible I'm just missing a better or more appropriate way to write that but so far the only method that works seems to be using the brackets within the quoted string.

EDIT
Sorry for cluttering the thread. After re-reading a few of the examples and further testing was able to get it work by breaking apart the quote and using concatenation. Makes it a little longer but does the job.

  when: '"username " ~ localauth.emergency.name ~ " privilege 0 secret" not in item'

I see that the warning is useful, but can e.g. longer expressions like the following one can be written without any templating delimiters:

  when: |-
      {%- set certs = {'sync': False} -%}
      {% if gen_node_certs[inventory_hostname] or
        (not etcdcert_node.results[0].stat.exists|default(False)) or
          (not etcdcert_node.results[1].stat.exists|default(False)) or
            (etcdcert_node.results[1].stat.checksum|default('') != etcdcert_master.files|selectattr("path", "equalto", etcdcert_node.results[1].stat.path)|map(attribute="checksum")|first|default('')) -%}
              {%- set _ = certs.update({'sync': True}) -%}
      {% endif %}
      {{ certs.sync }}

This may help new people,
We can not use variables inside when condition because it has some restrictions and below warning will be displayed
WARNING:when statements should not include jinja2 templating delimiters such as {{ }} or {% %}

so to avoid such things while checking the condition by passing variable, first we need to register that particular variable and then use that.Below is the small example

VERSION is already defined or passed explicitely while running a play book

- name: Check the java version
   shell: java -version 2>&1 | grep version | awk '{print $3}' | sed 's/"//g' | cut -d "_" -f1
   register: java_versios
 - name: testing senario
   shell: echo '{{ VERSION }}'
   register: r
- name: testing
   file:
     path: /root/Karthik/JAVATEST
     state: directory
     owner: root
     group: root
     mode: 0755
     recurse: yes
   when: java_version.stdout >= r.stdout

Hello,

What about the case when I am passing a inventory group name based on the predefined variable?
In this scenario based on the variable "sync_source", I invoke to inventory group which is passed as a variable by the user.

    - debug:
        msg:
          - "source instance: {{ inventory_hostname }}"
          - "{{ dir }} size is {{ result_dir_size }} GB"
      when: "inventory_hostname in groups.{{ sync_source }}"

@linlinas

when: "inventory_hostname in groups[sync_source]"
Was this page helpful?
0 / 5 - 0 ratings