Ansible is a popular automation platform allowing you to manage thousands of nodes at one time. One of the most useful features of Ansible is its ability to run ad-hoc commands on remote computers with the Ansible shell module.
The shell module allows you to run ad-hoc commands or even small scripts like you’re sitting in front of the local console of each machine. It’s a handy module if you need to quickly run a command on a managed node.
In this tutorial, you’re going to learn what the Ansible shell module is, how it works, and how to use the Ansible shell module to run commands on remote hosts.
Table of Contents
This post will be a step-by-step tutorial on the Ansible shell module. If you’d like to follow along, be sure you have the following in place:
- An Ansible controller host – This tutorial will be using Ansible v2.9.18 on an Ubuntu 18.04.5 LTS machine. Learn how to set up an Ansible controller host here.
- A user account on the Ansible controller host that will allow you to create simple playbooks and run them on a managed node.
- A remote computer to run commands on – This tutorial will be using a node called SRV1 and webserver.
Running Ad-Hoc Commands with the Ansible Shell Module
In its simplest form, the Ansible shell module can run a single command on a remote host. To do this, a one-line command on your Ansible controller will work. For example, let’s run a simple command on a remote host to print the
PATH environment variable‘s value on a remote machine.
Log onto your Ansible controller and run the following command. This command uses the shell module (
-m) to connect to the webserver machine and pass an argument (
-a) which is the command to execute. In this instance, it’s running
echo $PATH to return the PATH environment variable’s value.
# webserver is host # -m is syntax which is used with any module ansible webserver -m ansible.builtin.shell -a 'echo $PATH'
Running Commands within a Playbook
Looks can be deceiving when it comes to the Ansible shell module. You can run remote commands with this module in many different ways. But first, let’s ease in slowly and learn how to run a simple command.
Let’s say you need to create a text file on a managed node at /opt/new_file.txt on a Linux host. You’d like to use the Ansible shell module to do that.
1. SSH to your Ansible controller host.
2. Create a directory called ansible_shell_module_demo in your home directory. This directory will contain the [playbook] you’ll use to invoke the shell module.
mkdir ~/ansible_shell_module_demo cd ~/ansible_shell_module_demo
3. Open your favorite text editor and create a file called my_playbook.yml in the ~/ansible_shell_module_demo directory and paste in the following YAML playbook contents. This playbook has a single task that uses the Ansible
shell module to send some text (
I am creating a new file) to the
/opt/new_file.txt text file.
Since the playbook will be creating the file in the /opt directory, Ansible will need to run with sudo permissions using the
Ansible playbooks are written in YAML. To learn more about YAML, [click here]
tasks: - name: Create a text file in opt directory using /bin/sh shell ansible.builtin.shell: echo "I am creating a new file" > /opt/new_file.txt become: true # Ensure that the highest permissions are used on the remote host
By default, when you specify
shellas the module to use for a task, it will use the Bash shell. To invoke the Ansible shell module, you must use
ansible.builtin.shell. If you have Windows-based remote machines, you must use the ansible. windows.win_shell module instead.
4. Create a subfolder called SRV1 under ~/ansible_shell_module_demo. This folder represents the remote node the playbook Ansible will execute on.
5. Now, invoke the playbook to copy it to and execute the task on the remote host.
Inventory is basically a collection of remote hosts either by their hostnames or by their IP address. If you wish to read in-depth how to create inventories, click here.
You can see below that the TASK has a status of changed meaning the remote host wasn’t in the proper state and was changed to run the command.
In the previous example, you ran the Ansible shell module to invoke a simple command. Sometimes, you need to customize that behavior using arguments. Arguments allow you to pass configuration values to the shell module as it runs.
For example, when you run a shell command, the command also has a working directory or directory the command knows it’s running in. Perhaps, you must ensure you run a command in the /opt directory. To change that behavior, you’d use the
Assuming you’re still in your Ansible controller host:
1. Open the playbook in your favorite text editor you recently created.
2. Replace the playbook text with the below playbook. This playbook uses the
args attribute to pass the
chdir argument to the task which changes the working directory to /opt before the command executes.
tasks: - name: Change the working directory to /opt before executing the command ansible.builtin.shell: ls -lh >> my_text_file.txt args: chdir: /opt # Changes to /opt directory
You can see below the output is the same as previously.
3. Now, open the playbook again and paste in the following YAML. The below playbook performs the exact same action as the previous step. But, instead, it specifies the command to run (
ls -lh) on its own line via the
cmd attribute and removes the need for the
tasks: - name: Change the working directory to /opt ansible.builtin.shell: cmd: ls -lh # To list files under /opt directory chdir: /opt # changes to /opt directory
4. By default, the shell module uses the sh shell on remote Linux nodes. But, you can change that using the
executable argument. By specifying the
executable argument like below, the
ls command will now use the Bash shell.
tasks: - name: Reads all logs files in /opt directory ansible.builtin.shell: ls -lh args: executable: /bin/bash # Run ls in the Bash shell
Executing Multiple Commands
So far, you’ve only been running a single command a time. Let’s change that. You can also invoke the Ansible shell module to run many commands at once.
On your Ansible controller host:
1. Open a text editor again and create another playbook called my_playbook2.yml in the ~/ansible_shell_module_demo directory.
2. Copy and paste the following playbook into the my_playbook2.yml file. Notice that the shell module will not create four separate files by running four separate commands in the same playbook.
tasks: - name: Create multiple text file in tmp directory with shell module ansible.builtin.shell: | # Multiple commands in Ansible shell module echo "This will go in log file" > /tmp/log_file.txt echo "This will go in memory file"> /tmp/memory_file.txt echo "This will go in disk file" > /tmp/disk_file.txt echo "This will go in version file"> /tmp/version_file.txt become: true args: chdir: /var/log # Changing the directory
3. Now, execute the playbook on the SRV1 node.
ansible-playbook my_playbook.yml --inventory SRV1
You should see that the TASK has a status of changed.
4. Verify Ansible created the files on the remote host as expected.
Generating Debug Output
Sometimes playbooks won’t do what you expect and it’s time to start troubleshooting. When troubleshooting, you’ll sometimes need more granular information as to what’s going on inside of the playbook. In that case, you’ll need to read the debug output.
1. Perhaps the previous playbook was causing problems and you want to investigate what’s going on. In that case, use the
debug parameter as shown below.
When Ansible runs this playbook, it will generate the output in JSON format displaying the detailed execution of commands.
tasks: - name: Create multiple text file in tmp directory with shell module shell: | # Multiple commands in Ansible shell module echo "This will go in log file" > /tmp/log_file.txt echo "This will go in memory file"> /tmp/memory_file.txt echo "This will go in disk file" > /tmp/disk_file.txt echo "This will go in version file"> /tmp/version_file.txt register: shell_output become: true args: chdir: /var/log # Changing the directory - debug: var=shell_output # This will provide the output
2. Run the playbook against the SRV1 node again.
ansible-playbook my_playbook.yml --inventory SRV1
If all goes well, you should see output like below.
3. Now connect to the remote computer and verify the files were again created.
Ansible shell module is a great way to run your commands on remote hosts. It provides a variety of flavored functionalities and gives you a handy way to remotely execute commands.