Ansible - List available hosts - ansible

I got some hosts in my ansible inventory which the ansible server cannot connect to (there is no pubkey deployed).
How do I list all of them? (List unreachable hosts)
Maybe there is a way to generate an inventory file with all of the hosts?
(the less elegant way is to write a playbook and to copy the command line output, but is there a better way?)

To list them, you can use the ping module, and pipe the output :
ANSIBLE_NOCOWS=1 ansible -m ping all 2>&1 | grep 'FAILED => SSH' | cut -f 1 -d' '
If you want to generate an inventory, you can just redirect the output in a file :
ANSIBLE_NOCOWS=1 ansible -m ping all 2>&1 | grep 'FAILED => SSH' | cut -f 1 -d' ' > hosts_without_key
Then, you can use it later providing the -i switch to ansible commands :
ansible-playbook -i hosts_without_key deploy_keys.yml
If you can ssh using passwords, and assuming you have a key deploying playbook (e.g. deploy_keys.yml), you can issue :
ansible-playbook -i hosts_without_key deploy_keys.yml -kKu someuser
But if the point is to deploy keys on hosts that don't have them, remember Ansible is idempotent. It does no harm to execute the deploy_keys.yml playbook everywhere (it's just a bit longer).
Good luck.

Related

ansible-inventory --list host groups command line

I would like to obtain the list of host group names (only) from ansible-inventory, however I'm having to use grep to trim down the list based on known group name patterns - e.g.
Clean output but messy command line and need to know group name pattern ahead of time:
ansible-inventory -i inventory/production --list --yaml | grep webserver_.*:$
Clean command line and don't need to know group name pattern, but messy output:
ansible-playbook my-playbook.yml -i inventory/production --list-hosts
Is there a clean way to extract just the group names from inventory?
Example hosts.yml:
# NGINX
webserver_1:
hosts:
ws1.public.example.com
webserver_2:
hosts:
ws2.public.example.com
webserver_2:
hosts:
ws2.public.example.com
# EC2 back-ends
backend_ec2_1:
hosts:
be1.internal.example.com
backend_ec2_2:
hosts:
be2.internal.example.com
backend_ec2_3:
hosts:
be3.internal.example.com
[Ansible v2.9.7]
You could use the jq command to parse the json output from ansible-inventory --list, like this:
$ ansible-inventory -i hosts --list | jq .all.children
[
"backend_ec2_1",
"backend_ec2_2",
"backend_ec2_3",
"ungrouped",
"webserver_1",
"webserver_2"
]
Or if you want just bare names:
$ ansible-inventory -i hosts --list | jq -r '.all.children[]'
backend_ec2_1
backend_ec2_2
backend_ec2_3
ungrouped
webserver_1
webserver_2
This command lists the groups defined in the inventory
ansible localhost -m debug -a 'var=groups.keys()' -i inventory/production/

Ansible: How to use inventory variables in ad-hoc commands

I want to run an ad-hoc command over a host file, in the host file I have defined a variable for each host, How can I use that variable while executing an ad-hoc command.
For example:
ansible -i /home/bob/hosts_file -m shell -a "$VAR/project run"
I have defined the $VAR for each host in "hosts_file", the $VAR is different for every host in the inventory file. How can I use that variable in my ad-hoc command replacing for each host when executing.
With Ansible ad-hoc commands ansible can make use of all the same variables regardless.
Examples
#1. group_names
$ ansible -i inventory/lab -m debug -a "var=group_names" all | head -10
es-master-01.mydom.local | SUCCESS => {
"group_names": [
"elasticsearch",
"engineering",
"lab",
"lab-es-master"
]
}
Here I'm querying the servers in an inventory to find out what groups the server is assigned to in the inventory file. This variable group_names shows this from my inventory file.
#2. inventory_hostnames
Here's another example where I'm using the variable inventory_hostnames and accessing it using Jinja notation:
$ ansible -i inventory/nyc1 -l ocp-app* all -c local -m shell -a "echo {{ inventory_hostname }}"
ocp-app-01e.nyc1.dom.us | CHANGED | rc=0 >>
ocp-app-01e.nyc1.dom.us
ocp-app-01c.nyc1.dom.us | CHANGED | rc=0 >>
ocp-app-01c.nyc1.dom.us
ocp-app-01d.nyc1.dom.us | CHANGED | rc=0 >>
ocp-app-01d.nyc1.dom.us
ocp-app-01a.nyc1.dom.us | CHANGED | rc=0 >>
ocp-app-01a.nyc1.dom.us
ocp-app-01b.nyc1.dom.us | CHANGED | rc=0 >>
ocp-app-01b.nyc1.dom.us
ocp-app-01f.nyc1.dom.us | CHANGED | rc=0 >>
ocp-app-01f.nyc1.dom.us
Host variables are available to ansible, even while running an ad-hoc command. You insert them as you would through a playbook, using a jinja template.
ansible all -i /home/bob/hosts_file -m shell -a "{{var}}/project run"

How to run Ansible without hosts file

