How to instruct Ansible to use specific version of Python

Instruct Ansible to use specific version of Python interpreter on remote host.

Remote host

Remote operating system.

$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 10 (buster)
Release:        10
Codename:       buster

Python interpreters available on remote system.

$ ls -l /usr/bin/python*
lrwxrwxrwx 1 root root       7 Mar  4  2019 /usr/bin/python -> python2
lrwxrwxrwx 1 root root       9 Mar  4  2019 /usr/bin/python2 -> python2.7
-rwxr-xr-x 1 root root 3689352 Oct 10 22:02 /usr/bin/python2.7
lrwxrwxrwx 1 root root      33 Oct 10 22:02 /usr/bin/python2.7-config -> x86_64-linux-gnu-python2.7-config
lrwxrwxrwx 1 root root      16 Mar  4  2019 /usr/bin/python2-config -> python2.7-config
lrwxrwxrwx 1 root root       9 Mar 26  2019 /usr/bin/python3 -> python3.7
-rwxr-xr-x 2 root root 4877888 Apr  3  2019 /usr/bin/python3.7
-rwxr-xr-x 2 root root 4877888 Apr  3  2019 /usr/bin/python3.7m
lrwxrwxrwx 1 root root      10 Mar 26  2019 /usr/bin/python3m -> python3.7m
lrwxrwxrwx 1 root root      14 Mar  4  2019 /usr/bin/python-config -> python2-config

Ansible playbook and inventory.

Sample python_interpreter_test_inventory.yml Ansible inventory file.

---
python_interpreter_test_default:
  hosts:
    debian10_python_default:
      ansible_host: 192.168.50.221

python_interpreter_test_defined:
  vars:
    ansible_python_interpreter: /usr/bin/python3
  hosts:
    debian10_python_group_defined:
      ansible_host: 192.168.50.221
    debian10_python_defined:
      ansible_host: 192.168.50.221
      ansible_python_interpreter: /usr/bin/python2
    debian10_python_auto:
      ansible_host: 192.168.50.221
      ansible_python_interpreter: auto
    debian10_python_auto_legacy:
      ansible_host: 192.168.50.221
      ansible_python_interpreter: auto_legacy

Sample python_interpreter_test_playbook.yml Ansible playbook file.

---
- hosts: python_interpreter_test_default, python_interpreter_test_defined
  gather_facts: yes
  serial: 1
  tasks:
    - debug: var=ansible_python_interpreter
    - debug: var=ansible_python_version

Define and check Python interpreter

I am using Ansible 2.9.2 with a modification applied to INTERPRETER_PYTHON_DISTRO_MAP map (to support Debian 10) in lib/ansible/config/base.yml file from Default to python3 on Debian 10 #63097 merge request. This change will go live in Ansible 2.10.

