unable to parse ansible variable in jinja file using if condition - ansible

I'm encountering below error while trying to pass ansible variable through if condition in jinja. (elsewhere I'm able to pass a string direclty and use the same condition in jinja and also able to read the ansible variable without any condition) Any leads please...
yaml extract
---
tasks:
- set_fact:
ansible_role: "role1"
- name: Create the Jinja2 based template
template: src=./source.j2 dest=./output.txt
source.j2
---
{% for role in roles %}
{% if {{ ansible_role }} == role.name %}
{% for item in role.tests %}
"{{ item }}"
{% endfor %}
{% endif %}
{% endfor %}
error
TASK [Create the Jinja2 based template]
***************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "AnsibleError: template error while templating string: expected token
':', got '}'. String: ---\n{% for role in roles %}\n{% if {{
ansible_role }} == role.name %}\n{% for item in role.tests
%}\ninclude_controls \"{{ item }}\"\n{% endfor %}\n{% endif %}\n{%
endfor %}\n"}

Take some time to review the jinja2 template designer documentation. Your variable name is already inside a jinja2 expression. The double curly braces has to be used outside expressions to have the content of your variable written out, not inside an expression.
Therefore, you should change:
# !! WRONG !!
{% if {{ ansible_role }} == role.name %}
to
{% if ansible_role == role.name %}
My last 2 cent: don't prepend your own variable names with ansible_ as this is used by ansible itself for a lot of internal/magic variables and might lead to confusion.

I think the problem is in this line: {% if {{ ansible_role }} == role.name %}. When you use the {% %} notation, Jinja expects python inside. So you don't need to escape the ansible_role variable. Try it without it and see if now it works.

Related

Ansible template mix up order of elements

