specify subtree of hosts in ansible using --limit - ansible

I would like to apply playbook to a subset of hosts. To all dbservers hosts under 'atlanta', not under 'raleigh
but I cannot figure out how to use --limit to point to a specific subtree of hosts in the inventory
ansible all -i ./testhosts.yml --limit atlanta --list-hosts
hosts (4):
host1-atlanta
host2-atlanta
host1-raleigh
host2-raleigh
ansible all -i ./testhosts.yml --limit 'atlanta:&dbservers' --list-hosts
hosts (4):
host1-atlanta
host2-atlanta
host1-raleigh
host2-raleigh
what I am hoping to get is
host1-atlanta
host2-atlanta
(in my case the 'logical' path to the subtree is: all:usa:southeast:atlanta:dbservers)
My example testhosts.yml
all:
children:
usa:
children:
southeast:
children:
atlanta:
children:
dbservers:
hosts:
host1-atlanta:
ansible_port: 11022
ansible_host: 11.0.1.2
host2-atlanta:
ansible_port: 11022
ansible_host: 11.0.1.3
raleigh:
children:
dbservers:
hosts:
host1-raleigh:
ansible_port: 11022
ansible_host: 10.0.1.2
host2-raleigh:
ansible_port: 11022
ansible_host: 10.0.1.3
northeast:
northwest:
southwest:
is it possible to get the --limit command line option to filter out everything, except a particular subtree ?
My higher level ansible invocation scripts already know what site they will operate on, so I was hoping I could somehow have a common hosts file but be able to specify groups using some sort of a tree-navigation 'selector'.
I realize that I could do
ansible atlanta -i ./testhosts.yml --limit atlanta --list-hosts
but the problem I am running into with that, is that this type of 'selector' is not available when I use ansible-playbook.
And the only selector (filter) that I could find was --limit, and that does not seem to work for me.

Despite appearances, groups in ansible are not hierarchical. You have a single dbservers group, and members of that group also belong to both atlanta and raleigh. With a small change in your inventory you can get closer to what you want:
all:
children:
dbservers:
children:
dbservers-atl:
dbservers-ral:
usa:
children:
southeast:
children:
atlanta:
children:
dbservers-atl:
hosts:
host1-atlanta:
ansible_port: 11022
ansible_host: 11.0.1.2
host2-atlanta:
ansible_port: 11022
ansible_host: 11.0.1.3
raleigh:
children:
dbservers-ral:
hosts:
host1-raleigh:
ansible_port: 11022
ansible_host: 10.0.1.2
host2-raleigh:
ansible_port: 11022
ansible_host: 10.0.1.3
northeast:
northwest:
southwest:
With this inventory, each region has a seperate dbservers-<tag> group. There is a global dbservers group outside of your region hierarchy that includes all dbservers-<tag> groups. With this, you can ask for:
ansible all --list-hosts --limit dbservers-atl
And get:
hosts (2):
host1-atlanta
host2-atlanta
Or you can ask for the dbservers group:
ansible all --list-hosts --limit dbservers
And get:
hosts (4):
host1-raleigh
host2-raleigh
host1-atlanta
host2-atlanta

Your statement about the '--limit' option is not correct:
... this type of 'selector' is not available when I use ansible-playbook.
Quoting from 'man ansible', or 'man ansible-playbook':
-l 'SUBSET', --limit 'SUBSET'
further limit selected hosts to an additional pattern
Details are available in Working with Patterns. For example an equivalent of running ansible
ansible all --limit '!dbservers' ...
is declaring 'hosts' in a playbook.yml
- hosts: all:!dbservers
and running
ansible-playbook playbook.yml
Next equivalent is declaring all 'hosts' in the playbook.yml
- hosts: all
and running
ansible-playbook --limit '!dbservers' playbook.yml

Related

ansible-pull playing playbook on remote host : how to point to the localhost in inventory? (Could not match supplied host pattern)

