Let's say you have two systems, the Ansible controller (that's the system Ansible is installed and running on) and a target server, and let's say each system has a different version of Python. Perhaps the Python CLI on the Ansible controller is Python version 3.12.0.
[admin@controller ~]$ python --version
Python 3.12.0
And the Python version on a target server is Python version 3.9.18.
[john.doe@server1 ~]$ python --version
Python 3.9.18
Let's say you have the following playbook, which uses the ansible.builtin.file module to create and then delete the /tmp/foo.txt file on the target system on the target system.
---
- hosts: all
tasks:
- ansible.builtin.file:
path: /tmp/foo.txt
state: "{{ item }}"
with_items:
- touch
- absent
...
Let's run the ansible-playbook command with the -vvv (very verbose) flag.
ansible-playbook example.yml --inventory server1.example.com, --user john.doe --ask-pass -vvv
So, underneath the hood, a directory is created on the target server, something like this.
/home/john.doe/.ansible/tmp/ansible-tmp-1713408593.1331322-16539-164674748277391
This can been seen in the output with the -vvv flag. The output should have something like this.
<server1.example.com> SSH: EXEC \
sshpass -d12 \
ssh -C \
-o ControlMaster=auto \
-o ControlPersist=60s \
-o 'User="john.doe"' \
-o ConnectTimeout=10 \
-o 'ControlPath="/home/john.doe/.ansible/cp/da9b18fba3"' \
server1.example.com \
'/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo /home/john.doe/.ansible/tmp `"&& mkdir "` echo /home/john.doe/.ansible/tmp/ansible-tmp-1713408596.274309-16590-237914960550324 `" && echo ansible-tmp-1713408596.274309-16590-237914960550324="` echo /home/john.doe/.ansible/tmp/ansible-tmp-1713408596.274309-16590-237914960550324 `" ) && sleep 0'"'"''
It is important to recognize that the directory will be removed, so for this excercise, I would set the ANSIBLE_KEEP_REMOTE_FILES environment variable to 1 which instructs the Ansible controller to not delete the tmp files and directories that get created on the target server.
export ANSIBLE_KEEP_REMOTE_FILES=1
And then the AnsiballZ_file.py Python script is PUT from the Ansible controller to the target server.
<server1.example.com> PUT /home/admin/.ansible/tmp/ansible-local-1681708tty8e7/tmpclmqm36v TO /home/john.doe/.ansible/tmp/ansible-tmp-1713405726.6487887-17223-276200849623319/AnsiballZ_file.py
Ansible uses the sshpass command to connect to the target server and then ssh command and the /bin/sh (shell) CLI with the -c (command) flag to run a command on the target server. The command that is run on the target server is almost always /home/john.doe/.ansible/tmp/ansible-tmp/AnsiballZ_<module name>.py, a Python script. Since this command is being run on the target server, this uses the Python CLI on the target server, not Python on the Ansible controller.
<server1.example.com> SSH: EXEC \
sshpass -d12 \
ssh -C \
-o ControlMaster=auto \
-o ControlPersist=60s \
-o 'User="john.doe"' \
-o ConnectTimeout=10 \
-o 'ControlPath="/home/john.doe/.ansible/cp/da9b18fba3"' \
server1.example.com \
'/bin/sh -c '"'"'chmod u+x /home/john.doe/.ansible/tmp/ansible-tmp-1713405726.6487887-17223-276200849623319/ /home/john.doe/.ansible/tmp/ansible-tmp-1713405726.6487887-17223-276200849623319/AnsiballZ_file.py && sleep 0'"'"''
And if we look at line 1 in AnsiballZ_file.py, we can see it is using /usr/bin/python3.
[john.doe@server1 ~]$ head -1 /home/webproc/.ansible/tmp/ansible-tmp-1713408594.9606252-16563-253322922038816/AnsiballZ_file.py
#!/usr/bin/python3
There are a few different ways to tell Ansible to run the AnsiballZ_<module>.py files on the target servers with a certain version of Python.
- Using the ansible_python_interpreter variable
- Using the python_interpreter directive in ansible.cfg (this article)
The interpreter_python directive in ansible.cfg will be in the [defaults] section, something like this.
[defaults]
interpreter_python = /usr/bin/python3.9
Let's say the playbook includes ansible_python_interpreter: /usr/bin/python3.9.
---
- hosts: all
tasks:
- ansible.builtin.file:
path: /tmp/foo.txt
state: "{{ item }}"
with_items:
- touch
- absent
...
This will cause the AnsiballZ_file.py to have /usr/bin/python3.9.
[john.doe@server1 ~]$ head /home/john.doe/.ansible/tmp/ansible-tmp-1713410268.6565683-30050-40883123872239/AnsiballZ_file.py
#!/usr/bin/python3.9
Did you find this article helpful?
If so, consider buying me a coffee over at