Instantly share code, notes, and snippets.

Embed
What would you like to do?
Get Ansible to work on bare Ubuntu 16.04 without python 2.7
# Add this snippet to the top of your playbook.
# It will install python2 if missing (but checks first so no expensive repeated apt updates)
# gwillem@gmail.com
- hosts: all
gather_facts: False
tasks:
- name: install python 2
raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal)
@kiview

This comment has been minimized.

kiview commented Jul 28, 2016

Thanks, really useful!

@tedder

This comment has been minimized.

tedder commented Aug 5, 2016

High SEO on this :P which is awesome. I had to modify it to work on EC2. I might have to tune it a bit- for instance- I exclude my docker hosts since they aren't ubuntu.

raw: sudo bash -c "test -e /usr/bin/python || (apt -qqy update && apt install -qy python-minimal)"
@stephensamra

This comment has been minimized.

stephensamra commented Sep 5, 2016

This is great, thanks!

Worth noting that you may need to add become: true if you're not connecting as root

@jbroadway

This comment has been minimized.

jbroadway commented Sep 8, 2016

I ran into an issue with some roles needing the gather_facts step. Here's what ended up getting those roles working again for me:

gather_facts: False
pre_tasks:
  - raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal)
  - setup: # aka gather_facts

tasks:
  # etc. etc.
@nickhammond

This comment has been minimized.

nickhammond commented Sep 8, 2016

Also noticed this in the installation docs: ansible myhost --sudo -m raw -a "yum install -y python2 python-simplejson"

http://docs.ansible.com/ansible/intro_installation.html

@s0meone

This comment has been minimized.

s0meone commented Nov 4, 2016

Had the same problem as @jbroadway, some roles needing gather_facts.

Worth noting that pre_tasks can have the same options as regular tasks. So I made some small changes so the raw module doesn't report changes every time it's run:

gather_facts: False
pre_tasks:
  - name: Install python for Ansible
    raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal)
    changed_when: False
  - setup: # aka gather_facts

tasks:
  # etc. etc.
@jeromegamez

This comment has been minimized.

jeromegamez commented Dec 18, 2016

If you're using Ansible >2.2.0, you can set the ansible_python_interpreter configuration option to /usr/bin/python3:

ansible my_ubuntu_host -m ping -e 'ansible_python_interpreter=/usr/bin/python3'

or in your inventory file:

[ubuntu_hosts]
<xxx.xxx.xxx.xxx>

[ubuntu_hosts:vars]
ansible_python_interpreter=/usr/bin/python3

Source: https://docs.ansible.com/ansible/python_3_support.html

@cllovatto

This comment has been minimized.

cllovatto commented Jan 2, 2017

Building upon @s0meone 's work to make the step report correctly the changed status ('changed' only when it does execute the apt... commands):

  gather_facts: False
  pre_tasks:
    - name: Install python for Ansible
      raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal)
      register: output
      changed_when: output.stdout != ""
  - setup: # aka gather_facts
@JefStat

This comment has been minimized.

JefStat commented Jan 4, 2017

When launching an ec2 instance in the same play I had to do the following

tasks:
    - name: Creating es ec2 instance 
      ec2:
        region: us-west-2
        assign_public_ip: yes
        image: "{{ aws_base_amis.debian }}"
        wait: yes
      register: result_ec2_instance

    - name: Storing ec2 facts
      set_fact:
        aws_elasticsearch_ec2_instance: "{{ result_ec2_instance.instances | first }}"
      when: result_ec2_instance.changed

    - name: Waiting for ssh to spin up
      wait_for:
        host: "{{ aws_elasticsearch_ec2_instance.public_ip }}"
        port: 22

    - name: Adding ec2 instance as host for running python 2 install
      add_host:
        name: special_host_name
        ansible_host: "{{ aws_elasticsearch_ec2_instance.public_ip }}"
        ansible_port: 22
        ansible_user: ubuntu
        ansible_private_key_file: "XXXX.pem"

    - name: Installing python
      delegate_to: special_host_name
      raw: test -e /usr/bin/python || (sudo apt -y update && sudo apt install -y python-minimal)
@Chabane

This comment has been minimized.

Chabane commented Jan 5, 2017

Nice!

@xamox

This comment has been minimized.

xamox commented Feb 9, 2017

Thanks

@greenled

This comment has been minimized.

greenled commented Feb 17, 2017

Great work. Thanks

@bocharsky-bw

This comment has been minimized.

bocharsky-bw commented Apr 18, 2017

I think test -f /usr/bin/python will be more robust here, since -e is true if file exists (regardless of type), i.e. no matter is it a directory or a regular file. But -f is true if file exists and is a regular file.

@chrisbeyer

This comment has been minimized.

chrisbeyer commented Apr 19, 2017

Thanks. This is very helpful and got me on the right track :)
I decided to upgrade to the latest version of ansible (2.3.0.0) as per jeromegamez comment. Then I created a...

