The Ansible shell module is used to execute shell commands on remote hosts. It provides a way to run any command that you could run in a shell on a remote system. This module can execute complex command sequences and handle pipes and redirections, making it highly versatile.
This guide will provide an in-depth look at the Ansible shell module with practical examples to help you understand its capabilities and usage.
Table of Contents
When to Use the Shell Module
Use the shell module when:
- You need to execute a command that requires shell-specific functionality (e.g., pipes, redirections).
- No specific Ansible module exists for the task you want to perform.
- You want to run a series of commands or a command sequence that involves shell features.
Basic Usage
To use the shell module, you need to define it within a playbook. Here’s a basic example:
- name: Basic Shell Command Example
hosts: all
tasks:
- name: Run a simple command
shell: echo "Hello, World!"
In this example playbook, the echo “Hello, World!” command is executed on all targeted hosts.
Advanced Usage
The shell module can handle more complex scenarios by utilizing various parameters and features:
- chdir: Change to this directory before running the command.
- creates: A filename, when it already exists, will skip the command.
- removes: A filename, when it does not exist, will skip the command.
- stdin: Set the command’s standard input.
Here’s an advanced example:
- name: Advanced Shell Command Example
hosts: all
tasks:
- name: Run a command in a specific directory
shell: ls -la
args:
chdir: /tmp
This playbook runs the ls -la command in the /tmp directory on all targeted hosts.
Example 1: Running a Simple Command
Here’s an example playbook that uses the Ansible shell module to run the date command on all target hosts:
- name: Run a simple command
hosts: all
tasks:
- name: Get the current date and time
shell: date
register: current_date_time
- name: Display the current date and time
debug:
var: current_date_time.stdout
This playbook executes the date command on all specified hosts, which displays the current date and time.
Example 2: Show Memory Usage
In this example, we will use free -h command to show memory usage. The command output is registered in the memory_usage variable and displayed in the next task.
- name: Show memory usage
hosts: all
tasks:
- name: Get memory usage
shell: free -h
register: memory_usage
- name: Display memory usage
debug:
msg: "Memory usage on {{ inventory_hostname }}: {{ memory_usage.stdout }}"
The above playbook will display the following output.
PLAY [Show memory usage] **********************************************************************
TASK [Gathering Facts] ************************************************************************
ok: [host1]
ok: [host2]
TASK [Get memory usage] ***********************************************************************
changed: [host1]
changed: [host2]
TASK [Display memory usage] *******************************************************************
ok: [host1] => {
"msg": "Memory usage on host1: total used free shared buff/cache available\nMem: 7.7G 1.2G 5.9G 124M 618M 6.0G\nSwap: 2.0G 0B 2.0G"
}
ok: [host2] => {
"msg": "Memory usage on host2: total used free shared buff/cache available\nMem: 15.5G 4.5G 9.0G 512M 2.0G 12.5G\nSwap: 4.0G 512M 3.5G"
}
PLAY RECAP ************************************************************************************
host1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host2 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
In this playbook:
- The shell module runs the free -h command to get memory usage and stores the output in the memory_usage variable.
- The debug module is then used to display the output of the memory_usage.stdout variable, which contains the command’s result.
Example 3: Run Multiple Commands
In this example, we will use the shell module to run multiple commands on all target hosts:
- name: Run multiple commands
hosts: all
tasks:
- name: Execute multiple commands
shell: |
echo "Listing files in the current directory:"
ls -l
echo "Displaying disk usage:"
df -h
register: command_output
- name: Display the output of the commands
debug:
var: command_output.stdout
When you run the above playbook, you will see the following result:
PLAY [Run multiple commands] **********************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [host1]
ok: [host2]
TASK [Execute multiple commands] ******************************************************************
changed: [host1]
changed: [host2]
TASK [Display the output of the commands] *********************************************************
ok: [host1] => {
"command_output.stdout": "Listing files in the current directory:\ntotal 0\ndrwxr-xr-x 2 user user 64 Oct 9 13:30 folder1\ndrwxr-xr-x 2 user user 64 Oct 9 13:30 folder2\nDisplaying disk usage:\nFilesystem Size Used Avail Use% Mounted on\n/dev/sda1 50G 25G 25G 50% /\n"
}
ok: [host2] => {
"command_output.stdout": "Listing files in the current directory:\ntotal 0\ndrwxr-xr-x 2 user user 64 Oct 9 13:30 folder1\ndrwxr-xr-x 2 user user 64 Oct 9 13:30 folder2\nDisplaying disk usage:\nFilesystem Size Used Avail Use% Mounted on\n/dev/sda1 50G 25G 25G 50% /\n"
}
PLAY RECAP *****************************************************************************************
host1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host2 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
In this playbook:
- The shell module runs a series of commands using a multi-line (|) block.
- It first prints a message listing the files in the current directory with ls -l.
- Then, it displays disk usage information with df -h.
Example 4: Check If Apache Service is Running
Here’s an example playbook to check if the Apache service is running:
- name: Check if Apache service is running
hosts: all
tasks:
- name: Check Apache service status
shell: systemctl is-active apache2
register: apache_status
ignore_errors: yes
- name: Display Apache service status
debug:
msg: "Apache service status: {{ apache_status.stdout }}"
In this playbook:
- shell module runs systemctl is-active apache2 to check if the Apache service is active. The result is stored in the apache_status variable.
- ignore_errors: yes option ensures that the playbook continues running even if the command fails (e.g., if Apache is not installed).
- debug module then displays the status of the Apache service.
Example 5: Using creates to Check File Existence
This example demonstrates how to conditionally run a command based on the existence of a file.
- name: Using creates to Check File Existence
hosts: all
tasks:
- name: Create a file if it does not exist
shell: touch /tmp/unique_file.txt
args:
creates: /tmp/unique_file.txt
The command touch /tmp/unique_file.txt will only run if the file /tmp/unique_file.txt does not exist.
Example 6: Combining Shell Commands with Output Processing
This playbook finds the largest file in /var/log, sorts the results by size, and displays the largest file.
- name: Process command output
hosts: all
tasks:
- name: Find the largest file in /var/log
shell: |
find /var/log -type f -exec ls -lh {} + | sort -k 5 -rh | head -n 1
register: largest_file
- name: Display the largest file
debug:
msg: "The largest file in /var/log is: {{ largest_file.stdout }}"
In this example:
- The first task searches for all files in /var/log, lists them with detailed information, sorts them by size in descending order, and retrieves the largest file.
- The second task uses the debug module to display the name and details of the largest file found in /var/log.
Conclusion
The Ansible shell module is a handy tool that allows you to execute arbitrary shell commands on remote hosts. While it offers great flexibility, it is important to use it judiciously and follow best practices to ensure your playbooks remain secure and maintainable.
FAQs
1. How is the shell module different from the command module in Ansible?
The shell module allows commands with shell-specific features like pipes, redirects, and variables, whereas the command module only runs commands without a shell.
2. How do I handle errors when using the shell module?
You can handle errors using the ignore_errors parameter or by setting failed_when to customize failure conditions.
3. Can I capture the output of a command executed by the shell module?
Yes, use the register keyword to capture the output of the command into a variable for use in subsequent tasks.