Ansible when conditional with composed variable - ansible

I know ansible when-statements should not contain any jinja2 delimiters {{}}.
However, is there away to avoid this in the following situation:
vars:
xvar: ['/path/file-blabla-abc', '/path/file-blabla-def']
avar: blabla
bvar: def
tasks:
- debug:
msg: "yippie"
when: "'/path/file-{{ avar }}-{{ bvar }}' in xvar"
Gives me the expected result:
ok: [localhost] => {
"msg": "yippie"
}
But also includes the warning:
[WARNING]: when statements should not include jinja2 templating delimiters
such as {{ }} or {% %}. Found: '/path/file-{{ avar }}-{{ bvar }}'
How could I work around this problem? I cannot leave out the jinja2 delimiters here, as the variables would be undetectable by ansible then.

It's possible to create an additional variable
- debug:
msg: OK
vars:
my_path: "/path/file-{{ avar }}-{{ bvar }}"
when: my_path in xvar

Related

Is it possible to check whether a dynamic variable is defined in Ansible?

The Ansible Documentation on conditionals provides examples of how a task can check whether a variable is defined:
- name: Fail if "bar" is undefined
ansible.builtin.fail: msg="Bailing out. This play requires bar"
when: bar is undefined
I'm building a playbook that checks whether a list of variables is defined. My first insinct was to write the code like this:
- name: Fail if any variables are undefined
ansible.builtin.fail: msg="Bailing out. This play requires {{ item }}"
when: item is undefined
with_items:
- bar
- foo
- baz
However, this doesn't work, since this is checking that the item variable itself is defined.
Use lookup plugin varnames. See
shell> ansible-doc -t lookup varnames
For example, if none of the listed variables is defined
- set_fact:
my_vars: "{{ my_vars|d({})|combine({item: _defined}) }}"
loop:
- bar
- foo
- baz
vars:
_defined: "{{ lookup('varnames', item)|length > 0 }}"
gives
my_vars:
bar: false
baz: false
foo: false
Test it. For example, the task
- assert:
that: my_vars_undef|length == 0
fail_msg: "Bailing out. This play requires: {{ my_vars_undef }}"
vars:
my_vars_undef: "{{ my_vars|dict2items|
rejectattr('value')|
map(attribute='key')|
join(',') }}"
will fail
TASK [assert] ***********************************************************
fatal: [localhost]: FAILED! => changed=false
assertion: my_vars_undef|length == 0
evaluated_to: false
msg: 'Bailing out. This play requires: bar,foo,baz'
Example of a playbook
shell> cat pb.yml
- hosts: localhost
tasks:
- set_fact:
my_vars: "{{ my_vars|d({})|combine({item: _defined}) }}"
loop:
- bar
- foo
- baz
vars:
_defined: "{{ lookup('varnames', item)|length > 0 }}"
- assert:
that: my_vars_undef|length == 0
fail_msg: "Bailing out. This play requires: {{ my_vars_undef }}"
vars:
my_vars_undef: "{{ my_vars|dict2items|
rejectattr('value')|
map(attribute='key')|
join(',') }}"
The playbook will continue if all variables in the list are defined
shell> ansible-playbook pb.yml -e bar=A -e foo=B -e baz=C
...
TASK [assert] **********************************************************
ok: [localhost] => changed=false
msg: All assertions passed
It's technically possible to do this with a template string:
- name: Fail if any variables are undefined
ansible.builtin.fail: msg="Bailing out. This play requires {{ item }}"
when: "{{ item }} is undefined"
with_items:
- bar
- foo
- baz
but it triggers the following warning from Ansible:
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ item }} is undefined

ansible variables usage - ansible_hostname or {{ ansible_hostname }}