group_vars/all

..file and added...

ansible_python_interpreter: /usr/bin/python3

For some reason Vagrant wasn't happy otherwise. Now all good.

@ubante

This comment has been minimized.

ubante commented May 2, 2017

The advantage of test -e is it will accept a symlink like:

$ ls -l /usr/bin/python
lrwxrwxrwx 1 root root 9 Jun 18  2013 /usr/bin/python -> python2.7*
@alanwsmith

This comment has been minimized.

alanwsmith commented May 16, 2017

Here's an example combining various techniques from above. It works as expected with Ansible (2.3.0.0) and a Vagrant "ubuntu/xenial64" box (version 20170414.1.0).

---
- hosts: all
  become: true
  gather_facts: false
  pre_tasks:
  - name: Install python2 for Ansible
    raw: bash -c "test -e /usr/bin/python || (apt -qqy update && apt install -qqy python-minimal)"
    register: output
    changed_when: output.stdout != ""
  - name: Gathering Facts
    setup:
  tasks:
  - name: Install NGINX
    apt: name=nginx state=latest
  - debug: msg="{{ ansible_nodename }} (via Gathered Facts from 'setup')"

A few notes:

  • It's a standalone file I used for validation. Hence hosts: all and become: true at the start.
  • I added - name: Gathering Facts above setup: to reproduce the output that would show up if gather_facts wasn't turned off.
  • The NGINX install is a basic smoke test to make sure everything is really working.
  • The debug message uses {{ ansible_nodename }} which is an Ansible Fact pulled in from setup/gather_facts to verify they worked as expected.
@nicluo

This comment has been minimized.

nicluo commented May 16, 2017

I just want to point out that the task randomly has a change state even when python was already installed.

Debugging the output shows that stdout is sometimes "", but can also be "\r\n".
Building on previous answers, this is the modified task entry that I use.

---
- hosts: all
  become: true
  gather_facts: false

  pre_tasks:

  - name: Install python2 for Ansible
    raw: bash -c "test -e /usr/bin/python || (apt -qqy update && apt install -qqy python-minimal)"
    register: output
    changed_when:
    - output.stdout != ""
    - output.stdout != "\r\n"
  - name: Gathering Facts
    setup:
@flyer103

This comment has been minimized.

flyer103 commented May 16, 2017

It seems that it's not enough just to install python-minimal and it would cause setup module to fail. Installing python2.7 is more suitable.

@mabushey

This comment has been minimized.

mabushey commented Jun 8, 2017

On Ubuntu 16.04, apt install python-minimal installs: python python-minimal python2.7 python2.7-minimal

Only installing python2.7 does not get you /usr/bin/python, so this does not work for Ansible.

@manelclos

This comment has been minimized.

manelclos commented Jul 31, 2017

based on geromegamez answers, you can make python3 the default adding this to your hosts file (inventory):

[all:vars]
ansible_python_interpreter=/usr/bin/python3
@Lewiscowles1986

This comment has been minimized.

Lewiscowles1986 commented Aug 4, 2017

@mabushey here's what I'm doing (we need python3 for other things)

---
- hosts: all
  gather_facts: true
  vars:
  pre_tasks:
  - name: install python
    raw: bash -c "test -e /usr/bin/python || (apt -qqy update && apt install -qqy python python-pip python3 python3-pip)"
    register: output
    changed_when: output.stdout != ""
  tasks:

This seems to work for us on ubuntu stock and cloud images from various providers

@sebitoelcheater

This comment has been minimized.

sebitoelcheater commented Aug 18, 2017

I created many Digitalocean Droplets and all of them failed when tried to run ansible playbook (even after installing python with apt-get!). Adding this script made it works. Why?

@IuryAlves

This comment has been minimized.

IuryAlves commented Aug 28, 2017

Awesome!! Thanks

@inkatze

This comment has been minimized.

inkatze commented Sep 18, 2017

Thanks!

@iDemonix

This comment has been minimized.

iDemonix commented Sep 18, 2017

I spun up 4x new Ubuntu 17.04 VMs and tried to do the following:

  pre_tasks:
  - name: Install python2 for Ansible
    raw: bash -c "test -e /usr/bin/python || (apt -qqy update && apt install -qqy python-minimal)"
    register: output
    changed_when: output.stdout != ""
  - name: Install pip
    apt:
      name: python-pip
      state: present
fatal: [209.222.2.256]: FAILED! => {"changed": false, "failed": true, "module_stderr": "Shared connection to 209.222.2.256 closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n  File \"/root/.ansible/tmp/ansible-tmp-1505758704.28-42062379270360/apt.py\", line 17, in <module>\r\n    import zipfile\r\nImportError: No module named zipfile\r\n", "msg": "MODULE FAILURE", "rc": 0}

The python installation worked, but the pip installation failed, as the apt module required zipfile. I couldn't install pip using apt-get, and it was proving a pain to get it another way, so I swapped python-minimal in the above for python, which has the module, and it ran fine.

