How to run Ansible without hosts file - shell

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

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"

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.

Ansible - List available hosts

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.

how to ping each ip in a file?

I have a file named "ips" containing all ips I need to ping. In order to ping those IPs, I use the following code:
cat ips|xargs ping -c 2
but the console show me the usage of ping, I don't know how to do it correctly. I'm using Mac os
You need to use the option -n1 with xargs to pass one IP at time as ping doesn't support multiple IPs:
$ cat ips | xargs -n1 ping -c 2
Demo:
$ cat ips
127.0.0.1
google.com
bbc.co.uk
$ cat ips | xargs echo ping -c 2
ping -c 2 127.0.0.1 google.com bbc.co.uk
$ cat ips | xargs -n1 echo ping -c 2
ping -c 2 127.0.0.1
ping -c 2 google.com
ping -c 2 bbc.co.uk
# Drop the UUOC and redirect the input
$ xargs -n1 echo ping -c 2 < ips
ping -c 2 127.0.0.1
ping -c 2 google.com
ping -c 2 bbc.co.uk
With ip or hostname in each line of ips file:
( while read ip; do ping -c 2 $ip; done ) < ips
You can also change timeout, with -W flag, so if some hosts isn'up, it wont lock your script for too much time. Also -q for quiet output is useful in this case.
( while read ip; do ping -c1 -W1 -q $ip; done ) < ips
If the file is 1 ip per line (and it's not overly large), you can do it with a for loop:
for ip in $(cat ips); do
ping -c 2 $ip;
done
You could use fping. It also does that in parallel and has script friendly output.
$ cat ips | xargs fping -q -C 3
10.xx.xx.xx : 201.39 203.62 200.77
10.xx.xx.xx : 288.10 287.25 288.02
10.xx.xx.xx : 187.62 187.86 188.69
...
With GNU Parallel you would do:
parallel -j0 ping -c 2 {} :::: ips
This will run as many jobs in parallel as you have ips or processes.
It also makes sure the output from different jobs are not mixed together, so if you use the output you are guaranteed that you will not get half-a-line from two different jobs.
GNU Parallel is a general parallelizer and makes is easy to run jobs in parallel on the same machine or on multiple machines you have ssh access to.
If you have 32 different jobs you want to run on 4 CPUs, a straight forward way to parallelize is to run 8 jobs on each CPU:
GNU Parallel instead spawns a new process when one finishes - keeping the CPUs active and thus saving time:
Installation
If GNU Parallel is not packaged for your distribution, you can do a personal installation, which does not require root access. It can be done in 10 seconds by doing this:
(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash
For other installation options see http://git.savannah.gnu.org/cgit/parallel.git/tree/README
Learn more
See more examples: http://www.gnu.org/software/parallel/man.html
Watch the intro videos: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Walk through the tutorial: http://www.gnu.org/software/parallel/parallel_tutorial.html
Sign up for the email list to get support: https://lists.gnu.org/mailman/listinfo/parallel
Try doing this :
cat ips | xargs -i% ping -c 2 %
As suggested by #Lupus you can use "fping", but the output is not human friendly - it will scroll out of your screen in few seconds laving you with no trace as of what is going on. To address this I've just released ping-xray. I tried to make it as visual as possible under ascii terminal plus it creates CSV logs with exact millisecond resolution for all targets.
https://dimon.ca/ping-xray/
Hope you'll find it helpful.

Resources