Cheatsheet

How To Manage Multiple Servers with Ansible Ad Hoc Commands

Configuration ManagementAnsible

Introduction

Ansible is a modern configuration management tool that facilitates the task of setting up and maintaining remote servers. With a minimalist design intended to get users up and running quickly, it allows you to control one to hundreds of systems from a central location with either playbooks or ad hoc commands.

Unlike playbooks — which consist of collections of tasks that can be reused — ad hoc commands are tasks that you don’t perform frequently, such as restarting a service or retrieving information about the remote systems that Ansible manages.

In this cheat sheet guide, you’ll learn how to use Ansible ad hoc commands to perform common tasks such as installing packages, copying files, and restarting services on one or more remote servers, from an Ansible control node.

Prerequisites

In order to follow this guide, you’ll need:

  • One Ansible control node. This guide assumes your control node is an Ubuntu 20.04 machine with Ansible installed and configured to connect to your Ansible hosts using SSH keys. Make sure the control node has a regular user with sudo permissions and a firewall enabled, as explained in our Initial Server Setup guide. To set up Ansible, please follow our guide on How to Install and Configure Ansible on Ubuntu 20.04.
  • Two or more Ansible hosts. An Ansible host is any machine that your Ansible control node is configured to automate. This guide assumes your Ansible hosts are remote Ubuntu 20.04 servers. Make sure each Ansible host has:
    • The Ansible control node’s SSH public key added to the authorized_keys of a system user. This user can be either root or a regular user with sudo privileges. To set this up, you can follow Step 2 of How to Set Up SSH Keys on Ubuntu 20.04.
  • An inventory file set up on the Ansible control node. Make sure you have a working inventory file containing all your Ansible hosts. To set this up, please refer to the guide on How To Set Up Ansible Inventories. Then, make sure you’re able to connect to your nodes by running the connection test outlined in the section Testing Connection to Ansible Hosts.

Testing Connection to Ansible Hosts

The following command will test connectivity between your Ansible control node and all your Ansible hosts. This command uses the current system user and its corresponding SSH key as the remote login, and includes the -m option, which tells Ansible to run the ping module. It also features the -i flag, which tells Ansible to ping the hosts listed in the specified inventory file

  • ansible all -i inventory -m ping

If this is the first time you’re connecting to these servers via SSH, you’ll be asked to confirm the authenticity of the hosts you’re connecting to via Ansible. When prompted, type yes and then hit ENTER to confirm.

You should get output similar to this:

Output
server1 | SUCCESS => { "changed": false, "ping": "pong" } server2 | SUCCESS => { "changed": false, "ping": "pong" }

Once you get a "pong" reply back from a host, it means the connection is live and you’re ready to run Ansible commands on that server.

Adjusting Connection Options

By default, Ansible tries to connect to the nodes as a remote user with the same name as your current system user, using its corresponding SSH keypair.

To connect as a different remote user, append the command with the -u flag and the name of the intended user:

  • ansible all -i inventory -m ping -u sammy

If you’re using a custom SSH key to connect to the remote servers, you can provide it at execution time with the --private-key option:

  • ansible all -i inventory -m ping --private-key=~/.ssh/custom_id

Note: For more information on how to connect to nodes, please refer to our How to Use Ansible guide, which demonstrates more connection options.

Once you’re able to connect using the appropriate options, you can adjust your inventory file to automatically set your remote user and private key, in case they are different from the default values assigned by Ansible. Then, you won’t need to provide those parameters in the command line.

The following example inventory file sets up the ansible_user variable only for the server1 server:

~/ansible/inventory
server1 ansible_host=203.0.113.111 ansible_user=sammy
server2 ansible_host=203.0.113.112

Ansible will now use sammy as the default remote user when connecting to the server1 server.

To set up a custom SSH key, include the ansible_ssh_private_key_file variable as follows:

~/ansible/inventory
server1 ansible_host=203.0.113.111 ansible_ssh_private_key_file=/home/sammy/.ssh/custom_id
server2 ansible_host=203.0.113.112

In both cases, we have set up custom values only for server1. If you want to use the same settings for multiple servers, you can use a child group for that:

~/ansible/inventory
[group_a]
203.0.113.111
203.0.113.112

[group_b]
203.0.113.113


