How to create dynamic list in yaml? - yaml

I am trying to render a template. I have to create a list as
host:
- '111.222.333.444'
- '555.666.777.888'
which has to be taken from another host file.
I am using something like this:
{% for host in groups['hostgroup'] %}
host: {{ host }}
{% endfor %}
What is the correct way to achieve the result?

Assuming you wish to declare a variable in your inventory called host which contains a list of ip addresses, you can try:
host: {{ groups['hostgroup'] }}
Or you could skip declaring this variable and use {{ groups['hostgroup'] }} directly wherever you plan to use {{ host }}
Look at add-quotes-join thread and this filter plugin if you want quotes.
EDIT:
Assuming you are rendering a template, using the ansible template module
host:
{% for host in groups['hostgroup'] %}
- '{{ host }}'
{% endfor %}

Related

Ansible Jinja2 template for loop

I have two linux servers:
- server1: ip: 10.241.55.6, hostname: server1
- server2: ip: 10.242.55.7, hostname: server2
I have created an ansible inventory file named servers with the content bellow:
[IC]
10.241.55.6
10.241.55.7
Now I have created this jinja2 inventory template file: test.j2 with this content:
[IC]
{% for hostip in groups['IC'] %}
{% if hostip == ansible_default_ipv4.address %}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{% endif %}
{% endfor %}
And I'm running this ansible playbook:
---
- name: Generate portal inventory file
hosts: all
tasks:
- name: Generate inventory
delegate_to: localhost
template:
src: inventory/test.j2
dest: inventory/test
The command is: ansible-playbook -i inventory/servers generate-inventory.yml
The final goal is that ansible connects to each of the servers from the inventory files and then based on the jinja2 inventory template, it creates a new inventory file with this format:
[IC]
10.241.55.6 default_hostname=hostname_of_the_server_with_that_ip
and so on...
The issue here with the for loop is that all the entries are with the same server ip (while I should have an entry for each of the servers with their respective hostnames):
[IC]
10.241.55.6 default_hostname=server1
10.241.55.6 default_hostname=server2
What I'm missing here? Also if there is any other better way to achieve this please let me know.
You're using the same variable twice in the template...
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
...so of course you're getting two identical lines. It sounds like you want to access the per-host value of this variable, which means you need to access it via hostvars.
Maybe something like this:
[IC]
{% for host in groups['IC'] %}
{{ hostvars[host].ansible_default_ipv4.address }} default_hostname={{ hostvars[host].ansible_nodename }}
{% endfor %}

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() }}

Accessing loop variable in file lookup path

I'm trying to dynamically read file content inside for loop in template. But it fails as soon as i try to use host variable inside file path.
Some background:
There're three entries in groups.docker_hosts.
All files it's trying to read exist and are readable.
Each file has content of file content xx where xx is host index
For instance this example works fine:
template.j2
{% for host in groups.docker_hosts %}
"{{lookup('file', '{{inventory_dir}}/pki/dev-docker01/test')}}"
{% endfor %}
result
"file content 01"
"file content 01"
"file content 01"
This one renders each host correctly as well
template.j2
{% for host in groups.docker_hosts %}
"{{host}}"
{% endfor %}
result:
"dev-docker01"
"dev-docker02"
"dev-docker03"
But as soon as i try using host inside file path i'm getting this error "msg": "AnsibleUndefinedVariable: 'host' is undefined"
{% for host in groups.mongo_hosts %}
"{{lookup('file', '{{inventory_dir}}/pki/{{host}}/test')}}"
{% endfor %}
Seems like it's running in new isolate context which only has access to global variables. Any idea how to actually make it see host variable correctly?
You are passing the value {{ host }} as name to the host. just use host as a variable.
{% for host in groups['webservers'] %}
"{{ lookup('file', 'test/' + host + '/foo.txt') }}"
{% endfor %}

Ansible nodes IPs as the one string

I use Vagrant with Ansible. In my playbook I have the following variable:
seeds: '192.168.56.11,192.168.56.12'
192.168.56.11 and 192.168.56.12 here are IP addresses of multi-machine Vagrant configuration.
Can I do my configuration more flexible using Ansible i.e. can Ansible compose this string programmatically for me?
You can use Jinja2 to template out your variable from other variables.
So if we have a list of things like this:
seeds:
- 192.168.56.11
- 192.168.56.12
We can turn that into a comma delimited string by looping through it with something like this:
seeds_string: '{% for seed in seeds %} {{ seed }}{% if not loop.last %},{% endif %}{% endfor %}'
As for getting the IP addresses of hosts in your inventory we can access facts about other hosts than the one being configured by using the groups and hostvars magic variables.
So to get the IP addresses of all hosts in the inventory we could use something like:
{% for host in groups['all'] %}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}
Combining this together we can then do something like this:
seeds: '{% for host in groups['all'] %} {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}{% if not loop.last %},{% endif %}{% endfor %}'

Jinja2 in Ansible Playbook

how can i loop in an ansible playbook over gathered facts?
I tried:
...
haproxy_backends:
- name: 'backend'
servers:
{% for host in groups['app-servers'] %}
- name: "{{ hostvars[host]['ansible_hostname'] }}"
ip: "{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}"
{% endfor %}
But this doesn't work, it results in a Syntax Error. Is it even possible to use jinja in a playbook?
I use a ansible galaxy role (info.haproxy) and i don't want to change the provided templates.
No you can't do this.
This has to be done in a template, something like :
template/haproxy.cfg.j2 :
...
{% for host in groups['app-servers'] %}
backend {{ hostvars[host]['ansible_hostname'] }}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}:1234 check inter 5000 slowstart 2m
{% endfor %}
...
and use :
tasks:
- name: Deploy haproxy config
template: src=templatepath/haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg
You get the idea, YMMV.
Good luck.

Resources