Ansible | How to inherit variables in inventory - ansible

I have inventory:
[machine]
192.168.1.2
[group1:children]
machine
[group2:children]
machine
[group3:children]
machine
[group1:vars]
my_var="DOG"
[group2:vars]
my_var="CAT"
[group3:vars]
my_var="FISH"
When I run ansible -m debug -a "var=my_var" {group1 or group2 or group3 or machine} I always get my_var="FISH" because machine host inherits variable from the last group in file. How can I pass my_var value depending on the calling a specific parent group (group1, group2, group3)?

Related

How to Show Ansible Host Vars and Inventory Vars

Is there a command in Ansible in which I can see all the variables for a host. such as variables from the inventory and also from the host and group vars.
Ive tried ansible-inventory but that just provided inventory vars. The other options all seem to be printing via a playbook.
EDIT:
Ive tried the following.
ansible -m debug -i ../inventory.yaml -a "var=hostvars[inventory_hostname]" "spine1-nxos"
But this only works when Im within the folder of my host vars therefore ... the final questions are: how do I get the group vars and how do I provide it to the folder with the host and group vars.

Ansible- start multiple custom processes with overriding variables (same name) on same host (different group)?

So,
We have a scenario, where we need the ability to execute a custom command on a single or multiple hosts from a group with various possible values of the same variable.
For example-
#Inventory:
[ServerGroup_1]
abc0001 node=node1
abc0002 node=node2
[ServerGroup_2]
abc0001 node=node3
abc0002 node=node4
[ServersGroups: children]
ServerGroup_1
ServerGroup_2
group_vars/ServerGroup_1
JAVA_HOME: /home/java
PORT: 9998
group_vars/ServerGroup_2
JAVA_HOME: /home/java
PORT: 9999
The goal is to execute below shell command on host abc0001 with Ports as 9998 and 9999 within a single playbook run.
shell: {{ JAVA_HOME }} -Dprocess.port={{ PORT }}
Currently every time as per Ansible default variable behavior it is only getting executed for port 9999. Now, as an alternative, we could manually separate out the tasks and call it twice inside our playbook as explained here.
But, if we have 50 different ports that would be tedious to write and also we would want the configuration in such a way that it dynamically picks up from either inventory file or variable files, so for adding any new instance or running the command on different port, we just have to add it to our inventory/variable files rather than writing a separate task covering the port. The end configuration should work for all possible scenarios of running that command on one host of a group or all hosts from a group or a particular host and node combination....
ansible-playbook -i staging test_multinode.yml --limit=ServersGroups -l abc0001
The above playbook run should execute the shell command for both the ports 9998 and 9999 on abc0001 and the playbook needs to be flexible enough if just want to say start the process only for port 9998 on abc0001.
Note:
We have tried the with_items block by setting a Port variable in inventory file for the host, but that set up is very rigid and will not work for other scenarios.
We have also tried hash_behavior=merge and hash_behavior=replace settings in ansible.cfg, did not notice any change.
Hope this makes sense and We have not over-complicated things! Please suggest few options!!!
Q: "Execute a custom command on a single or multiple hosts from a group with various possible values of the same variable. Execute shell command on host abc0001 with Ports as 9998 and 9999 within a single playbook run."
A: Only dictionaries can be merged instead of default behavior replaced. See DEFAULT_HASH_BEHAVIOUR. Change the group_vars data to dictionaries. For example
shell> cat group_vars/ServerGroup_1
my_sets:
set1:
JAVA_HOME: /home/java
PORT: 9998
shell> cat group_vars/ServerGroup_2
my_sets:
set2:
JAVA_HOME: /home/java
PORT: 9999
Then, the playbook
shell> cat test.yml
- hosts: ServersGroups
tasks:
- debug:
msg: "{{ item.value.JAVA_HOME }} -Dprocess.port={{ item.value.PORT }}"
loop: "{{ my_sets|dict2items }}"
loop_control:
label: "{{ item.key }}"
gives (abridged)
shell> ANSIBLE_HASH_BEHAVIOUR=merge ansible-playbook -l abc0001 test.yml
ok: [abc0001] => (item=set1) =>
msg: /home/java -Dprocess.port=9998
ok: [abc0001] => (item=set2) =>
msg: /home/java -Dprocess.port=9999
Q: "We have also tried hash_behavior=merge and hash_behavior=replace settings in ansible.cfg, did not notice any change."
A: The replace option works as expected. The same playbook gives
shell> ANSIBLE_HASH_BEHAVIOUR=replace ansible-playbook -l abc0001 test.yml
ok: [abc0001] => (item=set2) =>
msg: /home/java -Dprocess.port=9999
Detailed Resolution
Short Answer- Rewrite the inventory file using aliases
#Inventory:
[ServerGroup_1]
#variable with name PORT on host abc0001 from group1
group1_node1 ansible_host=abc0001 PORT=9998
group1_node2 ansible_host=abc0002 PORT=9999
[ServerGroup_2]
#same variable name Port on the same host abc0001 present in a different group
group2_node1 ansible_host=abc0001 PORT=9998
group2_node2 ansible_host=abc0002 PORT=9999
[ServersGroups: children]
ServerGroup_1
ServerGroup_2
We are using group1_node1 as an alias, so by doing this Ansible will register group1_node1 and group2_node1 as two different hosts even though it’s the same host abc0001.
Now, we will be able to start two processes on the same host abc0001 using different parameters for the same variable name PORT.
ansible-playbook -i staging test_multinode.yml --limit=ServersGroups -l group1_node1:group2_node1
Hope this is clear.

