Ansible command Module: Run Shell Commands on Remote Hosts (ansible.builtin.command)
By Luca Berton · Published 2024-01-01 · Category: installation
How to run commands on remote hosts with Ansible command module (ansible.builtin.command). Execute binaries, use creates/removes guards. Practical YAML playbook examples.
The ansible.builtin.command module runs commands on remote hosts without shell processing. This means no pipes, redirects, or variable expansion — making it more secure than shell for most tasks. Here's everything you need to know.
Basic Usage
Key Parameters
| Parameter | Description | Example | |-----------|-------------|---------| | cmd | Command string to execute | cmd: ls -la /tmp | | argv | Command as list (safer) | argv: [ls, -la, /tmp] | | chdir | Change to directory first | chdir: /opt/app | | creates | Skip if file exists | creates: /opt/app/installed | | removes | Skip if file doesn't exist | removes: /tmp/lock.pid | | stdin | Pass data to stdin | stdin: "yes" | | stdin_add_newline | Add newline to stdin | stdin_add_newline: true | | strip_empty_ends | Strip empty lines from output | strip_empty_ends: true |
Idempotent Command Execution
The biggest problem with command is it always reports changed. Use creates and removes for idempotency:
Using argv for Safe Arguments
When arguments contain spaces or special characters, use argv:
Working Directory
Environment Variables
Capturing Output
Handling Return Codes
command vs shell vs raw
| Feature | command | shell | raw | |---------|-----------|---------|-------| | Shell processing | ❌ No | ✅ Yes | ✅ Yes | | Pipes & redirects | ❌ | ✅ \|, >, >> | ✅ | | Variable expansion | ❌ | ✅ $HOME | ✅ | | Glob patterns | ❌ | ✅ .log | ✅ | | Python required | ✅ | ✅ | ❌ | | Security | 🟢 Safest | 🟡 Injection risk | 🔴 Most risk |
Rule of thumb: Use command unless you specifically need shell features. Use shell for pipes and redirects. Use raw only for hosts without Python.
Common Patterns
Check Before Acting
Suppress Changed Status
Pipeline Alternative Without Shell
Warnings and Ansible-Lint
Ansible-lint flags command when a built-in module exists:
Common replacements:
| Command | Use This Module Instead | |---------|----------------------| | mkdir | ansible.builtin.file (state: directory) | | rm | ansible.builtin.file (state: absent) | | chmod | ansible.builtin.file (mode) | | chown | ansible.builtin.file (owner, group) | | cp | ansible.builtin.copy | | ln -s | ansible.builtin.file (state: link) | | systemctl | ansible.builtin.service or ansible.builtin.systemd | | apt install | ansible.builtin.apt | | yum install | ansible.builtin.yum or ansible.builtin.dnf |
FAQ
When should I use command instead of shell?
Always prefer command unless you need shell features like pipes (|), redirects (>), variable expansion ($VAR), or glob patterns (.log). The command module is more secure because it doesn't pass through a shell interpreter, eliminating shell injection risks.
How do I make command idempotent?
Use the creates parameter to skip execution when a file already exists, or removes to skip when a file doesn't exist. For more complex idempotency, use register + changed_when with a conditional check.
Why does command always show "changed"?
The command module has no way to know if the command actually changed anything. Use changed_when: false for read-only commands, or changed_when with a condition based on the registered output.
Can I use command with sudo?
Yes, use Ansible's become mechanism instead of putting sudo in the command:
Conclusion
Use ansible.builtin.command as your default for running commands on remote hosts. It's safer than shell because it skips shell processing. Make your commands idempotent with creates/removes, suppress false changed results with changed_when: false, and consider native modules before reaching for command.
Related Articles • Ansible shell vs command • Ansible Builtin Command Module • Ansible error handling
Category: installation