@philipobrien

This comment has been minimized.

philipobrien commented Sep 22, 2017

Using the suggestions above doesn't seem to be affecting the changed status for me

---
- hosts: all
  gather_facts: False
  become: yes
  pre_tasks:
    - name: Install python for Ansible
      raw: bash -c "test -e /usr/bin/python || (apt -qqy update && apt install -qqy python-minimal)"
      register: output
      changed_when: output.stdout != ""
    - name: Gathering Facts
      setup:

running it repeatedly keeps producing

PLAY [all] ***************************************************************************************************************************************************

TASK [Install python for Ansible]
changed: [xx.xx.xx.xxx]
@DanLipsitt

This comment has been minimized.

DanLipsitt commented Sep 22, 2017

Where are people putting this? I wanted to use it in an include in roles/common/main.yml, so that it can be shared between playbooks, but it seems to need to be in the playbook itself.

@dpetukhov

This comment has been minimized.

dpetukhov commented Oct 13, 2017

By the way, if you're using Vagrant, you can run shell provision before ansible. Here is my Vagrant file example:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|
  config.vm.box = "ubuntu/xenial64"

  config.vm.provider "virtualbox" do |vb|  
    vb.customize ["modifyvm", :id, "--cpus", "1"]
    vb.customize ["modifyvm", :id, "--memory", "768"]
    vb.name = "novoansible"
  end

  # Install python for ansible
  config.vm.provision "shell", inline: <<-SHELL
    test -e /usr/bin/python || (apt -qqy update && apt install -qqy python-minimal)
  SHELL
end
@okamos

This comment has been minimized.

okamos commented Oct 14, 2017

Thanks a lot 😄

@VaesNick

This comment has been minimized.

VaesNick commented Dec 6, 2017

@DanLipsitt you can make a role for it however you will need to diasable fact gathering for every playbook you use it in, and use the setup module afther to gather facts.

@tmugford

This comment has been minimized.

tmugford commented Dec 16, 2017

Great work all. Just on the question of notifying Ansible when a change has occurred, I found the following worked correctly for me without having to resort to string comparison.

- name: Install Python
  hosts: all
  gather_facts: false
  tasks:
    - name: Install Python 2.x
      raw: test -e /usr/bin/python || (apt update && apt install -y python-simplejson)
      register: test
      changed_when: test.stdout

Using Ansible 2.4.2.0 (macOS) against Ubuntu 16.04.3 (running on Digital Ocean).

@tlc

This comment has been minimized.

tlc commented Apr 19, 2018

Anyone have a good solution for proxies? (Raw doesn't use the environment.) I can use this.

  - name: Install python2 for ansible
    raw: export http_proxy=http://proxy.example.com:8000/; export https_proxy=http://proxy.example.com:8000/; test -e /usr/bin/python || (apt-get -y update && apt-get install -y python-minimal)
    args:
      executable: /bin/bash
    register: test
    changed_when: test.stdout

But I'd like to make the proxies optional.

@zoredache

This comment has been minimized.

zoredache commented Apr 20, 2018

@tlc, you could almost certainly use jinja expressions in what you pass to raw:

raw: |
  {% if foo %}
  http_proxy={{foo}}
  {% endif %}
  etc....
@muawiakh

This comment has been minimized.

muawiakh commented May 7, 2018

I use the following snippet, which can work across different distributions and only expects python to be installed, which is mostly the case with default ubuntu boxes:

  pre_tasks:
    - raw: which /usr/bin/python || which /usr/bin/python3
      register: py_interpreter
      changed_when: py_interpreter.stdout != ""
    - set_fact:
        ansible_python_interpreter: "{{ py_interpreter.stdout }}"
    - name: Gathering Facts
      setup: #aka gather_facts
@dmonagha

This comment has been minimized.

dmonagha commented Jun 22, 2018

Thanks @dpetukhov

@DeanKamali

This comment has been minimized.

DeanKamali commented Oct 10, 2018

ansible -i /path/to/inventory all -m raw -a "apt -y update && apt install -y python"

@ygotame

This comment has been minimized.

ygotame commented Oct 17, 2018

I needed to install also pyton-setuptools to avoid the error

raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal python-setuptools)

@C-Bam

This comment has been minimized.

C-Bam commented Oct 26, 2018

I don't know if that is possible but if CentOS or RHEL comes without python, I did this:

raw: test -e /usr/bin/python || (yum -y update && yum install -y python) || (apt -y update && apt install -y python-minimal)

@hitme

This comment has been minimized.

hitme commented Nov 3, 2018

gather_facts: False pre_tasks: - name: Install python for Ansible raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal) changed_when: False - setup: # aka gather_facts

works for me, thanks.

@SerenaFeng

This comment has been minimized.

SerenaFeng commented Dec 7, 2018

Thanks, a great help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment