What does ":&" mean in an Ansible/Jinja2 YAML file?
For example, in this line:
hosts: test-instances:&{{ target_host | default('None') }}
It is an intersection of two hosts groups in Ansible (it is not a Jinja2 syntax and is not used except for the hosts declaration).
In your example, the play will run only on the host (or host group) specified in the target_host variable as long as it is listed in the test-instances inventory group.
If target_host is not specified or target_host is not listed in the test-instances, the play will be skipped (assuming there is no host named None).
Per Working with Patterns:
You can also specify the intersection of two groups. This would mean the hosts must be in the group webservers and the host must also be in the group staging:
webservers:&staging
Related
Running Ansible 2.9.3
Working in a large environment with hosts coming and going on a daily basis, I need to use wildcard hostnames in a host group: ie:
[excluded_hosts]
host01
host02
host03
[everyone]
host*
in my playbook I have
name: "Test working with host groups"
hosts: everyone,!excluded_hosts
connection: local
tasks:
The problem is, the task is running on hosts in the excluded group.
If I specifically list one of the excluded hosts in the everyone group, that host then gets properly excluded.
So Ansible isn't working as one might assume it would.
What's the best way to get this to work?
I tried:
hosts: "{{ ansible_hostname }}",!excluded_hosts
but it errored as invalid yaml syntax.
requirements: I can not specifically list each host, they come and go too frequently.
The playbooks are going to be automatically copied down to each host and the execution started afterwards, therefore I need to use the same ansible command line on all hosts.
I was able to come up with a solution to my problem:
---
- name: "Add host name to thishost group"
hosts: localhost
connection: local
tasks:
- name: "add host"
ini_file:
path: /opt/ansible/etc/hosts
section: thishost
option: "{{ ansible_hostname }}"
allow_no_value: yes
- meta: refresh_inventory
- name: "Do tasks on all except excluded_hosts"
hosts: thishost,!excluded_hosts
connection: local
tasks:
What this does is it adds the host's name to a group called "thishost" when the playbook runs. Then it refreshs the inventory file and runs the next play.
This avoids a having to constantly update the inventory with thousands of hosts, and avoids the use of wildcards and ranges.
Blaster,
Have you tried assigning hosts by IP address yet?
You can use wildcard patterns ... IP addresses, as long as the hosts are named in your inventory by ... IP address:
192.0.\*
\*.example.com
\*.com**
https://docs.ansible.com/ansible/latest/user_guide/intro_patterns.html
I'm writing a role thats creates virtual machines & configures them. I need to perform a few tasks on the VM's before their DNS entries are created.
How can I get the task to connect to a known IP address instead of the usual ansible_host variable (FQDN)? I can't set it in the inventory file since the IP is not known when it is created.
TIA
The inventory file can use IP addresses. See the jumper example in https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html.
The way you typically handle this is to use Ansible's add_host module, which lets you create inventory entries dynamically during your playbook run. A task looks something like:
- name: add host to inventory
add_host:
name: my_new_host
ansible_host: "{{ new_host_ipaddr }}"
groups: [webservers]
You can see an example of this in action here (just search for add_host).
I have an Ansible role where I need to create an inventory file on the remote machine. I want to use the current inventory file, but with one single group removed.
For example, if I have the following inventory:
[operator]
host1.example.com
[control]
host2.example.com
host3.example.com
[compute]
host4.example.com some_var=value
host5.example.com
# some comment
[foo:children]
control
compute
[bar:vars]
var1=value
#var2=value
Then I need something that copies this:
- hosts: operator
tasks:
- name: Create inventory
...
With the following result on host1.example.com:
[control]
host2.example.com
host3.example.com
[compute]
host4.example.com some_var=value
host5.example.com
# some comment
[foo:children]
control
compute
[bar:vars]
var1=value
#var2=value
Split the current inventory file into two:
one containing the operator group only,
one containing the rest.
Copy only the latter to the target system.
You know that you can point to a directory containing multiple files as your inventory, right?
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
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.