How to list groups that host is member of?

I have a very complex Ansible setup with thousands of servers and hundreds of groups various servers are member of (dynamic inventory file).
Is there any way to easily display all groups that a specific host is member of?
I know how to list all groups and their members:
ansible localhost -m debug -a 'var=groups'
But I want to do this not for ALL hosts, but only for a single one.
Create a playbook called 'showgroups' (executable file) containing:
#!/usr/bin/env ansible-playbook
- hosts: all
gather_facts: no
tasks:
- name: show the groups the host(s) are in
debug:
msg: "{{group_names}}"
You can run it like this to show the groups of one particular host (-l) in your inventory (-i):
./showgroups -i develop -l jessie.fritz.box
There is group_names magic variable:
Additionally, group_names is a list (array) of all the groups the current host is in. This can be used in templates using Jinja2 syntax to make template source files that vary based on the group membership (or role) of the host
cat /etc/ansible/hosts | grep -e [[] && ansible all --list-hosts

Ansible: Include playbook according to inventory variable

I am trying to set up Ansible to be able to run a playbook according to what inventory group the host is in. For example, in the inventory, we have:
[group1]
host1.sub.domain.tld ansible_host=10.0.0.2
...
[group1:vars]
whatsmyplaybook=build-server.yml
Then we want to make a simple playbook that will more or less redirect to the playbook that is in the inventory:
---
- name: Load Playbook from inventory
include: "{{hostvars[server].whatsmyplaybook}}"
Where the "server" variable would be the host's FQDN, passed in from the command line:
ansible-playbook whatsmyplaybook.yml -e "server=host1.sub.domain.tld"
Our reasoning for this would be to have a server bootstrap itself from a fresh installation (PXE boot), where it will only really know its FQDN, then have a firstboot script SSH to our Ansible host and kick off the above command. However, when we do this, we get the below error:
ERROR! 'hostvars' is undefined
This suggests that the inventory is not parsed until a host list is provided, which sucks a lot. Is there another way to accomplish this?
A bit strange workflow, honestly.
Your setup doesn't work, because most of variables are not defined during playbook parse time.
You may be more lucky with defining single playbook with different plays for different groups (no need to set group var, just use correct host pattern (group name in my example)) and execute it limiting to specific host:
site.yml:
---
- hosts: group1
tasks:
- include: build-web-server-tasks.yml
- hosts: group2
tasks:
- include: build-db-server-tasks2.yml
command to provision specific server:
ansible-playbook -l host1.sub.domain.tld site.yml
You can develop your own dynamic inventory file so that all machines which needs to be bootstrapped will automatically added into your inventory and group respectively with out an manual entry in to the inventory file.
For developing dynamic inventory you can follow the below link:
http://docs.ansible.com/ansible/latest/dev_guide/developing_inventory.html
You can include multiple playbooks targeted to different groups as follows.
---
- hosts: all
tasks:
- include: build-web-server-tasks.yml
where: inventory_hostname in groups['group1']
- include: build-db-server-tasks2.yml
where: inventory_hostname in groups['group2']
inventory_hostname is the name of the hostname as configured in Ansible’s inventory host file. This can be useful for when you don’t want to rely on the discovered hostname ansible_hostname or for other mysterious reasons. If you have a long FQDN, inventory_hostname_short also contains the part up to the first period, without the rest of the domain.

Accessing group variable in template file of role belonging to another group

Inventory file:
[1]
IP
[2]
IP
[1:vars]
foo=test
How can I access variable foo in role which will be executed on group [2]?
Firstly, you cannot name host groups with a single digit, so fix the inventory file:
[group1]
IP1
[group2]
IP2
[group1:vars]
foo=test
Then when running against group2, to access the variable defined for group1, use the following construct:
- debug:
var: hostvars[groups['group1'][0]]['foo']
Generally, I would try to avoid such references though.
It doesn't matter where the variable come from (inventory,playbook,extras), you access it by name, like:
- debug:
msg: "{{ foo }}"
If variable with the same name is defined in multiple places, the one with higher precedence wins (see docs).

Resources