Im trying to use ansible-pull locally on a remote host where ansible retrieve a playbook on a git repo with an inventory.
I dont know how to point to the remote host in the inventory and in group vars. I have tried with the machine_name (user#machine_name in bash) but it tells me
[WARNING]: Could not match supplied host pattern, ignoring:
this is my inventory :
all:
children:
webserver:
hosts:
ip-172-31-21-218: #this is the machine name
dbserver:
hosts:
node2:
ansible_host: 34.201.53.127
I want the playbook to use the host machine to play the playbook locally with ansible pull but using the inventory to understand the inventory machine name matches the host machine name...
this is sample from official documentation and keep reading the comments :)
all:
hosts: # you ignore this line
mail.example.com: # and this line also
children:
webservers:
hosts:
foo.example.com:
bar.example.com:
dbservers:
hosts:
one.example.com:
two.example.com:
three.example.com:
Regards.

Ansible hosts file parsing not working as expected

I am having a strange issue. I have a 'hosts' file with multiple groups in it.
For some reason, when I am using 'ansible-playbook playbook.yml -l GROUPNAME', ansible applies the playbook to all hosts in the 'hosts' file.
hosts file
all:
children:
GROUP1:
children:
webservers:
hosts:
hostname1:
sqlservers:
hosts:
hostname2:
GROUP2:
children:
webservers:
hosts:
hostname3:
hostname4:
GROUP3:
children:
webservers:
hosts:
hostname5:
hostname6:
sqlservers:
hosts:
hostname7:
ansible log:
Positional arguments: playbook.yml
verbosity: 4
connection: smart
timeout: 10
become_method: sudo
tags: ('all',)
inventory: ('hostsfile',)
subset: GROUP1
forks: 5
1 plays in playbook.yml
In the 'playbook.yml' file I do have 'hosts: all' but in a different job I don't have this issue.
Ansible does notice the Subset I indicated but is not filtering it, instead it is running the playbook successfully against all hosts.
I combed through the web for the entire day yesterday and could not find the problem.
Any help will be greatly appreciated.
I think I found the solution to this problem on my own.
By default, Ansible is parsing the hosts file using a set of inventory plugins until it will succeed.
By specifying the plugin in 'ansible.cfg':
[inventory]
enable_plugins = yaml
The problem was solved, I am not sure why.
https://docs.ansible.com/ansible/latest/plugins/inventory.html

Dynamic File names in vars_files with inventory variables

Following is simple playbook
- name: Create VM and associated resources
hosts: linux
connection: local
vars_files:
- vars_files/{{ env_name }}_vars.yml
- vars_files/base_vars.yml
roles:
- linux
And my inventory File is TEST.yml
all:
vars:
env_name: TEST
linux:
hosts:
TEST-SERVER:
ansible_host: 10.10.10.10
When I run the playbook ansible-playbook -vvv plabook_test.yml, I am receiving the following error.
skipping vars_file 'vars_files/{{ env_name }}_vars.yml' due to an undefined variable
Any Idea how can I used a variable from my inventory in the file name?
Any help is greatly appreciated.
Thanks,
The inventory file is wrong because of the key "host"
all:
vars:
env_name: TEST
linux:
host:
TEST-SERVER:
ansible_host: 10.10.10.10
You should have seen a warning
[WARNING]: Skipping unexpected key (host) in group (linux), only "vars", "children" and "hosts" are valid
Fix the inventory
all:
vars:
env_name: TEST
linux:
hosts:
TEST-SERVER:
ansible_host: 10.10.10.10

Hosts are not being picked from the Inventory file