How to run Ansible without hosts file?
just like:
$ ansible --"Some Options" IP -a 'uptime'
you can do like this:
ansible all -i "<hostname-or-ip>," -a 'uptime'
Note the , at the end of the IP address, or it will be considered a hosts inventory filename.
Here is an example for reference:
ansible all -i "192.168.33.100," -a 'uptime'
192.168.33.100 | SUCCESS | rc=0 >>
12:05:10 up 10 min, 1 user, load average: 0.46, 0.23, 0.08
Hosts can be given to ansible using three ways
Using inventory path in ansible.cfg which is /etc/ansible/host by default
Using hosts file
ansible -i /tmp/hosts -a 'uptime' all
Using hosts ip as comma separated host list. Take care of the comma in the end of the list
ansible -i "192.168.1.16,192.168.1.80:2222," -a 'uptime' all
From ansible --help you can get -i option description
-i INVENTORY, --inventory-file=INVENTORY
specify inventory host path
(default=/etc/ansible/hosts) or comma separated host
list.
If you want run playbook at once or some more and not whole list, you can try with -l|--limit "your.node.local"
ansible-playbook -i inventory.hosts --limit your.node.local user.yml
Both the answers here have most of what you need but in order to SSH to a remote host you need to tell ansible what user to SSH as, especially if it's different on the system you're running ansible from and the remote targets.
For example, I have 4 RPi4 systems with Ubuntu 20.04 on them. To access them with an Ansiible ad-hoc command:
$ ansible -i "k8s-02a,k8s-02b,k8s-02c,pi-vpn," -a uptime all -u ubuntu
pi-vpn | CHANGED | rc=0 >>
12:47:26 up 7:52, 1 user, load average: 0.14, 0.14, 0.10
k8s-02c | CHANGED | rc=0 >>
12:47:27 up 7:58, 1 user, load average: 0.06, 0.10, 0.09
k8s-02a | CHANGED | rc=0 >>
12:47:27 up 7:58, 1 user, load average: 0.43, 0.50, 0.47
k8s-02b | CHANGED | rc=0 >>
12:47:27 up 7:58, 1 user, load average: 0.08, 0.06, 0.04
Here I'm providing a list of my hostnames to -i and also directing ansible to use the username ubuntu.
NOTE: My ansible version:
$ ansible --version | grep ^ans
ansible 2.9.11

Ansible adhoc command in sequence

I want to run ansible adhoc command on a list of EC2 instances. I want ansible to run it in sequence but ansible runs them in random. For example:
13:42:21 #cnayak ansible :► ansible aws -a "hostname"
ec2 | SUCCESS | rc=0 >>
ip-172-31-36-255
ec3 | SUCCESS | rc=0 >>
ip-172-31-45-174
13:42:26 #cnayak ansible :► ansible aws -a "hostname"
ec3 | SUCCESS | rc=0 >>
ip-172-31-45-174
ec2 | SUCCESS | rc=0 >>
ip-172-31-36-255
Any way to make them run in order?
By default ansible runs tasks in parallel. If you want them to be executed serially then you can limit number of workers running at the same time by using "--forks" option.
Adding "--forks 1" to your ansible invocation should run your command sequentially on all hosts (in order defined by inventory).
You can use the forks with adhoc command and serial: 1 inside the playbook.
On adhoc command:
ansible aws -a "hostname" --forks=1
Inside the playbook:
- hosts: aws
become: yes
gather_facts: yes
serial: 1
tasks:
- YOUR TASKS HERE
--forks=1 hasn't been sorting inventory for me in recent versions of ansible (2.7)
Another approach I find useful is using the "oneline" output callback, so I can use the standard sort and grep tools on the output:
ANSIBLE_LOAD_CALLBACK_PLUGINS=1 \
ANSIBLE_STDOUT_CALLBACK=oneline \
ansible \
-m debug -a "msg={{ansible_host}}\t{{inventory_hostname}}" \
| sort \
| grep -oP '"msg": \K"[^"]*"' \
| xargs -n 1 echo -e
This has been useful for quick-n-dirty reports on arbitrary vars or (oneline) shell command outputs.

How to get variables in script to work

I have the following Ansible script.
ansible localhost -m known_hosts -a "path=/home/vagrant/.ssh/known_hosts name=web key=\"{{ lookup('pipe', 'ssh-keyscan ' + item) }}\" state=present with_items={{hosts}}" --user vagrant -e "{hosts:[web, db]}"
Essentially it is meant to add a bunch of known hosts to the known_hosts file. I can't seem to get it to work with the array. Although I have managed to get it working for a single host.
ansible localhost -m known_hosts -a "path=/home/vagrant/.ssh/known_hosts name=web key=\"{{ lookup('pipe', 'ssh-keyscan ' + host) }}\" state=present" --user vagrant -e "host='web'"
Any ideas how I can get it working with the array?
This might not be an answer to your question. More proposing an alternative.
Why are you trying to do it with Ansible? Ansible is a nice tool to get some tasks quickly done on remote hosts but I don't see how you could benefit from it in this situation.
Here's a one-liner that's even shorter than your Ansible command:
for HOST in web db; do if [ ! -n "$(grep "^$HOST " /home/vagrant/.ssh/known_hosts)" ]; then ssh-keyscan $HOST >> /home/vagrant/.ssh/known_hosts 2>/dev/null; fi; done

Resources