I am new to ansible. What is the correct to call ansible variables? Here are the 3 playbooks, playbook 1 uses "{{ ansible_hostname }}", however, playbook 2 and 3 uses "ansible_hostname" directly. What are the differences? Thanks!
Playbook 1:
tasks:
- name: Jinja2 template
template:
src: template.j2
dest: "/tmp/{{ ansible_hostname }}_template.out"
trim_blocks: true
mode: 0644
Playbook 2:
tasks:
- name: Ansible Jinja2 if
debug:
msg: >
--== Ansible Jinja2 if statement ==--
{# If the hostname is ubuntu-c, include a message -#}
{% if ansible_hostname == "ubuntu-c" -%}
This is ubuntu-c
{% endif %}
Playbook 3:
tasks:
- name: Exploring register
command: hostname -s
when:
- ansible_distribution == "CentOS"
- ansible_distribution_major_version | int >= 8
register: command_register
playbook 1 uses "{{ ansible_hostname }}", however, playbook 2 uses "ansible_hostname"
That's not entirely correct. Both playbooks use the variable name ansible_hostname inside a Jinja templating context.
In the first playbook, it's simple variable substitution, so we use the {{ ... }} markers.
In the second playbook, it's being used in a control expression, so we use the {% ... %} markers.
In the third playbook, you're looking at the clauses of a when expression. From the documentation:
The when clause is a raw Jinja2 expression without double curly braces...
You can read more about Jinja syntax here.

jinja2 turning lists into strings even when from_json is being used

I am trying to run a nested for loop in order to retrieve a nested value.
I would like to retrieve some_value_4 when some_value_3 matches a criteria that's predefined.
{
"some_dict": {
"some_key_0": "value_0",
"some_key_1": "value_1"
},
"testval_dict": {
"test_key_0": "some_value_0",
"test_key_1": "some_value_1",
"test_key_2": "some_value_2",
"test_key_3": "some_value_3",
"test_key_4": "some_value_4"
}
}
The playbook:
- hosts: localhost
tasks:
- set_fact:
mydict: "{{ lookup('file', '/tmp/file.json' ) | from_json }}"
- debug:
msg: |
"{% for item in mydict %}
{{ item }}
{% endfor %}"
when run, it alreay looks like dict names are treated as string and nothing more:
ansible-playbook /tmp/test_playbook.yml -c local -i ', localhost'
TASK [debug] ******************************************************************
ok: [localhost] => {}
MSG:
" somee_dict
testval_dict
"
Then when I add an itme.key to the debug task, the playbook fails:
MSG:
The task includes an option with an undefined variable. The error was: 'str object' has no attribute 'value'
Thank you.
edit for clarification
In the real example, I will not know the names of the dicts, so I cannot use some_dict or testval_dict, that is why I'm trying to go over this data source in an item.key or item.value form.
Q: "{% for item in mydict %} ... dict names are treated as string and nothing more."
A: This is correct. A dictionary, when evaluated as a list, returns the list of its keys. See some examples below
- debug:
msg: "{{ mydict.keys()|list }}"
- debug:
msg: "{{ mydict[item] }}"
loop: "{{ mydict.keys()|list }}"
- debug:
msg: "{{ mydict|difference(['testval_dict']) }}"
give
msg:
- some_dict
- testval_dict
msg:
some_key_0: value_0
some_key_1: value_1
msg:
test_key_0: some_value_0
test_key_1: some_value_1
test_key_2: some_value_2
test_key_3: some_value_3
test_key_4: some_value_4
msg:
- some_dict
See How to iterate through a list of dictionaries in Jinja template?
If you need to loop over the dictionary, you can use with_dict loop functionality. This way, if you loop over mydict and get item.key you will get somee_dict and testval_dict.
tasks:
- set_fact:
mydict: "{{ lookup('file', '/tmp/file.json')|from_json }}"
# This will get the top level dictionaries somee_dict and testval_dict
- debug:
var: item.key
with_dict: "{{ mydict }}"
And if you get item.value of mydict you will get the sub-dictionary:
- debug:
var: item.value
with_dict: "{{ mydict }}"
Will produce (showing output only for testval_dict):
"item.value": {
"test_key_0": "some_value_0",
"test_key_1": "some_value_1",
"test_key_2": "some_value_2",
"test_key_3": "some_value_3",
"test_key_4": "some_value_4"
}

AnsibleError: template error while templating string: expected token 'end of print statement', got '{'

The following is a task, which is throwing me an error because jinja2 templating doesnt support this.
- name: Print the ip address
debug:
msg: "{{ ansible_{{ item }}['ipv4']['address'] }}"
with_items: "{{ ansible_interfaces|reject('search', 'lo')|list|sort }}"
The error thrown is:
"msg": "template error while templating string: expected token 'end of print statement', got '{'. String: {{ ansible_{{ item }}['ipv4']['address'] }}"
Any pointers on how to solve this issue?
You cannot use jinja2 expansion when you are already inside a jinja2 expansion expression. In other words mustaches don't stack.
In your case you can use the vars lookup to fetch your dynamically named var:
- name: Print the ip address
vars:
interface_var_name: "ansible_{{ item }}"
debug:
msg: "{{ lookup('vars', interface_var_name)['ipv4']['address'] }}"
with_items: "{{ ansible_interfaces | reject('search', 'lo') | list | sort }}"
Use lookup plugin vars. For example
- name: Print the ip address
debug:
msg: "{{ my_ifc.ipv4.address|default('Undefined') }}"
loop: "{{ ansible_interfaces|reject('search', 'lo')|list|sort }}"
vars:
my_ifc: "{{ lookup('vars', 'ansible_' ~ item) }}"
gives
ok: [localhost] => (item=eth0) =>
msg: 10.1.0.27
ok: [localhost] => (item=wlan0) =>
msg: Undefined

Is possible define a var at run-time and use it to access another var?

I'm not sure that this is possible.
I want to define a var at run-time and use it to access another var(defined in file,playbook..).
defined at run-time :
typeConfig (possible values: "in_config" or "out_config")
defined in playbook:
in_config:
url_config: http://localhost/configuration
out_config:
url_config: http://config.pi.dyn-dns.org/configuration
I need to resolve something similar to this:
{{ {{ typeConfig }}.url_config }}
I try with:
- name: Mytest
hosts: all
gather_facts: false
sudo: yes
vars:
- in_config:
url_config: http://localhost/configuration
- out_config:
url_config: http://config.pi.dyn-dns.org/configuration
tasks:
- set_fact:
typeConfig: in_config
- name: Value in_config.url_config
debug: msg=" {{in_config.url_config}}"
- name: Value out_config.url_config
debug: msg=" {{out_config.url_config}}"
- name: Value typeConfig
debug: var=typeConfig
- debug: msg="{{ {{ typeConfig }}.url_config }} "
ACTUAL RESULTS
task path: /home/nor/gitrepos/iiot-iac/ansible/myUnitTest.yml:19
fatal: [node1]: FAILED! => {
"failed": true,
"msg": "template error while templating string: expected token ':', got '}'. String: {{ {{ typeConfig }}.url_config }} " } " }
You can access the value using:
- debug:
msg: "{{ vars[typeConfig].url_config }}"
Remember that {{ ... }} is not a way to write a variable name, but to start a Jinja2 expression. And when querying values, variables are referenced using Jinja2 expressions in Ansible, thus using {{ {{ ... }} }} makes no sense.

Resources