AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

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.

Ansible script Module: Run Local Scripts on Remote Hosts Guide

How to Run Python Script on Remote Machines after transferring it?

I'm going to show you a live Playbook with some simple Ansible code. I'm Luca Berton and welcome to today's episode of Ansible Pilot.

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

Let’s talk about the Ansible module 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

cmd string - script name or path • executable string - executable name or path

Let me summarize the main parameters of the module 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

ansible.builtin.script

Demo

Let's jump into a real-life Ansible Playbook to Run Python Script on Remote Machines after transferring it. I'm going to show you how to create a cars.py custom Python script that output a JSON file, transfers it to a remote machine, and executes it using python3 interpreter.

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_list

execution

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 $

code with ❤️ in GitHub

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: true

With Arguments

- script:
    cmd: scripts/deploy.py --env production --version {{ app_version }}
  become: true

Conditional Execution (creates)

- script:
    cmd: scripts/init-database.sh
    creates: /opt/myapp/.db-initialized
  become: true

Specify Interpreter

# Use specific Python version
- script:
    cmd: scripts/analyze.py
    executable: /usr/bin/python3
  register: result

- debug: var: result.stdout_lines

Capture 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: true

script 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: true

Idempotent 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: true

FAQ

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 Guideflush_handlers in AnsibleAnsible privilege escalation patternsbuilding an Ansible inventoryAnsible set_fact guide

See also

Ansible Run Python Scripts: Execute & Manage Python on Remote Hosts

Category: installation

Watch the video: Ansible script Module: Run Local Scripts on Remote Hosts Guide — Video Tutorial

Browse all Ansible tutorials · AnsiblePilot Home