10+ practical examples with Ansible ad-hoc commands

Overview

  • Ad hoc commands in Ansible are used to perform tasks or operations that are needed on an ad hoc 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
NOTE:

In this section I will be using the default inventory file i.e. /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 ad-hoc commands. 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}

 

Sample ad-hoc command 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 works with modules

  • In the previous section we executed certain 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 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, 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 command.

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
10+ practical examples with Ansible ad-hoc commands

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
10+ practical examples with Ansible ad-hoc commands

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)

In this section I will share the ad-hoc commands 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

In this section I will share the ad-hoc commands 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:

10+ practical examples with Ansible ad-hoc commands

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:

10+ practical examples with Ansible ad-hoc commands

Now the file is created with mode 0755.

 

Remove a file from the managed nodes

To remove a file we will use "absent" module with following 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:

10+ practical examples with Ansible ad-hoc commands

 

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:

10+ practical examples with Ansible ad-hoc commands

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:10+ practical examples with Ansible ad-hoc commands:

 

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

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

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 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 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 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.

7 thoughts on “10+ practical examples with Ansible ad-hoc commands”

    • 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.

      Reply
  1. I am sending you again where the problem occurred

    ansible all -m copy -a "src ~/cp_file1.txt dest=/home/ansible"

    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

    Reply

Leave a Comment

Please use shortcodes <pre class=comments>your code</pre> for syntax highlighting when adding code.