In Ansible playbook: Using a boolean in `when` clause that might be undefined - ansible

Ansible tasks can have when clauses like this:
- name: Conditional output
debug:
msg: This is a conditional output
when: some_var
In order to protect about some_var being undefined, one can use
- name: Conditional output
debug:
msg: This is a conditional output
when: some_var is defined and some_var
There also seems to be a variant like this:
- name: Conditional output
debug:
msg: This is a conditional output
when: some_var | d() | bool
Are these variants the same? What are their advantages/disadvantages?

The strict equivalence of your two safe examples would be:
when: some_var is defined and some_var | bool
vs
when: some_var | d() | bool
The bool filter makes sure the var content is interpreted as a boolean and that, as an example taken from my experience, a string answer to a vars_prompt is working as expected ("true" => true).
d is an alias to default and is documented in the official jinja2 documentation for the filter
The 2 above are strictly equivalent and will produce the same result on all occasion. But I do prefer the most compact one that I would further enhance for documentation reasons like this:
when: some_var | d(false) | bool

The second form is less readable than the first, but is more flexible. It reads as follows:
Evaluate some_var variable.
Have a default value of blank/None (since d() is shorthand for default()).
Filter to a boolean (for truthiness).
The first form reads as follows:
If some_var is defined
Evaluation of some_var (as a Python boolean)
If some_var is truthy or has the potential to be truthy, then the first form could fail if it is not precisely True or False.

Related

Ansible remove last character from string if its a number

I have a list like the following
fruit: [ 'apple','orange2','grape3apple1']
I want an output like the following and want this to be stored in another var for further use
fruit_out: [ 'apple','orange','grape3apple' ]
If the last character is a number, I want the number stripped, if not, I need the string returned as is. I have looked around a bit without much luck getting any usable solution.
Reiterating my comment for context:
Given a string variable somevariable, somevariable[-1] is the last character, and somevariable[:-1] is the string not including the last character. –
I am looking for a validation to check if the last character is indeed a number. If there is a number at in the middle it should not count for the check
Ansible uses Python, so you can use any Python string method in your Ansible playbooks. For example, to have a task execute when a value ends with a number, you could write:
- debug:
msg: Your variable ends with a number!
when: somevariable[-1].isdigit()
You could perform the same check using a regular expression filter:
- debug:
msg: Your variable ends with a number!
when: somevariable is search('[0-9]$')
One possibility would be to use a regex and the filter regex_search.
So, given the task:
- debug:
msg: >-
{{
fruits
| map('regex_search', '^(.*?)[0-9]?$', '\1')
| map('first')
}}
vars:
fruits:
- apple
- orange2
- grape3apple1
This yields the expected:
ok: [localhost] =>
msg:
- apple
- orange
- grape3apple
The only tricky part in that regex is that you have to make your capturing group lazy, with the help of the lazy quantifier *?, otherwise your matched group will spawn over the unwanted number.
Use the filter regex_replace. For example, put the declarations below as appropriate
fruit: [apple, orange2, grape3apple1]
fruit_regex: '^(.+?)\d*$'
fruit_replace: '\1'
fruit_out: "{{ fruit|map('regex_replace', fruit_regex, fruit_replace)|list }}"
gives
fruit_out:
- apple
- orange
- grape3apple
Explanation of the regex
^ ....... matches the beginning of a string
(.+?) ... creates a capture group
. ... matches any character
+? ... 1 or more (append ? for non-greedy)
\d* ... digit, 0 or more
$ ....... matches the end of a string
Example of a complete playbook
- hosts: localhost
vars:
fruit: [apple, orange2, grape3apple1]
fruit_regex: '^(.+?)\d*$'
fruit_replace: '\1'
fruit_out: "{{ fruit|map('regex_replace', fruit_regex, fruit_replace)|list }}"
tasks:
- debug:
var: fruit_out

How to use variable substitution in a when condition?

I'm a new ansible user. I need a variable in a task's when condition.
I am trying to use home_directory variable, which is defined as home_directory: "{{ ansible_env.HOME }}" in the vars/main.yaml file.
I tried to use something like following:
when: {{ home_directory}}/var_name['var_key'] != command_output['stdout']
However, I later found out that jinja templates {{}} or {%%} are not allowed/recommended in the
when condition. I also tried condition without quotes and {{}} but home_directory value is
not being replaced in the when condition.
Could someone please tell me what I can do here?
However, I later found out that jinja templates {{}} or {%%} are not allowed/recommended in the when condition.
This is because the arguments to when are evaluated in an implicit template context. In other words, write exactly what you would write inside {{...}} markers, but you don't need the markers because the context is intrinsic to the command.
In other words, instead of:
when: {{ home_directory}}/var_name['var_key'] != command_output['stdout']
Write:
when: home_directory ~ "/" ~ var_name['var-key'] != command_output['stdout']
Where ~ is the Jinja string concatenation operator.
We can simplify that a bit:
when: "%s/%s" % (home_directory, var_name.var_key) != command_output.stdout
This takes advantage of Linux string formatting syntax to substitute variables into a string.

Use list key value in when in Ansible

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

Ansible - condition if a variable value exists in a variable

I am running a task which i only want to execute if the value of a variable that i have previously set does not exist in another variable. I have tried the following but it errors with a templating error:
name: get ip address
...
register: ipaddress
name: check cluster
....
register: topology
name: do my task
...
when: not topology is search(ipaddress)
Is there any way in ansible to accomplish what i am doing? I'm using version 2.6.
Use regex_filter.
Regular Expression Filters
To search a string with a regex, use the “regex_search” filter:
search for "foo" in "foobar"
{{ 'foobar' | regex_search('(foo)') }}
will return empty if it cannot find a match
{{ 'ansible' | regex_search('(foobar)') }}
case insensitive search in multiline mode
{{ 'foo\nBAR' | regex_search("^bar", multiline=True, ignorecase=True) }}
For your example, making a few assumptions -
when: not topology | regex_search(ipaddress, multiline=True)
(in a meeting, can't test this, please check it and let me know if it isn't quite right.)

What does conditional "when: var | d()" mean in Ansible 2.5

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.

Resources