I just got warning raised by Ansible Lint.
This is the issue:
[no-jinja-when] [HIGH] No Jinja2 in when
You can skip specific rules or tags by adding them to your configuration file:
# .ansible-lint
warn_list: # or 'skip_list' to silence them completely
- no-jinja-when # No Jinja2 in when
This is my task:
- name: remove unecessary batch
file:
path: "{{ item.path }}"
state: absent
when: var_service == "service2" and item.path | regex_replace(cron_regex_for_s2_deletion, '\\1') not in batches_{{ var_fuction | regex_replace('^service2-batch-', '') }}
with_items: "{{ result.files }}"
I'm not sure how to fix the when condition to fulfil Ansible lint recommendations.
when conditions are always templated:
The when clause is a raw Jinja2 expression without double curly braces
Source: https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html#basic-conditionals-with-when
So, you cannot have any expression block {{ ... }} or statement block {% ... %} in it.
In order to get a variable from a dynamic name you should use the vars lookup, so:
lookup(
'vars',
'batches_' ~ var_fuction | regex_replace('^service2-batch-', '')
)
You can also make that when more readable by switching your and for a list:
You can use logical operators to combine conditions. When you have multiple conditions that all need to be true (that is, a logical and), you can specify them as a list
Source: https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html#conditionals-based-on-ansible-facts
Some extra YAML multiline trickery could also help you keep the expression on a line, as that could also be a warning Ansible lint could raise.
You end up with this condition:
when:
- var_service == 'service2'
- >-
item.path | regex_replace(cron_regex_for_s2_deletion, '\1')
not in lookup(
'vars',
'batches_' ~ var_fuction | regex_replace('^service2-batch-', '')
)
Related
The following code is in the defaults/main.yml file for a role:
file_env: "{% if cf_env is equalto 'cf10_dev' %}\
dev\
{% elif cf_env is equalto 'cf10_stg' %}\
stg\
{% elif (cf_env is equalto 'cf10_prd') or (cf_env is equalto 'cf10_prd_ext') %}\
prd\
{% elif cf_env is equalto 'cf11' %}\
[dev, prd]
{% endif %}"
The first 3 conditional statements work fine, where the file_env var is set to a single value, but when trying to set the file_env var to a list (the last elif statement), it doesn't work:
failed: [server-new.xxx.com] (item=[dev, prd] ) => {"ansible_loop_var": "item", "changed": false, "item": "[dev, prd] ", "msg": "Destination /opt/coldfusion11/[dev, prd] 01/bin/jvm.config does not exist !", "rc": 257}
Here is the task that generates the above error:
- name: Update jvm.config for coldfusion11 server
lineinfile:
path: /opt/coldfusion11/{{ item }}01/bin/jvm.config
regexp: '^java.home'
line: 'java.home=/usr/lib/jvm/jre-openjdk'
loop:
- "{{ file_env }}"
notify:
- handler_create_script_list
- handler_restart_coldfusion10
when:
- cf_env == "cf11"
How can I set the file_env var to a list?
I believe your issue is that it's interpreting the variable as a string, literally '[dev, prd] ' (with a space at the end, judging by the error message).
I think you want to try a different format as suggested in https://serverfault.com/a/913936/512181
If your are going to loop over your var, you should always return a list (with single element when needed) to avoid possible tricky errors. You simply need to modify your loop expression to the following:
loop: "{{ file_env }}"
Your template expressions are not using white space control which will end up with new lines in the output when they should not appear.
Your variable definition is hardly readable (and makes me think you have a deeper project architecture issue but that's an other story). Taking into account point 1 above, I suggest you refactor to the following more readable, sustainable and working form using a key/value mapping in a dict and getting the corresponding value in your final var
files_by_env
cf10_dev:
- dev
cf10_stg:
- stg
cf10_prd:
- prd
cf10_prd_ext:
- prd
cf11:
- dev
- prd
file_env: "{{ files_by_env[cf_env] }}"
Alternatively, you can reverse the definition in the dict (i.e. list environments for every file) which is slightly less verbose but leads to a more complicated expression to get the result
env_by_file:
dev:
- cf10_dev
- cf11
stg:
- cf10_stg
prd:
- cf10_prd
- cf10_prd_ext
- cf11
file_env: "{{ env_by_file | dict2items | selectattr('value', 'contains', cf_env) | map(attribute='key') | list }}"
How can I use the list key value in when without using jinja2 templating?
Below is a example that does not work:
- name: "Give sudo rights"
user:
group: "{{users[item].username}}"
groups: sudo
append: yes
name: "{{ users[item].username }}"
become: true
when: {{ users[item].sudorights }} == yes
with_items: "{{addusers}}"
List:
users:
john:
username: john
sudorights: yes
As specified in the documentation on conditionnals
The when clause is a raw Jinja2 expression without double curly braces
Moreover, comparing to literal true/false is one of ansible-lint bad practice rule check
Taking the above into account, you can fix your expression as:
when: users[item].sudorights | bool
One step further
You may have in your users list some users without sudorights defined at all. In such a case, the above condition will fire an error and stop ansible processing as soon as such an item is encountered. To make sure this does not happen, you can default the value of this parameter if it is not present:
when: users[item].sudorights | default(false) | bool
Can someone help me in understanding and resolving it
- name: Copying file to sever2
fetch:
src: /tmp/
dest: /u0/test/
when:
"{{ inventory_hostname == 'testdb' }}"
In your case, you should use when condition without Jinja delimiters.
Example:
when: inventory_hostname == 'testdb'
Detailed explanation:
Jinja template delimiters are used when variable interpolation is required in context of text and templates. This tells Ansible to use the value of the variable instead of the variable name.
Consider everything as text unless told otherwise (with delimiters)
Example:
vars:
fav_tool: Ansible
tasks:
- debug:
msg: "I like fav_tool"
This will output:
"msg": "I like fav_tool"
This is not what I wanted, I wanted to display "I like Ansible". So then I have to "tell" ansible to use the value of fav_tool.
- debug:
msg: "I like {{ fav_tool }}"
Similarly we use Jinja template delimiters in templates. Where we want to separate the variable, and expressions from text.
Example template such as below:
if fav_tool == 'Ansible'
I like Ansible
endif
... will result in exactly the same text without evaluating:
if fav_tool == 'Ansible'
I like Ansible
endif
However, when we use Jinja delimiters:
{% if fav_tool == 'Ansible' %}
I like Ansible
{% endif %}
It will result in:
I like Ansible
When we use conditions such as when:, we don't need delimiters as the conditionals will automatically interpolate the variable to value.
Consider everything as variables and expressions unless told otherwise (with '')
Example:
The case is reversed here and whatever is not enclosed in single-quotes is automatically evaluated. Only 'Ansible' is considered as text here (not evaluated).
vars:
fav_tool: Ansible
tasks:
- debug:
msg: "Ansible rocks!"
when: fav_tool == 'Ansible'
It worked for me with
when:
(inventory_hostname in groups['testdb'])
I am unable to source from Ansible documents a clear meaning of a conditional such as when: var | d(). Is someone able give a clear explanation?
E.g. Below works whether inputing extra-var value from cli or defaulting to local ENV variable value:
vars:
my_var: "{{ e_var | default(ansible_env.USER | default(False,true)) }}"
tasks:
- name: Conditional check
debug:
msg: "{{ my_var }}"
when: my_var | d()
But this fails:
vars:
my_var: "{{ e_var | default(ansible_env.USER | default(false,true)) }}"
tasks:
- name: Conditional check
debug:
msg: "{{ my_var }}"
when: my_var
What is when: my_var | d() exactly doing? How how does it interplay with the | default(false,true) part in the variable declaration?
d is an alias to the default filter. It is a Jinja2 filter, so head for the Jinja2 docs. They work the same:
default(value, default_value=u'', boolean=False)
[ ]
Aliases: d
Regarding the problem you are facing, it is because Ansible processes a condition consisting of only a variable name differently from a more complex expression (which is passed directly to Jinja2/Python) (the actual code starts here):
If the my_var variable has a value of user01, the conditional will try to find a value of user01 variable and fail because it doesn't exist.
If you just add a logical conjunction (which in common sense is redundant), Ansible will process the whole expression differently and it will work:
when: my_var and true
In your case using another default filter in the expression is also redundant, but it prevents Ansible from trying to resolve a "nested" variable value.
I am trying to create configuration files from a template with include variables based on the fourth character of {{ ansible_hostname }}.
What works:
playbook:
---
- hosts: spock
roles:
- templaterole
role:
---
- name: testing autofs template on spock
template:
src=autofs
dest=/tmp/autofs
with_items:
- "{{ var_a }}"
when: ('{{ ansible_hostname }}' == "spock")
vars/main.yml:
var_a:
-
var_1: 'this is var_a1'
var_2: 'this is var_a2'
var_b:
-
var_1: 'this is var_b1'
var_2: 'this is var_b2'
template:
{{ item.var_1 }}
#
{{ item.var_2 }}
#
This works as expected and the output produces a /tmp/autofs file on the spock host that looks like:
this is var_a1
#
this is var_a2
#
Now, if I try to write the file based on trying to pull out the 4th character of the {{ ansible_hostname }}, the play does not get a match on the conditional and does not write the file. I'm trying this conditional in my role:
---
- name: testing autofs template on spock
template:
src=autofs
dest=/tmp/autofs
with_items:
- "{{ var_a }}"
when: ('{{ ansible_hostname }} | cut -c4' == "c") or
('{{ ansible_hostname }} | cut -c4' == "k")
the play skips this task due to not matching on the conditional. Ultimately i want to be able to pull any 4th character of our hostnames as this will always be predictable (can only be one of 4 known characters which defines my environment and lets me define the correct template variables based on these diff production environments.)
Can anyone help me to redefine my when statement such that i can do or conditionals and pull characters out of defined ansible variables like ansible_hostname?
Don't use curly brackets inside when statement, it's already a Jinja2 statement.
And in Jinja2 statements you use | to apply filter, but there is no cut filter available.
Your statement should be as simple as:
when: ansible_hostname[3] in 'ck'