Effortless Ansible Installation with Pipx & Pip

Posted on Jul 15, 2024 | By Andrei Buzoianu, Elif Samedin | 5 minutes read

Introduction

Installing software can be a daunting task for many, requiring hours of research and painstaking attention to detail. However, there is one tool that can make this process not only easier, but also enjoyable. Yes, we are talking about Ansible.

How about if we need to install Ansible itself? Ready to take the leap into the world of Ansible? Take a deep breath and let’s get started.

This articles walks you through installing Ansible using the officially supported method of installing the Python packages with pip. Ansible is described as being agentless because its design does not require that Ansible agent software execute on the target machine in order for Ansible to unlock its full potential. For us, in this context, it means we only need to install Ansible on the control node, namely our local machine.

Understanding Ansible Packages

Before diving into the command line, it’s important to understand that Ansible’s community packages are distributed in two main ways:

  1. ansible-core: A minimalist package that includes the Ansible language and runtime along with a set of Ansible.Builtin modules.
  2. ansible: A comprehensive “batteries included” package that adds a community-curated selection of Ansible Collections for automating a wide variety of devices.

Why Install Ansible Using pip?

Although Ansible can be installed from different distribution’s package repositories, this approach has limitations. For instance, although Ansible is available from the main Debian repository, it might be outdated. To obtain a more recent version, Debian users often need to use the Ubuntu PPA (Personal Package Archives). Additionally, using system package managers might also make it difficult to maintain numerous Ansible versions at the same time.

To get around these drawbacks, we can install our chosen versions of Ansible using pip, the Python package installer.

Installing Ansible with pip

Let’s start by checking that both Python and pip are installed:

$ python3 -V
Python 3.12.3
$ pip -V
pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.12)

Let’s proceed with installing the complete Ansible package for the current user.

$ pip install --user ansible
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.
    
    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3-full installed.
    
    If you wish to install a non-Debian packaged Python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you. Make sure you have pipx installed.
    
    See /usr/share/doc/python3.12/README.venv for more information.

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.

What sorcery is this? Installing a package globally with pip may result in conflicts with system-managed Python dependencies. This is where the “externally-managed-environment” error comes in.

Simply put, this scenario happens when pip installs packages outside of a virtual environment and a file entitled EXTERNALLY-MANAGED is present in Python’s standard library directory. This generally occurs when the system’s package manager handles Python packages.

To avoid such conflicts, it is best to install Python libraries and apps in a virtual environment wherever possible. With few exceptions, this approach is considered best practice.

Setting up a virtual environment using Python’s venv

$ python3 -m venv venv

Creates a new virtual environment named venv with Python 3, isolating dependencies from the global environment.

$ source venv/bin/activate

Activates the venv virtual environment, causing the shell to access the Python interpreter and libraries.

$ pip install ansible
Collecting ansible
  Using cached ansible-10.1.0-py3-none-any.whl.metadata (8.2 kB)
Collecting ansible-core~=2.17.1 (from ansible)
  Using cached ansible_core-2.17.1-py3-none-any.whl.metadata (6.9 kB)
Collecting jinja2>=3.0.0 (from ansible-core~=2.17.1->ansible)
  Using cached jinja2-3.1.4-py3-none-any.whl.metadata (2.6 kB)
Collecting PyYAML>=5.1 (from ansible-core~=2.17.1->ansible)
  Using cached PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (2.1 kB)
Collecting cryptography (from ansible-core~=2.17.1->ansible)
  Using cached cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl.metadata (5.3 kB)
Collecting packaging (from ansible-core~=2.17.1->ansible)
  Using cached packaging-24.1-py3-none-any.whl.metadata (3.2 kB)
Collecting resolvelib<1.1.0,>=0.5.3 (from ansible-core~=2.17.1->ansible)
  Using cached resolvelib-1.0.1-py2.py3-none-any.whl.metadata (4.0 kB)
Collecting MarkupSafe>=2.0 (from jinja2>=3.0.0->ansible-core~=2.17.1->ansible)
  Using cached MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.0 kB)
