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

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"

Related

Getting error when try to list all users on the hosts

I am trying to get all the users I created on the hosts machine. When I run the following command on terminal, I get all the users on the machine.
sudo getent passwd {1000..6000} | cut -d":" -f1
However when I try to run it using ansible, I get an error. I tried all like enquoting in double quotes, escaping the brackets, piping the output to cat etc but nothing is working.
---
- name: "run commands"
become: true
gather_facts: no
hosts: all
tasks:
- name: list all users
shell: getent passwd {1000..6000} | cut -d":" -f1
register: getent
- debug: var=getent.stdout_lines
Note that, per default, Ansible is using /bin/sh, as pointed in the synopsis of the command.
It is almost exactly like the ansible.builtin.command module but runs the command through a shell (/bin/sh) on the remote node.
Source: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/shell_module.html#synopsis
But sh won't interpret a sequence construct like {0..10}.
There are two ways you can overcome this:
Using seq rather:
- shell: getent passwd $(seq 1000 6000) | cut -d":" -f1
register: getent
Specifying to the shell task that you want it executed via bash:
- shell: getent passwd {1000..6000} | cut -d":" -f1
register: getent
args:
executable: /bin/bash

Ansible-playbook run against arbitrary host which is not in inventory file and include group vars

When I run a playbook runrole.yml this way:
ansible-playbook -i '192.168.0.7,' runrole.yml -e "ROLE=allwindows" -e "TARGETIP=192.168.0.7" -e "ansible_port=5986" --ask-vault-pass
runrole.yml has:
- hosts: '{{TARGETIP}}'
roles:
- { role: '{{ROLE}}' }
It works (i.e. it runs against 192.168.0.7), but it fails because I have not provided all additional arguments
ansible_user: Administrator
ansible_password: SecretPasswordGoesHere
ansible_connection: winrm
I would like Ansible to use variables which are defined in group-vars/allwindows.yml.
It will work, If I add into inventory file into a group [allwindows] host 192.168.0.7:
[allwindows]
host1
...
hostN
192.168.0.7
and run using:
ansible-playbook runrole.yml -e "ROLE=allwindows" -e "TARGETIP=192.168.0.7" -e "ansible_port=5986" --ask-vault-pass
It works fine as it detects that 192.168.0.7 belongs to a group allwindows.
In certain scenarios I would like to run a role against a host without touching the inventory file. How do I specify to include group allwindows to use all variables from group_vars/allwindows.yml without modifying the inventory file?
I have found a hack how to do that. It is not nice as #techraf's answer but it works with ansible-playbook
ATARGETIP=192.168.0.7 && echo "[allwindows]" > tmpinventory && echo "$ATARGETIP" >> tmpinventory && ansible-playbook -i tmpinventory runrole.yml -e "ROLE=allwindows" -e "TARGETIP=$ATARGETIP" -e "ansible_port=5986" --ask-vault-pass && rm tmpinventory

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.

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.

Resources