I'm trying to perform tasks in the playbook for the hosts mentioned in my inventory file which are grouped under "Jira" But for some reason my group is not being identified to pick. for the content of the files please look below.
How can I run all the tasks mentioned in the playbook with all the hosts in the inventory?
I have an Inventory file with the below contents: Hosts.yml
all: # the all group contains all hosts
hosts:
ansible:
ansible_host: #{ansible-controller}
ansible_user: root
crowd:
ansible_host: #{crowd}
ansible_user: root
jira:
ansible_host1: 53.31.54.56
ansible_host2: 53.31.54.55
I have playbook with content:
---
- name: Install Jira Application
hosts: jira
gather_facts: true
become: true
remote_user: root
roles:
- ansible-preparation
#- jira-applicationsetup
I always get below error message:
root#sedcagse0550:/usr/Anil/InfraAutomation/gsep-infrastructure-automation : ansible-playbook jira-fullinstall.yml
[WARNING]: Could not match supplied host pattern, ignoring: jira
PLAY [Install Jira Application] *************************************************************
skipping: no hosts matched
PLAY RECAP **********************************************************************************
How can I perform all the tasks to all the hosts mentioned in the inventory file?
You should run the ansible-playbook with inventory parameter(-i) like this:
ansible-playbook -i Hosts.yml jira-fullinstall.yml
Otherwise, ansible checks the default inventory file location which is "/etc/ansible/hosts".
If your Hosts.yml already in that location, check your inventory file. In Ansible 2.7 User Guide YAML inventory files look like this:
all:
jira:
hosts:
53.31.54.56:
53.31.54.55:
If i understood it correct, Your inventory file should look like as below::
ansible: # Group Name
hosts:
ansible_host: #Host name
ansible_user: root # Host Variable
crowd:
hosts:
ansible_host: #{crowd}
ansible_user: root
jira:
hosts:
ansible_host1:
ansible_host: 53.31.54.56
ansible_host2:
ansible_host: 53.31.54.55`
Please refer this link for detailed formatting about yaml based inventory

Override hosts variable of Ansible playbook from the command line

This is a fragment of a playbook that I'm using (server.yml):
- name: Determine Remote User
hosts: web
gather_facts: false
roles:
- { role: remote-user, tags: [remote-user, always] }
My hosts file has different groups of servers, e.g.
[web]
x.x.x.x
[droplets]
x.x.x.x
Now I want to execute ansible-playbook -i hosts/<env> server.yml and override hosts: web from server.yml to run this playbook for [droplets].
Can I just override as a one time off thing, without editing server.yml directly?
Thanks.
I don't think Ansible provides this feature, which it should. Here's something that you can do:
hosts: "{{ variable_host | default('web') }}"
and you can pass variable_host from either command-line or from a vars file, e.g.:
ansible-playbook server.yml --extra-vars "variable_host=newtarget(s)"
For anyone who might come looking for the solution.
Play Book
- hosts: '{{ host }}'
tasks:
- debug: msg="Host is {{ ansible_fqdn }}"
Inventory
[web]
x.x.x.x
[droplets]
x.x.x.x
Command: ansible-playbook deplyment.yml -i hosts --extra-vars "host=droplets"
So you can specify the group name in the extra-vars
We use a simple fail task to force the user to specify the Ansible limit option, so that we don't execute on all hosts by default/accident.
The easiest way I found is this:
---
- name: Force limit
# 'all' is okay here, because the fail task will force the user to specify a limit on the command line, using -l or --limit
hosts: 'all'
tasks:
- name: checking limit arg
fail:
msg: "you must use -l or --limit - when you really want to use all hosts, use -l 'all'"
when: ansible_limit is not defined
run_once: true
Now we must use the -l (= --limit option) when we run the playbook, e.g.
ansible-playbook playbook.yml -l www.example.com
Limit option docs:
Limit to one or more hosts This is required when one wants to run a
playbook against a host group, but only against one or more members of
that group.
Limit to one host
ansible-playbook playbooks/PLAYBOOK_NAME.yml --limit "host1"
Limit to multiple hosts
ansible-playbook playbooks/PLAYBOOK_NAME.yml --limit "host1,host2"
Negated limit.
NOTE: Single quotes MUST be used to prevent bash
interpolation.
ansible-playbook playbooks/PLAYBOOK_NAME.yml --limit 'all:!host1'
Limit to host group
ansible-playbook playbooks/PLAYBOOK_NAME.yml --limit 'group1'
This is a bit late, but I think you could use the --limit or -l command to limit the pattern to more specific hosts. (version 2.3.2.0)
You could have
- hosts: all (or group)
tasks:
- some_task
and then ansible-playbook playbook.yml -l some_more_strict_host_or_pattern
and use the --list-hosts flag to see on which hosts this configuration would be applied.
An other solution is to use the special variable ansible_limit which is the contents of the --limit CLI option for the current execution of Ansible.
- hosts: "{{ ansible_limit | default(omit) }}"
If the --limit option is omitted, then Ansible issues a warning, but does nothing since no host matched.
[WARNING]: Could not match supplied host pattern, ignoring: None
PLAY ****************************************************************
skipping: no hosts matched
I'm using another approach that doesn't need any inventory and works with this simple command:
ansible-playbook site.yml -e working_host=myhost
To perform that, you need a playbook with two plays:
first play runs on localhost and add a host (from given variable) in a known group in inmemory inventory
second play runs on this known group
A working example (copy it and runs it with previous command):
- hosts: localhost
connection: local
tasks:
- add_host:
name: "{{ working_host }}"
groups: working_group
changed_when: false
- hosts: working_group
gather_facts: false
tasks:
- debug:
msg: "I'm on {{ ansible_host }}"
I'm using ansible 2.4.3 and 2.3.3
I changed mine to default to no host and have a check to catch it. That way the user or cron is forced to provide a single host or group etc. I like the logic from the comment from #wallydrag. The empty_group contains no hosts in the inventory.
- hosts: "{{ variable_host | default('empty_group') }}"
Then add the check in tasks:
tasks:
- name: Fail script if required variable_host parameter is missing
fail:
msg: "You have to add the --extra-vars='variable_host='"
when: (variable_host is not defined) or (variable_host == "")
Just came across this googling for a solution. Actually, there is one in Ansible 2.5. You can specify your inventory file with --inventory, like this: ansible --inventory configs/hosts --list-hosts all
If you want to run a task that's associated with a host, but on different host, you should try delegate_to.
In your case, you should delegate to your localhost (ansible master) and calling ansible-playbook command
I am using ansible 2.5 (2.5.3 exactly), and it seems that the vars file is loaded before the hosts param is executed. So you can set the host in a vars.yml file and just write hosts: {{ host_var }} in your playbook
For example, in my playbook.yml:
---
- hosts: "{{ host_name }}"
become: yes
vars_files:
- vars/project.yml
tasks:
...
And inside vars/project.yml:
---
# general
host_name: your-fancy-host-name
Here's a cool solution I came up to safely specify hosts via the --limit option. In this example, the play will end if the playbook was executed without any hosts specified via the --limit option.
This was tested on Ansible version 2.7.10
---
- name: Playbook will fail if hosts not specified via --limit option.
# Hosts must be set via limit.
hosts: "{{ play_hosts }}"
connection: local
gather_facts: false
tasks:
- set_fact:
inventory_hosts: []
- set_fact:
inventory_hosts: "{{inventory_hosts + [item]}}"
with_items: "{{hostvars.keys()|list}}"
- meta: end_play
when: "(play_hosts|length) == (inventory_hosts|length)"
- debug:
msg: "About to execute tasks/roles for {{inventory_hostname}}"
This worked for me as I am using Azure devops to deploy an application using CICD pipelines. I had to make this hosts (in yml file) more dynamic so in release pipeline I can add it's value, for example:
--extra-vars "host=$(target_host)"
pipeline_variable
My ansible playbook looks like this
- name: Apply configuration to test nodes
hosts: '{{ host }}'

Resources