Table of Contents
We have already seen a handful of ansible ad hoc commands so far in our Ansible Tutorials, but to recap, they are single commands you can run with Ansible, making use of Ansible modules without the need to create or save playbooks. They are very useful for performing quick, one-off tasks on a number of remote machines or for testing and understanding the behavior of the Ansible modules that you intend to use in your playbooks. They are both a great learning tool and a quick and dirty (because you never document your work with a playbook!) automation solution.
Understanding Ansible ad hoc commands
- Ad hoc commands are used in Ansible to perform tasks or operations that are needed on an immediate basis, or only once, based upon the requirement.
- In other words, these are tasks that a user wants to be performed on the fly but doesn't want to be saved for later use.
- Ansible modules can be called using
ansible
command - Use "
ansible-doc -l
" for a list of modules and "ansible-doc <modulename>
" for information about module options - Specify which module to use, using
-m
- The "
command
" module is default, and does not have to be specified
/etc/ansible/hosts
where I have added the hostnames of my managed nodes. Since we are using default inventory file so I won't specify the location of the inventory file in the ansible ad hoc commands examples. You can learn more about inventory when we reach Ansible Inventory chapter where we will learn about static and dynamic inventory files.Syntax:
ansible [-i INVENTORY] [server] [-m MODULE] {-a MODULE_OPTIONS}
Ansible ad hoc commands syntax and examples
Let's execute some ad hoc commands using ansible on both of our managed hosts. In this example we will collect the uptime of our managed hosts
[ansible@controller ~]$ ansible all -m shell -a uptime
server1 | CHANGED | rc=0 >>
15:36:00 up 53 min, 4 users, load average: 0.00, 0.00, 0.00
server2 | CHANGED | rc=0 >>
15:36:00 up 53 min, 3 users, load average: 0.00, 0.00, 0.00
You will see that Ansible faithfully logs in to each machine in turn and runs the uptime
command, returning the current uptime output:
Similarly, we can execute any other command
[ansible@controller ~]$ ansible all -m shell -a "free -m"
server2 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 815 176 248 12 391 497
Swap: 0 0 0
server1 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 815 181 243 12 391 492
Swap: 0 0 0
How ansible ad hoc commands works with modules
- In the previous section we executed certain ansible ad hoc commands, but what happens in the backend?
- Ansible gets the list of hosts from the
inventory
file. Since we have not provided any inventory file, ansible will collect the list of hosts from default location i.e./etc/ansible/hosts
- Using SSH then
ansible
will connect to individual managed hosts in parallel - Next the module what you are passing with the command will be pushed to your remote nodes. If module name is not provided with
-m
then by default "command
" is considered as the module. - This respective
module
is used to execute the task on the managed host - After the execution these modules will be removed from the managed host.
Let's verify this:
Delete any ~/.ansible
directory from the managed hosts (this is created every time ansible executes a task on the managed host). I will verify the behaviour on server2
[ansible@server-2 ~]$ rm -rf ~/.ansible
The existing content of our home directory for ansible
user:
[ansible@server-2 ~]$ ls -al total 44 drwx------ 3 ansible ansible 4096 Sep 19 16:41 . drwxr-xr-x. 12 root root 20480 Apr 3 23:00 .. -rw------- 1 ansible ansible 37 Sep 19 16:18 .bash_history -rw-r--r-- 1 ansible ansible 18 May 11 2019 .bash_logout -rw-r--r-- 1 ansible ansible 141 May 11 2019 .bash_profile -rw-r--r-- 1 ansible ansible 312 May 11 2019 .bashrc drwx------ 2 ansible ansible 4096 Jan 29 2020 .ssh
Now we will execute an ansible ad hoc command from the controller to this server2
[ansible@controller ~]$ ansible server2 -m shell -a "free -m"
server2 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 815 176 248 12 391 497
Swap: 0 0 0
After the execution you can verify that ~/.ansible
directory is created on server2
[ansible@server-2 ~]$ tree ~/.ansible/
/home/ansible/.ansible/
└── tmp
1 directory, 0 files
But there are no files in this directory. That's because the "shell
" module was copied to server2
, executed the task and then ansible removed the module.
To retain the module on the managed host you can enable this configuration for the respective command by setting ANSIBLE_KEEP_REMOTE_FILES=1
. In this example we are re-running the same command:
[ansible@controller ~]$ ANSIBLE_KEEP_REMOTE_FILES=1 ansible server2 -m shell -a "free -m"
server2 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 815 176 248 12 391 497
Swap: 0 0 0
Now let's verify the .ansible
directory on the server2
[ansible@server-2 ~]$ tree ~/.ansible/
/home/ansible/.ansible/
└── tmp
└── ansible-tmp-1600514171.7996144-28879-119129181135603
└── AnsiballZ_command.py
2 directories, 1 file
This time the module was retained and not deleted. So now you should be familiar of how a module is used to execute the tasks on managed hosts.
Control the number of hosts for parallel execution (forks)
- We have already covered this part in "ansible configuration" section but let's discuss this with some examples.
- In
ansible.cfg
we have an option to define the number of parallel tasks executed to the client hosts using "forks
". - The default value of
forks
is 5 but we can increase it based on the available system resources and network bandwidth. - For demonstration purpose you can execute a command with default forks value i.e. 5 on 5 servers and you will observe that ansible executes and prints the output of execution from all 5 hosts at the same time
- You can decrease/increase this value in your
ansible.cfg
or use-f <val>
and accordingly ansible will execute remote tasks. - In my case as I have only two managed hosts, In this ansible ad hoc commands example I am executing ansible with
-f 1
[ansible@controller ~]$ ansible all -m shell -a "free -m" -f 1
server1 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 815 181 242 12 391 492
Swap: 0 0 0
server2 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 815 176 248 12 391 497
Swap: 0 0 0
which was executed on server1
first followed by server2
(obviously I can't show you via snippet).
Transfer file from Ansible Engine to Managed Nodes
In this section we will copy a file from our Ansible Engine to the Managed Nodes using copy module with ansible ad hoc commands.
The syntax to transfer file from ansible engine to managed nodes would be:
ansible [-i INVENTORY] [server] -m copy -a "src=/source/file/path dest=/dest/path"
I will create an empty file for the demonstration:
[ansible@controller ~]$ touch /tmp/ctrl_file
Next, we will use copy module for the transfer this file to server2
:
[ansible@controller ~]$ ansible server2 -m copy -a "src=/tmp/ctrl_file dest=/home/ansible/"
Verify the file on server2
[ansible@server-2 ~]$ ls -l
total 0
-rw-rw-r-- 1 ansible ansible 0 Sep 19 19:10 ctrl_file
In this example we will append some text into the file we just coped to server2
. To add some data we use "copy
" module with "content=<TEXT>
". Here replace TEXT
with the data you want to be appended along with "dest=
"
[ansible@controller ~]$ ansible server2 -m copy -a "content='Hello, My name is deepak' dest=~/ctrl_file"
Verify the content on server
[ansible@server-2 ~]$ cat ctrl_file
Hello, My name is Deepak
To get the list of all supported options with copy module
Download file from managed nodes to controller node
In the earlier section we transferred file from the Ansible engine to the managed nodes, now in this section we will perform the opposite. So, we will download files from managed nodes to the ansible engine node.
Instead of "copy
" we will use the "fetch
" module here, below is the syntax:
ansible [-i INVENTORY] [server] -m fetch -a "src=/source/file/path dest=/dest/path"
I will create one dummy file on server2
[ansible@server-2 ~]$ touch /tmp/demo.txt
Now we will fetch this file on our ansible engine under ansible user's home directory
[ansible@controller ~]$ ansible server2 -m fetch -a "src=/tmp/demo.txt dest=~/"
Output from this command
If you observe the output from this command
"changed": true
"dest": "/home/ansible/server2/tmp/demo.txt",
Now the process of fetch is little different from the copy module. If you check the home folder of ansible user, you will now find a new directory "server2"
[ansible@controller ~]$ ls -al
total 64
drwx------ 5 ansible ansible 4096 Sep 20 10:26 .
drwxr-xr-x. 15 root root 20480 Aug 30 15:40 ..
drwx------ 4 ansible ansible 4096 Sep 19 16:10 .ansible
-rw------- 1 ansible ansible 145 Sep 19 13:30 .bash_history
-rw-r--r-- 1 ansible ansible 18 May 11 2019 .bash_logout
-rw-r--r-- 1 ansible ansible 141 May 11 2019 .bash_profile
-rw-r--r-- 1 ansible ansible 312 May 11 2019 .bashrc
drwxrwxr-x 3 ansible ansible 4096 Sep 20 10:26 server2
drwx------ 2 ansible ansible 4096 Sep 19 14:58 .ssh
-rw------- 1 ansible ansible 10318 Sep 20 10:25 .viminfo
This directory was created with the fetch operation. You can check the directory structure of this directory:
[ansible@controller ~]$ tree server2/
server2/
└── tmp
└── demo.txt
1 directory, 1 file
So, the fetch
module has copied the file in the same path tmp/demo.txt
under server2
directory. Now I am sure most users wouldn't want this entire directory structure to be downloaded when all we want is just a file.
To overcome this, we will use "flat=yes
" which will only fetch
the file instead of all the director structure.
Let's try the same command by adding "flat=yes
"
[ansible@controller ~]$ ansible server2 -m fetch -a "src=/tmp/demo.txt dest=~/ flat=yes"
Output from this command
Verify if the file was downloaded
[ansible@controller ~]$ ls -l demo.txt
-rw-rw-r-- 1 ansible ansible 0 Sep 20 10:36 demo.txt
Copy files locally on the remote server (managed node) using ansible ad hoc commands
In this section I will share the ansible ad hoc commands examples to create a file or directory on the managed nodes i.e. the source and destination, both will be from the same managed node. This can help you take backup of any configuration file before performing any configuration changes. We will use the same copy module and it's syntax, additionally we need "remote_src=true
" to be able to work locally on the managed node.
In this example I will perform operation on server1 where I will copy ~/demo.txt
to /tmp/demo-2.txt
on the same server.
[ansible@controller ~]$ ansible server1 -m copy -a "src=~/demo.txt dest=/tmp/demo-2.txt remote_src=yes" server1 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/libexec/platform-python" }, "changed": true, "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "dest": "/tmp/demo-2.txt", "gid": 1000, "group": "ansible", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "mode": "0664", "owner": "ansible", "size": 0, "src": "/home/ansible/demo.txt", "state": "file", "uid": 1000 }
So the command seems to be successful, we can verify our file on server1
[ansible@controller ~]$ ssh server1 "ls -l /tmp/demo-2.txt"
-rw-rw-r-- 1 ansible ansible 0 Sep 27 10:09 /tmp/demo-2.txt
Create or Remove file and directory using ansible ad hoc commands
In this section I will share the ansible ad hoc commands examples to create a file or directory on the managed nodes i.e. the source and destination, both will be from the same managed node. This can help you take backup of any configuration file before performing any configuration changes. We will use the same copy module and it's syntax, additionally we need "remote_src=true
"
Create a file on managed nodes
We will use the following syntax to create a new file:
ansible [-i INVENTORY] [server] -m file -a "path=</path/to/file.txt state=touch>"
Now we will use this syntax to create a new file /tmp/demo_1.txt
on server2
using ansible engine.
[ansible@controller ~]$ ansible server2 -m file -a "path=/tmp/demo_1.txt state=touch"
Output from my controller
node:
The command has executed successfully. We can see a couple of default values which has been applied to the new file such as gid
, group
, mode
, owner
etc. We also have the flexibility to define a mode
for the new file which we create.
Create a file with custom arguments
In this example I have also defined a mode value i.e. 755 for this new file:
[ansible@controller ~]$ ansible server2 -m file -a "path=/tmp/demo_2.txt state=touch mode=755"
Output from my controller
node:
Now the file is created with mode 0755
.
Remove a file from the managed nodes using ansible ad hoc commands
To remove a file we will use "absent
" module with following ansible ad hoc commands syntax:
ansible [-i INVENTORY] [server] -m file -a "path=</path/to/file.txt state=absent>"
So instead of touch
we will use absent
and the provided file with path=
will be deleted. Let's use this syntax to delete one of the files we created earlier:
[ansible@controller ~]$ ansible server2 -m file -a "path=/tmp/demo_2.txt state=absent"
Output from my controller node:
Create a directory on managed nodes
We can use the file module to also create a new directory but with a slightly different syntax as follows:
ansible [-i INVENTORY] [server] -m file -a "path=</path/to/dir state=directory>"
Here instead of touch
, now we are using state=directory
to create a directory on the managed nodes. Let's use this in an example:
[ansible@controller ~]$ ansible server2 -m file -a "path=/tmp/dir_1.txt state=directory"
Output from my controller
node:
So the directory is created on server2
[ansible@server-2 ~]$ ls -l /tmp/dir_1.txt/
total 0
By default, umask
value from server2
is considered to create the directory but again you can modify the modes
and permission
while creating the directory as we did for the file earlier.
Remove a directory
We will again use the absent
as the state with file module to delete the directory with following syntax:
ansible [-i INVENTORY] [server] -m file -a "path=</path/to/dir state=absent>"
Let's use this syntax to delete dir_1
which we just created in the above example:
[ansible@controller ~]$ ansible server2 -m file -a "path=/tmp/dir_1.txt state=absent"
Output from my controller
node::
Now verify if the directory was deleted from server2
[ansible@server-2 ~]$ ls -l /tmp/dir_1.txt/
ls: cannot access '/tmp/dir_1.txt/': No such file or directory
Execute commands with root privileges using ansible ad hoc commands
In all the examples we learned till now was performed as ansible user. But what if you try to execute a command which requires root level privilege. Let's try to check the available partitions and storage devices using "fdisk -l
" command which can only be executed as root
user:
[ansible@controller ~]$ ansible server2 -m command -a "fdisk -l"
server2 | CHANGED | rc=0 >>
fdisk: cannot open /dev/xvda: Permission denied
We have got permission denied error as "fdisk -l
" command can be executed only as root
user. In such case we must use -b
or --become
to execute the command with sudo privilege. If you remember during ansible configuration stage we had given full sudo privilege to ansible
user.
Let's try to execute the same command with --become
[ansible@controller ~]$ ansible server2 -m command -a "fdisk -l" --become server2 | CHANGED | rc=0 >> Disk /dev/xvda: 10 GiB, 10737418240 bytes, 20971520 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0xa323d5eb Device Boot Start End Sectors Size Id Type /dev/xvda1 2048 4095 2048 1M 83 Linux /dev/xvda2 * 4096 20971486 20967391 10G 83 Linux
This time the command has executed successfully. If you have configured your sudo to prompt for password then the command would again fail asking for sudo password as by default --become
will not prompt for any password. In such case you can use --ask-become-pass
along with --become
to prompt for the sudo password of root user.
[ansible@controller ~]$ ansible server2 -m command -a "fdisk -l" --become --ask-become-pass BECOME password: server2 | CHANGED | rc=0 >> Disk /dev/xvda: 10 GiB, 10737418240 bytes, 20971520 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0xa323d5eb Device Boot Start End Sectors Size Id Type /dev/xvda1 2048 4095 2048 1M 83 Linux /dev/xvda2 * 4096 20971486 20967391 10G 83 Linux
In this command, ansible prompted for BECOME password
and if the password was correct, the command was executed.
You can use --become
to perform any root level task with ansible ad hoc commands.
Working with packages using yum module in ansible ad hoc commands
Since we are using RHEL/CentOS 8 environment, we will use yum
module as my module for working with rpms but if you are on Ubuntu/Debian then you should use "apt
" as your module.
The syntax to install a package on managed nodes:
ansible [-i INVENTORY] [server] -m yum -a "name=<pkg> state=<present/latest/absent>"
Let's use this syntax to install git rpm on server2
[ansible@controller ~]$ ansible server2 -m yum -a "name=git state=latest"
server2 | FAILED! => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"msg": "This command has to be run under the root user.",
"results": []
}
The command has failed as we are trying to install the rpm as ansible user while this can be performed only with root privileges. So we must use --become
with this command:
[ansible@controller ~]$ ansible server2 -m yum -a "name=git state=latest" --become
server2 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"msg": "",
"rc": 0,
"results": [
"Installed: perl-libnet-3.11-3.el8.noarch",
"Installed: perl-Digest-1.17-395.el8.noarch",
"Installed: perl-Mozilla-CA-20160104-7.el8.noarch",
"Installed: perl-Digest-MD5-2.55-396.el8.x86_64",
"Installed: perl-Net-SSLeay-1.88-1.el8.x86_64",
"Installed: perl-Error-1:0.17025-2.el8.noarch",
"Installed: perl-Data-Dumper-2.167-399.el8.x86_64",
"Installed: perl-Encode-4:2.97-3.el8.x86_64",
"Installed: perl-Getopt-Long-1:2.50-4.el8.noarch",
"Installed: perl-HTTP-Tiny-0.074-1.el8.noarch",
"Installed: perl-MIME-Base64-3.15-396.el8.x86_64",
"Installed: perl-Git-2.18.4-2.el8_2.noarch",
"Installed: perl-TermReadKey-2.37-7.el8.x86_64",
"Installed: perl-Pod-Escapes-1:1.07-395.el8.noarch",
"Installed: perl-Pod-Perldoc-3.28-396.el8.noarch",
"Installed: perl-Pod-Simple-1:3.35-395.el8.noarch",
"Installed: perl-Pod-Usage-4:1.69-395.el8.noarch",
"Installed: perl-Storable-1:3.11-3.el8.x86_64",
"Installed: perl-Term-ANSIColor-4.06-396.el8.noarch",
"Installed: perl-Term-Cap-1.17-395.el8.noarch",
"Installed: perl-IO-Socket-IP-0.39-5.el8.noarch",
"Installed: perl-Text-ParseWords-3.30-395.el8.noarch",
"Installed: perl-IO-Socket-SSL-2.066-4.el8.noarch",
"Installed: perl-podlators-4.11-1.el8.noarch",
"Installed: perl-URI-1.73-3.el8.noarch",
"Installed: git-2.18.4-2.el8_2.x86_64",
"Installed: git-core-2.18.4-2.el8_2.x86_64",
"Installed: git-core-doc-2.18.4-2.el8_2.noarch"
]
}
This time the command execution was success and git was installed along with all other dependencies. Verify the same on server2
:
[ansible@server-2 ~]$ rpm -q git
git-2.18.4-2.el8_2.x86_64
To install a package with specific version you can use state=present
while to remove a rpm you can use state=absent
Execute ansible ad hoc commands as different user
In this section we will learn to execute ansible ad hoc commands as a different user. By default when you execute commands with ansible
, it uses the same username using which you call the ansible
command. In our case since we are logged in as ansible
and executing all the ansible ad hoc commands, so by default ansible user will be used for SSH connection.
If you have a requirement to connect to the managed node and execute command as a different user then we must use -u
or --user
along with ansible
command.
I will create a new user "deepak
" on server3
and assign a password:
[root@server3 ~]# useradd deepak [root@server3 ~]# passwd deepak
Now we have not configured passphrase based login for user "deepak
" so we will use password to connect to server3
as deepak
user. We will learn more about password based login in "Working with Managed Nodes using Password (not Passphrase)" chapter
If we execute the ansible command with "whoami
" argument, this should give us the username of the user using which the command was executed on server3
[ansible@controller ~]$ ansible server3 -m command -a "whoami" --ask-pass
SSH password:
server3 | CHANGED | rc=0 >>
ansible
So here "ansible
" user was used to execute whoami
.
Similarly, now I will use --user deepak
and execute the same command:
[ansible@controller ~]$ ansible server3 -m command -a "whoami" --user deepak --ask-pass SSH password: server3 | CHANGED | rc=0 >> deepak
Now you can see the command was executed using "deepak
" user. So now you can use this syntax across different ansible ad hoc commands and perform your task as different user.
What’s Next
Next in our Ansible Tutorial we will learn about different types of inventory files and how to use them.
I’m sorry for my oversight. No equal sign in src
I couldn’t find the line you are highlighting, can you please share the exact command which is incorrect
I sent two comments. The first was about the problem and the second was about the solution. Only the second is displayed
There was a single comment for moderation, I am afraid I still can’t find any example with copy module where “=” (equal to) sign is missing.
I am sending you again where the problem occurred
ERROR! this task ‘copy’ has extra params, which is only allowed in the following modules: add_host, win_shell, include_vars, set_fact, include_tasks, include, shell, group_by, command, raw, script, import_role, win_command, import_tasks, include_role, meta
I have not used
cp_file1.txt
in any of my examples. Although I did usedsrc=~/demo.txt dest=/tmp/demo-2.txt
but it has=
sign. Am I missing anything?Sorry, this is my example. The file names are from my lab
Hii, Thank you somuch for this tutorial, i wont more stuff from you
Excellent example please keep it up …