Collecting cffi>=1.12 (from cryptography->ansible-core~=2.17.1->ansible)
  Using cached cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB)
Collecting pycparser (from cffi>=1.12->cryptography->ansible-core~=2.17.1->ansible)
  Using cached pycparser-2.22-py3-none-any.whl.metadata (943 bytes)
Using cached ansible-10.1.0-py3-none-any.whl (47.9 MB)
Using cached ansible_core-2.17.1-py3-none-any.whl (2.2 MB)
Using cached jinja2-3.1.4-py3-none-any.whl (133 kB)
Using cached PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (724 kB)
Using cached resolvelib-1.0.1-py2.py3-none-any.whl (17 kB)
Using cached cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl (3.9 MB)
Using cached packaging-24.1-py3-none-any.whl (53 kB)
Using cached cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (477 kB)
Using cached MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (28 kB)
Using cached pycparser-2.22-py3-none-any.whl (117 kB)
Installing collected packages: resolvelib, PyYAML, pycparser, packaging, MarkupSafe, jinja2, cffi, cryptography, ansible-core, ansible
Successfully installed MarkupSafe-2.1.5 PyYAML-6.0.1 ansible-10.1.0 ansible-core-2.17.1 cffi-1.16.0 cryptography-42.0.8 jinja2-3.1.4 packaging-24.1 pycparser-2.22 resolvelib-1.0.1

Installs Ansible in the active venv while keeping its dependencies distinct from the system-wide Python installation.

Installing Ansible with pipx

On certain systems, operating system constraints might prevent Ansible from being installed with pip; in such cases, pipx comes in handy.

At an initial glance, pipx seems to be comparable to pip, as both facilitate the installation of Python packages from PyPI or other indexes. However, pipx sets itself apart by avoiding installation into the system-wide Python interpreter or an active virtual environment. Instead, it creates and manages distinct virtual environments for each package, ensuring that their dependencies are separate from each other.

If pipx isn’t already installed, follow the installation instructions for your specific operating system. For instance, since I’m using Ubuntu 24.04 LTS, here are the steps to install pipx and ansible on my system:

$ sudo apt update && sudo apt install pipx

Let’s now proceed to installing the entire Ansible package within your environment using pipx. This command ensures all dependencies are included:

$ pipx install --include-deps ansible
  installed package ansible 10.1.0, installed using Python 3.10.12
  These apps are now globally available
    - ansible
    - ansible-community
    - ansible-config
    - ansible-connection
    - ansible-console
    - ansible-doc
    - ansible-galaxy
    - ansible-inventory
    - ansible-playbook
    - ansible-pull
    - ansible-test
    - ansible-vault
⚠️  Note: '~/.local/bin' is not on your PATH environment variable. These apps will not be globally accessible until your PATH is updated. Run `pipx ensurepath` to
    automatically add it, or manually modify your PATH in your shell's config file (i.e. ~/.bashrc).
done! ✨ 🌟 ✨

$ pipx ensurepath
Success! Added ~/.local/bin to the PATH environment variable.

Consider adding shell completions for pipx. Run 'pipx completions' for instructions.

You will need to open a new terminal or re-login for the PATH changes to take effect.

Otherwise pipx is ready to go! ✨ 🌟 ✨

And the indisputable evidence:

$ ansible --version
ansible [core 2.17.2]
  config file = None
  configured module search path = ['~/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = ~/.local/lib/python3.10/site-packages/ansible
  ansible collection location = ~/.ansible/collections:/usr/share/ansible/collections
  executable location = ~/.local/bin/ansible
  python version = 3.10.12 (main, Mar 22 2024, 16:50:05) [GCC 11.4.0] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True

Conclusions

Both pip and pipx convey robust methods for installing Ansible, accommodating diverse needs and preferences. Whether you opt for the straightforwardness of pip or the isolation of pipx, you will be able to efficiently use Ansible’s automation capabilities.

By the way, you could use Ansible to… install Ansible. To manage applications installed with pipx on a managed node (Ansible likewise), you can use community.general.pipx module. Why not give Ansible a taste of its own automation medicine?

Happy Ansible’ing!

References