[group_a:vars]
ansible_user=sammy
ansible_ssh_private_key_file=/home/sammy/.ssh/custom_id

This example configuration will assign a custom user and SSH key only for connecting to the servers listed in group_a.

Defining Targets for Command Execution

When running ad hoc commands with Ansible, you can target individual hosts, as well as any combination of groups, hosts and subgroups. For instance, this is how you would check connectivity for every host in a group named servers:

  • ansible servers -i inventory -m ping

You can also specify multiple hosts and groups by separating them with colons:

  • ansible server1:server2:dbservers -i inventory -m ping

To include an exception in a pattern, use an exclamation mark, prefixed by the escape character \, as follows. This command will run on all servers from group1, except server2:

  • ansible group1:\!server2 -i inventory -m ping

In case you’d like to run a command only on servers that are part of both group1 and group2, for instance, you should use & instead. Don’t forget to prefix it with a \ escape character:

  • ansible group1:\&group2 -i inventory -m ping

For more information on how to use patterns when defining targets for command execution, please refer to Step 5 of our guide on How to Set Up Ansible Inventories.

Running Ansible Modules

Ansible modules are pieces of code that can be invoked from playbooks and also from the command-line to facilitate executing procedures on remote nodes. Examples include the apt module, used to manage system packages on Ubuntu, and the user module, used to manage system users. The ping command used throughout this guide is also a module, typically used to test connection from the control node to the hosts.

Ansible ships with an extensive collection of built-in modules, some of which require the installation of additional software in order to provide full functionality. You can also create your own custom modules using your language of choice.

To execute a module with arguments, include the -a flag followed by the appropriate options in double quotes, like this:

ansible target -i inventory -m module -a "module options"

As an example, this will use the apt module to install the package tree on server1:

  • ansible server1 -i inventory -m apt -a "name=tree"

Running Bash Commands

When a module is not provided via the -m option, the command module is used by default to execute the specified command on the remote server(s).

This allows you to execute virtually any command that you could normally execute via an SSH terminal, as long as the connecting user has sufficient permissions and there aren’t any interactive prompts.

This example executes the uptime command on all servers from the specified inventory:

  • ansible all -i inventory -a "uptime"
Output
server1 | CHANGED | rc=0 >> 14:12:18 up 55 days, 2:15, 1 user, load average: 0.03, 0.01, 0.00 server2 | CHANGED | rc=0 >> 14:12:19 up 10 days, 6:38, 1 user, load average: 0.01, 0.02, 0.00

Using Privilege Escalation to Run Commands with sudo

If the command or module you want to execute on remote hosts requires extended system privileges or a different system user, you’ll need to use Ansible’s privilege escalation module, become. This module is an abstraction for sudo as well as other privilege escalation software supported by Ansible on different operating systems.

For instance, if you wanted to run a tail command to output the latest log messages from Nginx’s error log on a server named server1 from inventory, you would need to include the --become option as follows:

  • ansible server1 -i inventory -a "tail /var/log/nginx/error.log" --become

This would be the equivalent of running a sudo tail /var/log/nginx/error.log command on the remote host, using the current local system user or the remote user set up within your inventory file.

Privilege escalation systems such as sudo often require that you confirm your credentials by prompting you to provide your user’s password. That would cause Ansible to fail a command or playbook execution. You can then use the --ask-become-pass or -K option to make Ansible prompt you for that sudo password:

  • ansible server1 -i inventory -a "tail /var/log/nginx/error.log" --become -K

Installing and Removing Packages

The following example uses the apt module to install the nginx package on all nodes from the provided inventory file:

  • ansible all -i inventory -m apt -a "name=nginx" --become -K

To remove a package, include the state argument and set it to absent:.

  • ansible all -i inventory -m apt -a "name=nginx state=absent" --become -K

Copying Files

With the file module, you can copy files between the control node and the managed nodes, in either direction. The following command copies a local text file to all remote hosts in the specified inventory file:

  • ansible all -i inventory -m copy -a "src=./file.txt dest=~/myfile.txt"

To copy a file from the remote server to your control node, include the remote_src option:

  • ansible all -i inventory -m copy -a "src=~/myfile.txt remote_src=yes dest=./file.txt"

Changing File Permissions

To modify permissions on files and directories on your remote nodes, you can use the file module.