I have a problem with may came up with a new Ansible version, as it worked before:
I'm passing this block to the ansible template
- monitoring-test-blackbox_exporter:
source: "{{ consul_template_template_dir }}/blackbox_exporter.ctmpl"
destination: "/etc/prometheus/file_sd/blackbox_exporter.json"
create_dest_dirs: true
command_timeout: "60s"
error_on_missing_key: false
grafana_link: "xtkCtBkiz"
This is the template:
# Template configuration
{% for ctmpl in consul_template_templates_config_node %}
# {{ ctmpl | first }}
template {
{% for option, value in ctmpl.items() %}
{% if value is sameas true %}
{{ option }} = true
{% elif value is sameas false %}
{{ option }} = false
{% elif value is string %}
{{ option }} = "{{ value|string }}"
{% elif value is number %}
{{ option }} = {{ value|int }}
{% endif %}
{% endfor %}
ctmpl | first always worked before to filter out first element monitoring-test-blackbox_exporter this is important as we use it later in the template configuration.
I tried several things with sort and select attributes neither of them worked. Does anyone have an idea to get it working again?

Creating text file from jinja template with inputs from csv file

I'm trying to create a text file which goes through the CSV file and populates variables into specific fields.
playbook.yml:
- hosts: localhost
tasks:
- name: "Reading user information"
read_csv:
path: /home/test/vlans.csv
delimiter: ','
register: vlans
- debug: var=vlans
- name: Creating VLANs configuration
template:
src: vlan.conf.j2
dest: /tmp/vlan.conf
Jinja2 Template vlan.conf.j2:
{% for item in vlans %}
!
vlan {{ item.VLAN }}
name {{ item.Description }}
vn-segment {{ item.VNI }}
interface nve1
member vni {{ item.VNI }}
{% endfor %}
and this is a test vlans.csv file:
Tenant,VRF ,VLAN,VNI,Subnet,Description,Good to go
Test,,5,20005,,LAB-Checkpoint-FW-Mgmt,Yes
Test,,208,20208,,LAB-DMZ,Yes Test,,209,20209,,LAB-CSR-MGMT,Yes
Test,10000,210,20210,192.168.12.1/28,LAB-VRF-to-FW,Yes
Prod,,761,20761,,PROD-CORE,Yes
Prod,105,840,20840,172.18.33.1/24,Backups,Yes
Prod,,841,20841,,Transport,Yes
I want to end up with file like in jinja2 template and not repeating line "interface nve 1"
In your question, interface nve1 is inside a loop. It will repeat multiple times in the resulting vlan.conf file.
Use multiple loops in the jinja template to decide what is repeated and what isn't:
{% for item in vlans.list %}
vlan {{ item.VLAN }}
name {{ item.Description }}
vn-segment {{ item.VNI }}
{% endfor %}
interface nve1
{% for item in vlans.list %}
member vni {{ item.VNI }}
{% endfor %}
Note that I've referred to vlans.list in the start of the loops instead of vlans. This is correct as per documentation, but different to the example in the question so it may need adjusting.

How to set a value in defaults/main.yml dynamically from inventory

I have a dynamic inventory for my project and on deployment the host_groups get loaded with inventory list. Meantime, I want to set a variable named master_ip with value from host_vars [master] in a role/defaults/main.yml.
My hosts are grouped as below:
[master]
xx.eco.project.com
[slave]
yy.eco.project.com
zz.eco.project.com
As i cannot use if/else in YAML. Like in templates i use,
{% if inventory_hostname in groups['master'] %}
master_ip: {{ lookup('dig', inventory_hostname) }}
as master_ip: 10.0.1.1. How is it possible?
I have solved this issue by defining a macro function in templates to determine the master and return the value to the template variables for master_ip. I find this more meaningful than the initial idea of getting the value in defaults/main.yml .
{% macro master_ip() -%}
{% for inventory_hostname in groups['master'] -%}
{{ lookup('dig', inventory_hostname) }}
{%- endfor -%}
{% endmacro -%}
Use in template
bind {{ master_ip() }}

Variable in Jinja2 For Loop

We're trying to come up with a way to use ansible facts within jinja2 For Loops.
For example, I want to get all servers that belong to my memcached group as well as a group based on release (something like tag_release_dev or tag_release_prod). When I try to use {{ tt_release }} within the For Loop it evaluates {{ tt_release }} rather than the value of the variable. Is there a way to use a variable within the loop definition?
{% for host in groups["tag_function_mem"] | intersect(groups["tag_release_{{ tt_release }}"]) %}
{{ host }}:11211
{%- if not loop.last %},{% endif %}
{%- if loop.last %}"{% endif %}
{% endfor %}
{% endif %}
it evaluates {{ tt_release }} rather than the value of the variable.
This is because you already are inside a expression. You can not nest expressions - and you don't need to.
What you want is to concatenate the string "tag_release_" and the variable tt_release. In Jinja2 concatenation is done with a +.
{% for host in groups["tag_function_mem"] | intersect(groups["tag_release_" + tt_release]) %}

Multiple groups when iterating in a template file

This article from Ansible.com shows how you can iterate over a group inside of a template file: https://support.ansible.com/hc/en-us/articles/201957887-How-to-loop-over-a-list-of-hosts-in-a-group-inside-of-a-template-
It shows the following code:
{% for host in groups['db_servers'] %}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}
It works beautifully, but the servers I want to iterate over are defined by being in multiple groups. So imagine that I want to iterate over all of the servers that are in BOTH the db_servers and qa groups. How can I do this?
I tried to specify the intersection of the group in the same manner I do in my playbook, but that doesn't work. So, when I try:
{% for host in groups['db_servers:&qa'] %}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}
I get the following error:
fatal: [54.173.247.115] => {'msg': "AnsibleUndefinedVariable: One or more undefined variables: 'dict object' has no attribute 'db_servers:&qa'", 'failed': True}
Any suggestions on how to iterate over multiple groups in a template file?
Ansible has the intersect filter. See Set Theory Filters.
{% for host in groups['db_servers'] | intersect(groups['qa']) %}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}
You could wrap your loop in another for the two server groups:
{% for svrs in ['db_servers', 'qa'] %}
{% for host in groups[svrs] %}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}
{% endfor %}

Resources