How to use hosts in my playbook for initiating my template - ansible

I have a playbook to initiate a conf to servers that requires hostname. I want to pass my hosts that define in playbook as group so as to loop the variables in my jinja2 template. And I don't want to set the hosts in vars(because that was already define in playbook, I don't know the reason for re-define)
For example:
host file:
[test_servers]
t1
t2
t3
[test2_servers]
t2
t4
playbook:
- hosts: test_servers
tasks:
- name: generate my conf
template:
src: templates/temp.conf.j2
dest: "test.conf"
force: True
vars:
hosts: test_servers # So far I need to declare the var here duplicately, I've group my server in host file and I just want to use the current group.
temp.conf
....
{% for host in groups[hosts] %}
Entry.{{ loop.index }} = {{ host }}
{% endfor %}
....
I wonder if there is a better way to pass the hosts that set in playbook to my jinja2 template so I can re-use the playbook for different hosts. For example, I just need to reset the playbook hosts to test2, no need to rewrite the vars.

There is no special variable with the name of the group, but there is ansible_play_hosts_all
List of all the hosts that were targeted by the play
Remove vars and use ansible_play_hosts_all in the template
- hosts: test_servers
tasks:
- name: generate my conf
template:
src: templates/temp.conf.j2
dest: "test.conf"
force: True
{% for host in ansible_play_hosts_all %}
Entry.{{ loop.index }} = {{ host }}
{% endfor %}

Related

How can I copy multiple files from inventory_hostname in Ansible?

I want to log my config files, using inventory_hostname variable but it's only considering the first host in my inventory file and copying its config to all the other hosts. (Note: All 3 configs are different)
My Task:
- name: Logging Output
blockinfile:
path: "./output.log"
block: |
Config files
------------------------------
{% for host_name in ansible_play_hosts_all %}
{{ host_name }}
{{ inventory_hostname }}.cfg
{% endfor %}
My inventory file:
[junos]
junos-1
junos-2
junos-3
[junos:vars]
ansible_network_os=junos
ansible_connection=netconf
My config files:
junos-1.cfg
junos-2.cfg
junos-3.cfg
which were created using:
- name: Generate Config
template:
src: ./generate_config.j2
dest: ./{{ inventory_hostname }}.cfg
Can someone please suggest how can I copy all my configs for their respective hosts or if there is a way I can loop within inventory_hostname in the same task to fetch my configs?

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

Run an imported Ansible playbook for each unique value in a set of host vars

I've got a playbook which needs to run against my entire inventory, with a list of hostnames as an extra variable (target_hosts).
The hosts in target_hosts all have a group_id hostvar defined on them. I use the whole inventory because some ancillary hosts which correspond to the group_id var need per-group configuration to match in one section.
There will often be multiple group_id values associated with the hosts in the target_hosts list. I need to select the correct inventory group of ancillary hosts and import/run a playbook to configure both sets of servers partway through the main playbook.
This is what I currently do:
include_playbook: group-configure.yaml
vars:
src_hosts: "group-{{ group_id }}-ancillary-1"
dest_hosts: "{{ target_hosts }}"
I currently have to manually separate the target_hosts by group_id manually, then run the main playbook once for each. This has tons of unnecessary overhead.
What I really want to execute is this:
for each group of hosts from `target_hosts` with the same `group_id` hostvar:
import and run group-configure.yaml with:
src_hosts: "ancillary-{{ group_id }}"
target_hosts: restricted to those with that value of `group_id`'
How can I do this? If the current way this is structured won't work, what's the best alternative approach?
I am pretty sure the add_host: combined with groupby is what you are looking for, which will allow you to roll up those hosts by their attribute, and then run the playbook against them as if that group was defined already:
- hosts: localhost
connection: local
gather_facts: no
become: no
vars:
list_of_name_groups: >-
{%- set results = [] -%}
{%- for g_id, items in (dict(hostvars) | dict2items | groupby("value.group_id")) -%}
{%- for hostname in (items | map(attribute="key") | list) -%}
{%- set _ = results.append({"group_id": g_id, "hostname": hostname}) -%}
{%- endfor -%}
{%- endfor -%}
{{ results }}
tasks:
- add_host:
name: '{{ item.hostname }}'
groups: ancillary-{{ item.group_id }}
with_items: '{{ list_of_name_groups }}'
- hosts: ancillary-my-awesome-groupid
# etc etc

How to get all inventory variables for hosts in same group (except the current host)?

Suppose we have an ansible inventory like this:
[somegroup]
host1 secondaryIp=1.0.0.0
host2 secondaryIp=1.0.0.1
host3 secondaryIp=1.0.0.2
While running a task (specifically template module) is there a way to get the list of secondaryIp's for "all other hosts" in the group [somegroup] ??
I tried searching ansible filters: http://docs.ansible.com/ansible/latest/playbooks_filters.html#list-filters
My only working idea was to do this in inventory:
[somegroup]
host1 secondaryIp=1.0.0.0 otherIps="1.0.0.1,1.0.0.2"
host2 secondaryIp=1.0.0.1 otherIps="1.0.0.0,1.0.0.2"
host3 secondaryIp=1.0.0.2 otherIps="1.0.0.0,1.0.0.1"
You can get a list of hosts in the group using groups['somegroup'] and access their variables using hostvars. To exclude the host ansible is currently running on you just need to check if the current host in the list equals inventory_hostname. Here's how it works in practice.
In a template
{% for host in groups['somegroup'] %}
{% if host != inventory_hostname %}
{{ hostvars[host]['secondaryIp'] }}
{% endif %}
{% endfor %}
In a task
- name: debug
debug:
msg: "{{ hostvars[item]['secondaryIp'] }}"
when: item != inventory_hostname
with_items: "{{ groups['somegroup'] }}"

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