The following command will adjust permissions on a file named file.txt located at /var/www on the remote host. It will set the file’s umask to 600, which will enable read and write permissions only for the current file owner. Additionally, it will set the ownership of that file to a user and a group called sammy:

  • ansible all -i inventory -m file -a "dest=/var/www/file.txt mode=600 owner=sammy group=sammy" --become -K

Because the file is located in a directory typically owned by root, we might need sudo permissions to modify its properties. That’s why we include the --become and -K options. These will use Ansible’s privilege escalation system to run the command with extended privileges, and it will prompt you to provide the sudo password for the remote user.

Restarting Services

You can use the service module to manage services running on the remote nodes managed by Ansible. This will require extended system privileges, so make sure your remote user has sudo permissions and you include the --become option to use Ansible’s privilege escalation system. Using -K will prompt you to provide the sudo password for the connecting user.

To restart the nginx service on all hosts in group called webservers, for instance, you would run:

  • ansible webservers -i inventory -m service -a "name=nginx state=restarted" --become -K

Restarting Servers

Although Ansible doesn’t have a dedicated module to restart servers, you can issue a bash command that calls the /sbin/reboot command on the remote host.

Restarting the server will require extended system privileges, so make sure your remote user has sudo permissions and you include the --become option to use Ansible’s privilege escalation system. Using -K will prompt you to provide the sudo password for the connecting user.

Warning: The following command will fully restart the server(s) targeted by Ansible. That might cause temporary disruption to any applications that rely on those servers.

To restart all servers in a webservers group, for instance, you would run:

  • ansible webservers -i inventory -a "/sbin/reboot" --become -K

Gathering Information About Remote Nodes

The setup module returns detailed information about the remote systems managed by Ansible, also known as system facts.

To obtain the system facts for server1, run:

  • ansible server1 -i inventory -m setup

This will print a large amount of JSON data containing details about the remote server environment. To print only the most relevant information, include the "gather_subset=min" argument as follows:

  • ansible server1 -i inventory -m setup -a "gather_subset=min"

To print only specific items of the JSON, you can use the filter argument. This will accept a wildcard pattern used to match strings, similar to fnmatch. For example, to obtain information about both the ipv4 and ipv6 network interfaces, you can use *ipv* as filter:

  • ansible server1 -i inventory -m setup -a "filter=*ipv*"
Output
server1 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "203.0.113.111", "10.0.0.1" ], "ansible_all_ipv6_addresses": [ "fe80::a4f5:16ff:fe75:e758" ], "ansible_default_ipv4": { "address": "203.0.113.111", "alias": "eth0", "broadcast": "203.0.113.111", "gateway": "203.0.113.1", "interface": "eth0", "macaddress": "a6:f5:16:75:e7:58", "mtu": 1500, "netmask": "255.255.240.0", "network": "203.0.113.0", "type": "ether" }, "ansible_default_ipv6": {} }, "changed": false }

If you’d like to check disk usage, you can run a Bash command calling the df utility, as follows:

  • ansible all -i inventory -a "df -h"
Output
server1 | CHANGED | rc=0 >> Filesystem Size Used Avail Use% Mounted on udev 3.9G 0 3.9G 0% /dev tmpfs 798M 624K 798M 1% /run /dev/vda1 155G 2.3G 153G 2% / tmpfs 3.9G 0 3.9G 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup /dev/vda15 105M 3.6M 101M 4% /boot/efi tmpfs 798M 0 798M 0% /run/user/0 server2 | CHANGED | rc=0 >> Filesystem Size Used Avail Use% Mounted on udev 2.0G 0 2.0G 0% /dev tmpfs 395M 608K 394M 1% /run /dev/vda1 78G 2.2G 76G 3% / tmpfs 2.0G 0 2.0G 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup /dev/vda15 105M 3.6M 101M 4% /boot/efi tmpfs 395M 0 395M 0% /run/user/0

Conclusion

In this guide, we demonstrated how to use Ansible ad hoc commands to manage remote servers, including how to execute common tasks such as restarting a service or copying a file from the control node to the remote servers managed by Ansible. We’ve also seen how to gather information from the remote nodes using limiting and filtering parameters.

As an additional resource, you can check Ansible’s official documentation on ad hoc commands.

Creative Commons License