Variable in Jinja2 For Loop - ansible

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]) %}

Related

How to trim last character when using ansible jinja loop

My template like as blow
{% if hostvars[inventory_hostname].local_zk_server_id is defined %}
zookeeperServers={% for host in {{ groups[{{ target_hosts }}] %}}
"{{ hostvars[host].inventory_hostname }}:2181,"
{% endfor %}
{% endif %}
output ishost1:2181,host2:2181,host3:2181,
How to trim last comma
There are several possible gotchas in your above template regarding variables access. Moreover, rather than trimming the last character in your string, the best solution is probably not to write it. Here is a better solution IMO in my below example fixing all the problems I'm referring to:
{% set zookeeperServers=[] %}
{% if hostvars[inventory_hostname].local_zk_server_id is defined %}
{% for host in groups[target_hosts] %}
{% zookeeperServers.append(hostvars[host].inventory_hostname + ":2181") %}
{% endfor %}
zookeeperServers="{{ zookeeperServers | join(',') }}"
{% endif %}

How can I use AWS tags in a template?

I know that it is possible to loop over lists in a Jinja2 template like this:
{% for host in vars['play_hosts'] %}
"{{ host }}"{% if not loop.last %},{% endif %}
{% endfor %}
(Example stolen from this question.)
How can I use AWS tag based values like "the IPs of all hosts with value 'dev' in tag 'Environment'" instead of vars['play_hosts']?
I found out myself that it is only a matter of using the right group. All ec2 instances with the same tag can be found in a group and can be addressed by using e.g. groups['tag_Environment_dev']. So the example from the question would look like this:
{% for host in groups['tag_Environment_dev'] %}
"{{ host }}"{% if not loop.last %},{% endif %}
{% endfor %}

Ansible: get list of hosts in comma separated value

I have the following loop in a template:
{% for host in groups['dbnodes'] %}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}
the issue is that it gives the output in list of ip's and I need it in comma separated value. Any idea how to achieve that?
the answer I get look like this:
10.0.0.190
10.0.0.117
10.0.0.151
but I need it like this:
10.0.0.190,10.0.0.117,10.0.0.151
A quick fix to your Jinja2 template:
{% for host in groups['dbnodes'] -%}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}{% if not loop.last %},{% endif %}
{%- endfor %}

Short version of if var is defined for ansible templates

Jinja2 in Ansible templates allows this type of expression in templates:
{% if foobar is defined %} foo_bar = {{foobar}} {% endif %}
{% if barfoo is defined %} bar_foo = {{barfoo}} {% endif %}
etc.
Is there any shorter version to say 'do not print this line if its variable is not defined?
Something like foo_bar = {{foobar|skip_this_line_if_undefined}}?
You can use the default(omit) filter. For details have a look at the documentation.
You could use a macro.
{% macro line(key, value) -%}
{% if not value|none %}{{ key }} = {{ value }}{% endif %}
{%- endmacro %}
Then just call the macro for every key/value pair.
{{ line('foo_bar', foobar) }}
{{ line('bar_foo', barfoo) }}
Could be problematic in edge cases though. If foobar or barfoo are not defined it probably will raise an error. In the macro, value in any case would be defined, so the condition is defined doesn't make sense any more. But if null/none actually is a valid value for any of the variables, you hit the wall...
A bit longer but probably water proof:
{% macro line(key, value) -%}
{% if value != omit %}{{ key }} = {{ value }}{% endif %}
{%- endmacro %}
{{ line('foo_bar', foobar|default(omit)) }}
{{ line('bar_foo', barfoo|default(omit)) }}

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