Ansible handles configuration management using specialized modules (e.g., for managing services or packages). When tasks require remote execution of arbitrary shell syntax, users can turn to one of the two primary generic modules: the ansible.builtin.command module for simple command execution, and the ansible.builtin.shell module for complex commands and scripts.
This article will explain how to use the Ansible shell module to execute shell operations on remote machines.

Prerequisites
- Ansible installed.
- SSH connection with remote hosts.
- Python installed on remote hosts.
What Is Ansible Shell Module?
The Ansible shell module runs a command string on a remote host by passing it to the host's shell interpreter. By default, Ansible attempts to use /bin/sh or the remote user's configured default shell.
Because the command is interpreted by the shell, it can contain complex syntax that the simpler command module cannot handle. This capability makes the shell module one of the most widely used modules in the Ansible ecosystem.
Note: The shell module is part of the standard Ansible collection, so it is available out of the box.
Ansible Shell Module vs. Other Modules
Understanding the distinction between the shell module and other command-execution modules is essential for writing secure and efficient Ansible playbooks.
The following table compares the shell module and four other popular Ansible modules:
| Module | Execution Method | Shell Features | Use Case |
|---|---|---|---|
| shell | Passes the command to the default remote shell interpreter (e.g., /bin/sh). | Available. | Executing complex scripts or commands with shell syntax. |
| command | Executes the command directly, bypassing the shell. | Not available. | Simple command execution (e.g., ls, mkdir) where shell features are not needed. Preferred for simple tasks. |
| raw | Executes the command directly via SSH/WinRM. | Not available. | Helps with remote nodes that lack a Python interpreter. |
| script | Transfers a local script file to the remote host and then executes it using the remote host's default shell. | Available (for the script's content) | Running large, complex logic defined in a local .sh or similar file. |
| expect | Executes an interactive command and allows the playbook to wait for specific output (prompts) and send pre-defined responses. | Available (through the executed command's shell) | Interacting with older or legacy CLI programs that require interactive input and don't support non-interactive automation. |
When to Run Ansible Commands Using Shell Module?
Use the shell module only when the command structure requires a shell interpreter. The following scenarios explicitly require the shell module:
- Piping output. Using the pipe symbol (
|) to send the output of one command as input to another, such asdmesg | grep -i error. - Input/output redirection. Using symbols like overwrite (
>), append (>>), or input from file (<) to manage command I/O, e.g.,echo "Hello" >> /tmp/log.txt. - Variable expansion. Accessing shell variables like
$HOME,$PATH, or user-defined variables within the command string. - Chained/conditional execution. Combining multiple commands using shell control operators like semicolon (
;), double ampersand (&&) for logical AND, or double pipe (||) for logical OR, e.g.,mkdir -p /app/data && chown user:group /app/data. - Background processes. Running commands in the background using the ampersand (
&).
If the command is a simple executable name followed by arguments (e.g., ls -l /tmp), the command module is appropriate and safer.
Ansible Shell Module Syntax
The shell module syntax accepts the command string as its primary input. It also supports several arguments that modify the execution context.
For quick, one-off tasks, the shell module is called directly from the command line using the ad-hoc syntax:
ansible [pattern] -m [module_name] -a '[module_arguments]'
Shell features like pipes (|) and variable expansion often require quoting or escaping to ensure they are passed correctly to the remote shell.
The following example shows the backslash used before the awk command variable:
ansible webservers -m shell -a 'df -h /var | awk "{print \$5}"'

Within an Ansible Playbook, the shell module is defined as a task. Using the args section allows you to pass additional parameters that modify the command's execution context.
The code below shows the syntax for each parameter:
- name: [command_description]
ansible.builtin.shell: "[command_string]"
args:
chdir: [path]
creates: [path]
removes: [path]
warn: [boolean]
executable: [path_to_interpreter]
register: [command_output]
Key arguments for the shell module include:
chdir(string). Specifies the directory on the remote node where the shell command executes.creates(string). The shell command runs only if the specified file does not exist on the remote node, making the task idempotent.removes(string). The shell command runs only if the specified file exists on the remote node, which is helpful for cleanup tasks.warn(boolean). Controls the warning message that suggests using the command module instead of the shell. Default istrue. Setting it tofalsesuppresses the warning, though proper module selection is still recommended.executable(string). Specifies the exact shell interpreter to use, overriding the system's default, e.g., /bin/bash instead of /bin/sh. It allows the use of shell-specific features (such as bash arrays) if required.
Ansible Shell Module Examples
The following examples demonstrate how to use key arguments like chdir, creates, and shell redirection operators to perform complex tasks.
Change Directory and Execute Command
Use the chdir argument to navigate to a specific directory before running a sequence of commands.
- name: Run git pull inside the application directory
shell: "git pull origin main && npm install"
args:
chdir: /var/www/my_app
Use Conditional Chaining with creates
In the example below, the creates parameter ensures the setup runs only once by instructing Ansible to run the complex command only if the /tmp/setup_complete file does not exist. The ('&&') symbol guarantees that the touch command runs only if the wget command succeeds.
- name: Download and unpack a tarball if it hasn't been done
shell: "wget -q -O- http://example.com/file.tar.gz | tar xzf - -C /opt/data && touch /tmp/setup_complete"
args:
creates: /tmp/setup_complete
The following example uses the shell module to clone the test repository if and only if it does not already exist, then prints the result of the cloning task.
- name: Clone the repository if it does not exist
ansible.builtin.shell: "git clone https://github.com/example/test.git ."
args:
chdir: /opt/myproject/
creates: /opt/myproject/repo
register: git_clone_result
- name: Display the result of the cloning task
ansible.builtin.debug:
msg: "Cloning Status: {{ git_clone_result.changed | ternary('Cloned successfully', 'Already exists/Skipped') }}"
Redirect Output for System Logging
The code below executes the date command and appends its output, along with a custom message, to a log file on the remote server.
- name: Server Configuration and Logging
hosts: webservers
gather_facts: yes
vars:
deployment_status_msg: "Deployment of service A finished successfully"
log_file_path: "/var/log/application.log"
tasks:
- name: Log a status update to a system file
ansible.builtin.shell: "echo '{{ deployment_status_msg }} at $(date)' >> {{ log_file_path }}"
Alternative Ways to Run Remote Commands with Ansible
Although the shell module is versatile, exclusive reliance on it introduces risks and often leads to less readable, less idempotent playbooks. Ansible provides purpose-built modules for executing remote code, each with specific advantages.
Ansible Command Module
The command module is the standard choice for running simple executables. Because it bypasses the shell, it prevents shell injection vulnerabilities and avoids unexpected behavior caused by shell environment settings.
Ansible Expect Module
The expect module addresses interactive commands. It operates by sending input (using the send argument) after matching a specific pattern (using the regexp argument) in the command's output. The module is crucial for automation, where a command prompts for confirmation or credentials that cannot be passed as direct arguments.
Ansible Script Module
The script module allows execution of entire script files (such as Perl, Python, or shell scripts) stored locally on the Ansible control node. Ansible transfers the script to the remote node and executes it using the remote shell. This approach is better for long, complex logic than using a single shell module string.
Ansible Raw Module
The raw module provides the lowest-level remote execution. It executes a command directly via SSH or WinRM, without loading the Python interpreter on the remote host. This property is necessary for bootstrapping environments that lack a Python installation, such as embedded systems or a fresh, minimal install where the Python requirement for standard Ansible modules has not yet been met.
Conclusion
This article should help you to confidently use the Ansible Shell module to execute complex commands in your playbooks. While the Ansible Shell module is essential for complex operations that require native shell features like piping, best practices prioritize dedicated Ansible modules for security and idempotency.
Next, learn how to create a file in Ansible.

