Ansible script Module: Run Local Scripts on Remote Hosts Guide
By Luca Berton · Published 2024-01-01 · Category: installation
How to run local scripts on remote hosts with Ansible script module. Execute Python, Bash, and custom scripts without copying them first. Examples included.

How to Run Python Script on Remote Machines after transferring it?
See also: Configuring Ansible for VMware: Complete Setup Guide & Playbook
Run Python Script on Remote Machines
ansible.builtin.script- Runs a local script on a remote node after transferring it
script.
The full name is ansible.builtin.script, which means that is part of the Ansible builtin modules included in ansible-core.
The purpose of the module is to Runs a local script on a remote node after transferring it.
Parameters
cmdstring - script name or pathexecutablestring - executable name or path
script.
This module doesn't have any required parameters bus some options become necessary in this use case.
The cmd parameter specifies the script name or path.
The executable parameter specifies the interpreter name or path.
See also: Simplify Ansible Output with the community.general.dense Callback Plugin
Links
Demo
Let's jump into a real-life Ansible Playbook to Run Python Script on Remote Machines after transferring it.code
- cars.py
#!/usr/bin/env python3
import json
cars = {
"manufacturers": [
"Acura", "Alfa-Romeo", "Aston-Martin", "Audi", "Bentley", "BMW",
"Bugatti", "Buick", "Cadillac", "Chevrolet", "Chrysler", "Citroen",
"Deus Automobiles", "Dodge", "Ferrari", "Fiat", "Ford", "Geely",
"Genesis", "GMC", "Honda", "Hyundai", "Infiniti", "Jaguar", "Jeep",
"Kia", "Koenigsegg", "Lamborghini", "Lancia", "Land Rover", "Lexus",
"Lincoln", "Lotus", "Maserati", "Maybach", "Mazda", "McLaren", "Mercedes",
"Mini", "Mitsubishi", "Nissan", "Opel", "Pagani", "Peugeot", "Pontiac",
"Porsche", "Ram", "Renault", "Rolls-Royce", "Skoda", "Smart", "Subaru",
"Suzuki", "Tesla", "Toyota", "Volkswagen", "Volvo"
]
}
print(json.dumps(cars, indent=4))- run_python_script.yml
---
- name: run Python script
hosts: all
tasks:
- name: run cars.py script
ansible.builtin.script:
executable: python3
cmd: cars.py
register: cars_raw_output
- name: print cars_raw_output
ansible.builtin.debug:
var: cars_raw_output
verbosity: 2
- name: convert output to JSON
ansible.builtin.set_fact:
cars_list: "{{ cars_raw_output.stdout | from_json }}"
- name: print cars_list
ansible.builtin.debug:
var: cars_listexecution
ansible-pilot $ ansible-playbook -i ../virtualmachines/demo/inventory run_python_script.yml
PLAY [run Python script] **************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [demo.example.com]
TASK [run cars.py script] *************************************************************************
changed: [demo.example.com]
TASK [print cars_raw_output] **********************************************************************
skipping: [demo.example.com]
TASK [convert output to JSON] *********************************************************************
ok: [demo.example.com]
TASK [print cars_list] ****************************************************************************
ok: [demo.example.com] => {
"cars_list": {
"manufacturers": [
"Acura",
"Alfa-Romeo",
"Aston-Martin",
"Audi",
"Bentley",
"BMW",
"Bugatti",
"Buick",
"Cadillac",
"Chevrolet",
"Chrysler",
"Citroen",
"Deus Automobiles",
"Dodge",
"Ferrari",
"Fiat",
"Ford",
"Geely",
"Genesis",
"GMC",
"Honda",
"Hyundai",
"Infiniti",
"Jaguar",
"Jeep",
"Kia",
"Koenigsegg",
"Lamborghini",
"Lancia",
"Land Rover",
"Lexus",
"Lincoln",
"Lotus",
"Maserati",
"Maybach",
"Mazda",
"McLaren",
"Mercedes",
"Mini",
"Mitsubishi",
"Nissan",
"Opel",
"Pagani",
"Peugeot",
"Pontiac",
"Porsche",
"Ram",
"Renault",
"Rolls-Royce",
"Skoda",
"Smart",
"Subaru",
"Suzuki",
"Tesla",
"Toyota",
"Volkswagen",
"Volvo"
]
}
}
PLAY RECAP ****************************************************************************************
demo.example.com : ok=4 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
ansible-pilot $verbosity two execution
ansible-pilot $ ansible-playbook -i ../virtualmachines/demo/inventory run_python_script.yml -vv
ansible-playbook [core 2.13.1]
config file = None
configured module search path = ['/Users/lberton/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/Cellar/ansible/6.1.0/libexec/lib/python3.10/site-packages/ansible
ansible collection location = /Users/lberton/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible-playbook
python version = 3.10.5 (main, Jun 23 2022, 17:15:32) [Clang 13.0.0 (clang-1300.0.29.30)]
jinja version = 3.1.2
libyaml = True
No config file found; using defaults
Skipping callback 'default', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.
PLAYBOOK: run_python_script.yml *******************************************************************
1 plays in run_python_script.yml
PLAY [run Python script] **************************************************************************
TASK [Gathering Facts] ****************************************************************************
task path: /Users/lberton/prj/github/ansible-pilot/how-to-run-script/run_python_script.yml:2
ok: [demo.example.com]
META: ran handlers
TASK [run cars.py script] *************************************************************************
task path: /Users/lberton/prj/github/ansible-pilot/how-to-run-script/run_python_script.yml:5
changed: [demo.example.com] => {"changed": true, "rc": 0, "stderr": "Shared connection to demo.example.com closed.\r\n", "stderr_lines": ["Shared connection to demo.example.com closed."], "stdout": "{\r\n \"manufacturers\": [\r\n \"Acura\",\r\n \"Alfa-Romeo\",\r\n \"Aston-Martin\",\r\n \"Audi\",\r\n \"Bentley\",\r\n \"BMW\",\r\n \"Bugatti\",\r\n \"Buick\",\r\n \"Cadillac\",\r\n \"Chevrolet\",\r\n \"Chrysler\",\r\n \"Citroen\",\r\n \"Deus Automobiles\",\r\n \"Dodge\",\r\n \"Ferrari\",\r\n \"Fiat\",\r\n \"Ford\",\r\n \"Geely\",\r\n \"Genesis\",\r\n \"GMC\",\r\n \"Honda\",\r\n \"Hyundai\",\r\n \"Infiniti\",\r\n \"Jaguar\",\r\n \"Jeep\",\r\n \"Kia\",\r\n \"Koenigsegg\",\r\n \"Lamborghini\",\r\n \"Lancia\",\r\n \"Land Rover\",\r\n \"Lexus\",\r\n \"Lincoln\",\r\n \"Lotus\",\r\n \"Maserati\",\r\n \"Maybach\",\r\n \"Mazda\",\r\n \"McLaren\",\r\n \"Mercedes\",\r\n \"Mini\",\r\n \"Mitsubishi\",\r\n \"Nissan\",\r\n \"Opel\",\r\n \"Pagani\",\r\n \"Peugeot\",\r\n \"Pontiac\",\r\n \"Porsche\",\r\n \"Ram\",\r\n \"Renault\",\r\n \"Rolls-Royce\",\r\n \"Skoda\",\r\n \"Smart\",\r\n \"Subaru\",\r\n \"Suzuki\",\r\n \"Tesla\",\r\n \"Toyota\",\r\n \"Volkswagen\",\r\n \"Volvo\"\r\n ]\r\n}\r\n", "stdout_lines": ["{", " \"manufacturers\": [", " \"Acura\",", " \"Alfa-Romeo\",", " \"Aston-Martin\",", " \"Audi\",", " \"Bentley\",", " \"BMW\",", " \"Bugatti\",", " \"Buick\",", " \"Cadillac\",", " \"Chevrolet\",", " \"Chrysler\",", " \"Citroen\",", " \"Deus Automobiles\",", " \"Dodge\",", " \"Ferrari\",", " \"Fiat\",", " \"Ford\",", " \"Geely\",", " \"Genesis\",", " \"GMC\",", " \"Honda\",", " \"Hyundai\",", " \"Infiniti\",", " \"Jaguar\",", " \"Jeep\",", " \"Kia\",", " \"Koenigsegg\",", " \"Lamborghini\",", " \"Lancia\",", " \"Land Rover\",", " \"Lexus\",", " \"Lincoln\",", " \"Lotus\",", " \"Maserati\",", " \"Maybach\",", " \"Mazda\",", " \"McLaren\",", " \"Mercedes\",", " \"Mini\",", " \"Mitsubishi\",", " \"Nissan\",", " \"Opel\",", " \"Pagani\",", " \"Peugeot\",", " \"Pontiac\",", " \"Porsche\",", " \"Ram\",", " \"Renault\",", " \"Rolls-Royce\",", " \"Skoda\",", " \"Smart\",", " \"Subaru\",", " \"Suzuki\",", " \"Tesla\",", " \"Toyota\",", " \"Volkswagen\",", " \"Volvo\"", " ]", "}"]}
TASK [print cars_raw_output] **********************************************************************
task path: /Users/lberton/prj/github/ansible-pilot/how-to-run-script/run_python_script.yml:11
ok: [demo.example.com] => {
"cars_raw_output": {
"changed": true,
"failed": false,
"rc": 0,
"stderr": "Shared connection to demo.example.com closed.\r\n",
"stderr_lines": [
"Shared connection to demo.example.com closed."
],
"stdout": "{\r\n \"manufacturers\": [\r\n \"Acura\",\r\n \"Alfa-Romeo\",\r\n \"Aston-Martin\",\r\n \"Audi\",\r\n \"Bentley\",\r\n \"BMW\",\r\n \"Bugatti\",\r\n \"Buick\",\r\n \"Cadillac\",\r\n \"Chevrolet\",\r\n \"Chrysler\",\r\n \"Citroen\",\r\n \"Deus Automobiles\",\r\n \"Dodge\",\r\n \"Ferrari\",\r\n \"Fiat\",\r\n \"Ford\",\r\n \"Geely\",\r\n \"Genesis\",\r\n \"GMC\",\r\n \"Honda\",\r\n \"Hyundai\",\r\n \"Infiniti\",\r\n \"Jaguar\",\r\n \"Jeep\",\r\n \"Kia\",\r\n \"Koenigsegg\",\r\n \"Lamborghini\",\r\n \"Lancia\",\r\n \"Land Rover\",\r\n \"Lexus\",\r\n \"Lincoln\",\r\n \"Lotus\",\r\n \"Maserati\",\r\n \"Maybach\",\r\n \"Mazda\",\r\n \"McLaren\",\r\n \"Mercedes\",\r\n \"Mini\",\r\n \"Mitsubishi\",\r\n \"Nissan\",\r\n \"Opel\",\r\n \"Pagani\",\r\n \"Peugeot\",\r\n \"Pontiac\",\r\n \"Porsche\",\r\n \"Ram\",\r\n \"Renault\",\r\n \"Rolls-Royce\",\r\n \"Skoda\",\r\n \"Smart\",\r\n \"Subaru\",\r\n \"Suzuki\",\r\n \"Tesla\",\r\n \"Toyota\",\r\n \"Volkswagen\",\r\n \"Volvo\"\r\n ]\r\n}\r\n",
"stdout_lines": [
"{",
" \"manufacturers\": [",
" \"Acura\",",
" \"Alfa-Romeo\",",
" \"Aston-Martin\",",
" \"Audi\",",
" \"Bentley\",",
" \"BMW\",",
" \"Bugatti\",",
" \"Buick\",",
" \"Cadillac\",",
" \"Chevrolet\",",
" \"Chrysler\",",
" \"Citroen\",",
" \"Deus Automobiles\",",
" \"Dodge\",",
" \"Ferrari\",",
" \"Fiat\",",
" \"Ford\",",
" \"Geely\",",
" \"Genesis\",",
" \"GMC\",",
" \"Honda\",",
" \"Hyundai\",",
" \"Infiniti\",",
" \"Jaguar\",",
" \"Jeep\",",
" \"Kia\",",
" \"Koenigsegg\",",
" \"Lamborghini\",",
" \"Lancia\",",
" \"Land Rover\",",
" \"Lexus\",",
" \"Lincoln\",",
" \"Lotus\",",
" \"Maserati\",",
" \"Maybach\",",
" \"Mazda\",",
" \"McLaren\",",
" \"Mercedes\",",
" \"Mini\",",
" \"Mitsubishi\",",
" \"Nissan\",",
" \"Opel\",",
" \"Pagani\",",
" \"Peugeot\",",
" \"Pontiac\",",
" \"Porsche\",",
" \"Ram\",",
" \"Renault\",",
" \"Rolls-Royce\",",
" \"Skoda\",",
" \"Smart\",",
" \"Subaru\",",
" \"Suzuki\",",
" \"Tesla\",",
" \"Toyota\",",
" \"Volkswagen\",",
" \"Volvo\"",
" ]",
"}"
]
}
}
TASK [convert output to JSON] *********************************************************************
task path: /Users/lberton/prj/github/ansible-pilot/how-to-run-script/run_python_script.yml:16
ok: [demo.example.com] => {"ansible_facts": {"cars_list": {"manufacturers": ["Acura", "Alfa-Romeo", "Aston-Martin", "Audi", "Bentley", "BMW", "Bugatti", "Buick", "Cadillac", "Chevrolet", "Chrysler", "Citroen", "Deus Automobiles", "Dodge", "Ferrari", "Fiat", "Ford", "Geely", "Genesis", "GMC", "Honda", "Hyundai", "Infiniti", "Jaguar", "Jeep", "Kia", "Koenigsegg", "Lamborghini", "Lancia", "Land Rover", "Lexus", "Lincoln", "Lotus", "Maserati", "Maybach", "Mazda", "McLaren", "Mercedes", "Mini", "Mitsubishi", "Nissan", "Opel", "Pagani", "Peugeot", "Pontiac", "Porsche", "Ram", "Renault", "Rolls-Royce", "Skoda", "Smart", "Subaru", "Suzuki", "Tesla", "Toyota", "Volkswagen", "Volvo"]}}, "changed": false}
TASK [print cars_list] ****************************************************************************
task path: /Users/lberton/prj/github/ansible-pilot/how-to-run-script/run_python_script.yml:20
ok: [demo.example.com] => {
"cars_list": {
"manufacturers": [
"Acura",
"Alfa-Romeo",
"Aston-Martin",
"Audi",
"Bentley",
"BMW",
"Bugatti",
"Buick",
"Cadillac",
"Chevrolet",
"Chrysler",
"Citroen",
"Deus Automobiles",
"Dodge",
"Ferrari",
"Fiat",
"Ford",
"Geely",
"Genesis",
"GMC",
"Honda",
"Hyundai",
"Infiniti",
"Jaguar",
"Jeep",
"Kia",
"Koenigsegg",
"Lamborghini",
"Lancia",
"Land Rover",
"Lexus",
"Lincoln",
"Lotus",
"Maserati",
"Maybach",
"Mazda",
"McLaren",
"Mercedes",
"Mini",
"Mitsubishi",
"Nissan",
"Opel",
"Pagani",
"Peugeot",
"Pontiac",
"Porsche",
"Ram",
"Renault",
"Rolls-Royce",
"Skoda",
"Smart",
"Subaru",
"Suzuki",
"Tesla",
"Toyota",
"Volkswagen",
"Volvo"
]
}
}
META: ran handlers
META: ran handlers
PLAY RECAP ****************************************************************************************
demo.example.com : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ansible-pilot $See also: Simplifying Ansible Output with the community.general.unixy Callback Plugin
Conclusion
Now you know how to Run Python Script on Remote Machines after transferring it with Ansible.
Run a Python Script
- ansible.builtin.script:
cmd: scripts/setup.py
become: trueWith Arguments
- script:
cmd: scripts/deploy.py --env production --version {{ app_version }}
become: trueConditional Execution (creates)
- script:
cmd: scripts/init-database.sh
creates: /opt/myapp/.db-initialized
become: trueSpecify Interpreter
# Use specific Python version
- script:
cmd: scripts/analyze.py
executable: /usr/bin/python3
register: result
- debug:
var: result.stdout_linesCapture Output
- script: scripts/get-version.sh
register: version_info
changed_when: false
- set_fact:
current_version: "{{ version_info.stdout | trim }}"Script with Environment
- script: scripts/deploy.sh
environment:
APP_ENV: production
DB_HOST: "{{ db_host }}"
API_KEY: "{{ vault_api_key }}"
become: true
no_log: truescript vs command vs shell
| Module | Source | Features |
|---|---|---|
script | Local script → remote | Transfers and executes |
command | Remote command | No shell features |
shell | Remote command | Shell pipes, redirects |
When to Use script
# Complex setup that's easier as a script
- script: scripts/configure-network.py
when: initial_setup
become: true
# One-off migration script
- script: scripts/migrate-data.sh
args:
creates: /opt/myapp/.migrated
become: trueIdempotent Script Pattern
# Use creates/removes for idempotency
- script: scripts/install-app.sh
args:
creates: /opt/myapp/bin/app # Skip if exists
become: true
- script: scripts/cleanup.sh
args:
removes: /tmp/install-cache # Only run if exists
become: trueFAQ
Does the script need to exist on remote?
No — script copies from controller to remote, executes, then removes. The script only needs to exist on your Ansible controller.
How to make script idempotent?
Use creates: (skip if file exists) or removes: (only run if file exists). Or handle idempotency within the script itself.
Can I run scripts from a role?
# Script in roles/myrole/files/setup.sh
- script: setup.sh # Automatically looks in role's files/Related Articles
- Ansible JSON Conversion Guide
- flush_handlers in Ansible
- Ansible privilege escalation patterns
- building an Ansible inventory
- Ansible set_fact guide
See also
Category: installation
Watch the video: Ansible script Module: Run Local Scripts on Remote Hosts Guide — Video Tutorial