INTERPRETER_PYTHON:
  name: Python interpreter path (or automatic discovery behavior) used for module execution
  default: auto_legacy
  env: [{name: ANSIBLE_PYTHON_INTERPRETER}]
  ini:
  - {key: interpreter_python, section: defaults}
  vars:
  - {name: ansible_python_interpreter}
  version_added: "2.8"
  description:
  - Path to the Python interpreter to be used for module execution on remote targets, or an automatic discovery mode.
    Supported discovery modes are ``auto``, ``auto_silent``, and ``auto_legacy`` (the default). All discovery modes
    employ a lookup table to use the included system Python (on distributions known to include one), falling back to a
    fixed ordered list of well-known Python interpreter locations if a platform-specific default is not available. The
    fallback behavior will issue a warning that the interpreter should be set explicitly (since interpreters installed
    later may change which one is used). This warning behavior can be disabled by setting ``auto_silent``. The default
    value of ``auto_legacy`` provides all the same behavior, but for backwards-compatibility with older Ansible releases
    that always defaulted to ``/usr/bin/python``, will use that interpreter if present (and issue a warning that the
    default behavior will change to that of ``auto`` in a future Ansible release.
INTERPRETER_PYTHON_DISTRO_MAP:
  name: Mapping of known included platform pythons for various Linux distros
  default:
    centos: &rhelish
      '6': /usr/bin/python
      '8': /usr/libexec/platform-python
    debian:
      '10': /usr/bin/python3
    fedora:
      '23': /usr/bin/python3
    redhat: *rhelish
    rhel: *rhelish
    ubuntu:
      '14': /usr/bin/python
      '16': /usr/bin/python3
  version_added: "2.8"
  # FUTURE: add inventory override once we're sure it can't be abused by a rogue target
  # FUTURE: add a platform layer to the map so we could use for, eg, freebsd/macos/etc?
INTERPRETER_PYTHON_FALLBACK:
  name: Ordered list of Python interpreters to check for in discovery
  default:
  - /usr/bin/python
  - python3.7
  - python3.6
  - python3.5
  - python2.7
  - python2.6
  - /usr/libexec/platform-python
  - /usr/bin/python3
  - python
  # FUTURE: add inventory override once we're sure it can't be abused by a rogue target
  version_added: "2.8"

Since Ansible 2.8 the default value ansible_python_interpreter is auto_legacy, which means that it will prefer /usr/bin/python (if it exists) over the discovered Python version. You can set it to auto, which will be default in the future, so it will work in the opposite way.

To suppress the deprecation and fallback warning use auto_legacy_silent or auto_silent values.

Alternatively, set it to path to a specific Python interpreter.

To illustrate this use the Ansible playbook that was described earlier.

$ ansible-playbook -i python_interpreter_test_inventory.yml python_interpreter_test_playbook.yml
PLAY [python_interpreter_test_default, python_interpreter_test_defined] ***********************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [debian10_python_default]

TASK [debug] **********************************************************************************************************************************************************************
ok: [debian10_python_default] => {
    "ansible_python_interpreter": "VARIABLE IS NOT DEFINED!"
}

TASK [debug] **********************************************************************************************************************************************************************
ok: [debian10_python_default] => {
    "ansible_python_version": "2.7.16"
}

PLAY [python_interpreter_test_default, python_interpreter_test_defined] ***********************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [debian10_python_defined]

TASK [debug] **********************************************************************************************************************************************************************
ok: [debian10_python_defined] => {
    "ansible_python_interpreter": "/usr/bin/python2"
}

TASK [debug] **********************************************************************************************************************************************************************
ok: [debian10_python_defined] => {
    "ansible_python_version": "2.7.16"
}

PLAY [python_interpreter_test_default, python_interpreter_test_defined] ***********************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [debian10_python_auto]

TASK [debug] **********************************************************************************************************************************************************************
ok: [debian10_python_auto] => {
    "ansible_python_interpreter": "auto"
}

TASK [debug] **********************************************************************************************************************************************************************
ok: [debian10_python_auto] => {
    "ansible_python_version": "3.7.3"
}

PLAY [python_interpreter_test_default, python_interpreter_test_defined] ***********************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [debian10_python_auto_legacy]

TASK [debug] **********************************************************************************************************************************************************************
ok: [debian10_python_auto_legacy] => {
    "ansible_python_interpreter": "auto_legacy"
}

TASK [debug] **********************************************************************************************************************************************************************
ok: [debian10_python_auto_legacy] => {
    "ansible_python_version": "2.7.16"
}

PLAY [python_interpreter_test_default, python_interpreter_test_defined] ***********************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [debian10_python_group_defined]

TASK [debug] **********************************************************************************************************************************************************************
ok: [debian10_python_group_defined] => {
    "ansible_python_interpreter": "/usr/bin/python3"
}

TASK [debug] **********************************************************************************************************************************************************************
ok: [debian10_python_group_defined] => {
    "ansible_python_version": "3.7.3"
}

PLAY RECAP ************************************************************************************************************************************************************************
debian10_python_auto          : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
debian10_python_auto_legacy   : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
debian10_python_default       : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
debian10_python_defined       : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
debian10_python_group_defined : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

It's as simple as that.