How to prevent Ansible from merging host variables? - ansible

I have a host under two group names (in the example below). If group_1 is called I want it to connect to it via ssh, but if group_2 is called I want it to be a local connection. However, ansible seems to be merging the two hosts variables together even though they're in different groups? It's using a local connection for group_1.
How can I prevent this?
[group_1]
example.com ansible_user=ansible ansible_ssh_private_key_file="{{ lookup('env','PATH_TO_KEYS') }}"/my.pem
[group_2]
example.com ansible_port=8081 ansible_connection=local

The inventory hostname can be arbitrary but it is the key identifier for the host so the vars will aggregated as described here: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable
We can use the arbitrary string combined with ansible_host to kind of game the system to do what you want. https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#hosts-and-non-standard-ports
Also, note that the ansible_connection=local is going to execute code on your localhost without use of any connections or service daemon (ssh or otherwise) so the ansible_port is not necessary.
[group_1]
example_ssh ansible_host=example.com ansible_user=ansible ansible_ssh_private_key_file="{{ lookup('env','PATH_TO_KEYS') }}"/my.pem
[group_2]
example_local_8081 ansible_host=example.com ansible_port=8081 ansible_connection=local

Related

Ansible: Connect to Nodes without DNS or /etc/hosts" resolution

I have a Dynamic Inventory Script which outputs the following.
"NODE_A": {
"inventory_hostname": "10.0.2.6",
"inventory_lparname": "NODE_A"
}
The Nodes are not resolvable via DNS or something as this Network is some kind of isolated "Management" LAN.
Until now i had a Play running which modifies the local /etc/hosts File to enable Name Resolution.
As the Ansible Controller is going to move to an foreign Machine, this is not possible anymore.
So the big question is how to proceed. How do i instruct Ansible to connect to the IP Adress instead of the Hostname, repectively can i use "inventory_hostname" instead of "ansible_hostname" as Connection String, but keep the Hostname displayed in the Play Recap?
How do i instruct Ansible to connect to the IP Adress instead of the Hostname, repectively can i use "inventory_hostname" instead of "ansible_hostname" as Connection String, but keep the Hostname displayed in the Play Recap?
The normal way you handle this is to set the inventory hostname to the "friendly" name, and then set ansible_hostname to the ip address. That is, if your inventory script reports a host named "host0", then when called with --host host0 it should produce:
{
"inventory_hostname": "host0",
"ansible_host": "10.0.2.6",
}
You will see the name host0 in playbook output, but ansible will use the ip address for connections.
An option would be to extend the Dynamic Inventory Scrip to add or replace nodes in an inventory directory. For example in INI format
NODE_A ansible_host=10.0.2.6

Ansible run same role multiple times using different variable files

Supose I have a server that is used to serve one static website (e.g. catpics.com). Using Ansible, I have a base role (used to set up firewall, time, user accounts, etc.), a letsencrypt role (gets TLS certificates for a website), and a webserver role (creates web root directory for a website).
Now I want to host another website (e.g. snakepics.com) on this same server. With this new addition, the base role still needs to be run once, but the letsencrypt and webserver roles now need to be run twice. At this point, I have two variable files:
vars/catpics.com:
---
domain_name: catpics.com
# and other variables...
...
vars/snakepics.com:
---
domain_name: snakepics.com
# and other variables...
...
Now that I have these two variable files, how do I run both the letsencrypt and webserver roles twice? (once using the vars/catpics.com variables file, and once using the vars/snakepics.com variables file)
You can use different Host Groups to make it run for each type and achieve your goal. There are 2 types of variables for hosts.
Host Variable which is specific to that individual host that is within a group
Group Variable which is for that whole host group
inventory / hosts file.
[catpics]
catpics.com http_port=301
morecatpics.com http_port=80
[snakepics]
snakepics.com
moresnakepics.com
note the http_port= is a Host Variable that is inline with the hostname declaration, seperated by a space
then in that same file, using the host:vars naming format, you define the Group Variables which is for the whole group. So all snakepics hosts will have those variables and all catpics hosts will have theirs, but keeping their individual Host Variables ie. http_port=
[snakepics:vars]
FQDN= https://www.snake.com
VAR2= something2
[catpics:vars]
FQDN= https://www.cat.com
VAR2= something2
So great, now we've got the hosts setup, we can define what roles are run for each host. The same role will be run
- hosts: catpics
roles:
- letsencrypt
- webserver
- hosts: snakepics
roles:
- letsencrypt
- webserver
or in this case, hosts could simply look like - hosts: all and it will get the vars corresponding to each type of host (snake and cat).
To call the variables is like any other ansible variable fetch {{ variable_name }} when running a task in a role:
-name: Show how variables like this work
domain_name: "{{ FQDN }}"
some_other_task_variable: "{{VAR2}}"
For reference:
Host and Group variables
Now that will solve your issue in the quickest way. The current best practice for using group and host variables uses a seperate file in the group_vars/*role_name_file* directory to automatically fetch all the variables for each Host Group

Ansible get hostname as defined in inventory

In my inventory I define hosts like this:
[server1]
141.151.176.223
I am looking for a variable which keeps the server1 name, as I am using it to define server hostname.
inventory_hostname is set to 141.151.176.223
ansible_hostname as well as inventory_hostname_short is set to 148.
To workaround this problem I am setting my own variable like this:
[server1]
141.151.176.223 hostname=server1
but I am not satisfied with this approach.
Any ideas?
Explanation
If the inventory file was defined this way:
[server1_group]
server1 ansible_host=141.151.176.223
Then you can access:
server1 with the inventory_hostname fact;
141.151.176.223 with the ansible_host fact;
server1_group with group_names|first (group_names fact contains a list of all groups server belongs to, first selects the first element from that list).
Regardless of the above, ansible_hostname fact contains the host name as defined on the host itself (the value is set during facts gathering).
Solution
You should use a standard ansible_host declaration to point to the target's IP address and set the inventory hostname to however you want to refer to the server in Ansible playbooks.
In particular you can skip groups definition altogether and define just:
server1 ansible_host=141.151.176.223
The [server1] declaration is the name of a group, not a host (even if you only assign a single host to that group).
As ansible allows a host to be placed in multiple groups, you can only get the names as an array: http://docs.ansible.com/ansible/latest/playbooks_variables.html#magic-variables-and-how-to-access-information-about-other-hosts
So I think the workaround that you found is the way to go, unless you can use real host names instead of IP addresses (using DNS or just static hosts file).

Inventory with both a hostname and IP address pattern

I'm using the vmware_guest module to create a bunch of vms.
Is there anyway to use a pattern for both the hostname and IP address?
For example I want to create 5 master vms with hostname/IP like:
master1 10.123.0.1
master2 10.123.0.2
master3 10.123.0.3
etc.
So an inventory yaml with something like the following:
all:
children:
Elvis:
children:
masterNodes:
hosts:
master[1-5]:
ansible_host: 10.123.0.[1-5]
slaveNodes:
hosts:
slave[1-10]:
ansible_host: 10.124.0.[1-10]
Also, is there a way to run a playbook for a specific parent:child group?
How can I run a play for Elvis:masterNodes only? If for instance, the same inventory yaml has another parent child group Bono:masterNodes
Is there anyway to use a pattern for both the hostname and IP address?
No. Instead, Ansible allows running a script to generate an inventory - it is trivial to implement a loop populating inventory-hostname along with IP address in Python (apparently it was made clear as early as in July 2013, although in the same thread someone suggested a workaround).
Also, is there a way to run a playbook for a specific parent:child group?
No. There is no hierarchy in Ansible inventory; the namespace is flat; there are no child groups; Ansible operates on host/group sets.
In effect, you can't have distinct (sub)groups bearing the same name. The contents of those (sub)groups will be merged. So if Elvis contains a group masterNodes and you execute a play with hosts:Elvis declaration, it will run on all hosts defined for masterNodes anywhere (including in Bono:masterNodes).

Run Ansible playbook without inventory

Consider if I want to check something quickly. Something that doesn't really need connecting to a host (to check how ansible itself works, like, including of handlers or something). Or localhost will do. I'd probably give up on this, but man page says:
-i PATH, --inventory=PATH
The PATH to the inventory, which defaults to /etc/ansible/hosts. Alternatively, you can use a comma-separated
list of hosts or a single host with a trailing comma host,.
And when I run ansible-playbook without inventory, it says:
[WARNING]: provided hosts list is empty, only localhost is available
Is there an easy way to run playbook against no host, or probably localhost?
Prerequisites. You need to have ssh server running on the host (ssh localhost should let you in).
Then if you want to use password authentication (do note the trailing comma):
$ ansible-playbook playbook.yml -i localhost, -k
In this case you also need sshpass.
In case of public key authentication:
$ ansible-playbook playbook.yml -i localhost,
And the test playbook, to get you started:
- hosts: all
tasks:
- debug: msg=test
You need to have a comma in the localhost, option argument, because otherwise it would be treated as a path to an inventory. The inventory plugin responsible for parsing the value can be found here.
You can define a default inventory with only localhost
See it is explained here:
https://docs.ansible.com/ansible/latest/reference_appendices/config.html#the-configuration-file
And in your playbook add use this
- hosts: all
connection: local
tasks:
- debug: msg=test
It will use local connection so no SSH is required (thus it doesn't expose your machine). It might be quicker unless you need to troubleshoot a ssh issue.
Also for quicker feedback loop you can use: gather_facts: no you already know your target.

Resources