Install Ansible and Connect to Managed Hosts

PythonBeginner
Practice Now

Introduction

You are preparing an Ansible control node to manage two Linux servers. Before writing automation, you need a predictable installation and proof that the controller can execute Python modules on both targets.

In this lab, you will create a project virtual environment, install Ansible Core from an offline package cache, activate it in the Terminal, and use a supplied inventory to test two real SSH connections. Basic command-line familiarity is enough; no previous Ansible experience is required.

Create an Isolated Controller Environment

In this step, you will create the Python environment that will hold your own Ansible installation.

Ansible runs on a control node and connects to managed nodes over SSH. Your LabEx Terminal is the Ubuntu control node. Two prepared Linux containers, web1 and web2, act as separate managed nodes: they have different addresses, hostnames and filesystems, but share the VM's kernel. You will not need Docker commands or changes to the platform login settings.

A Python virtual environment keeps project packages separate from the operating system and the shared course tools. The image already has a shared Ansible command for infrastructure, but your task is to install a separate copy in /home/labex/project/.venv.

Open the Terminal. Use cd to select the project directory; subsequent relative paths are resolved from here:

cd /home/labex/project

pwd prints your current working directory:

pwd

The output should be /home/labex/project. Keep using this directory throughout the lab.

ansible-python selects the prepared course Python. --version displays its version:

ansible-python --version

Expect Python 3.12.15. The control node's system Python remains separate; do not replace it.

The -m venv option runs Python's virtual-environment module. The final .venv argument names the directory to create inside the project. A leading dot makes it hidden from ordinary directory listings:

ansible-python -m venv .venv

This normally finishes without output. It creates the environment and pip, Python's package installer; it has not installed Ansible yet.

Run the new environment's Python by its relative path. This confirms you can use the isolated interpreter without first activating it:

.venv/bin/python --version

It should also print Python 3.12.15. The two Python commands use the same Python release, but packages installed into .venv belong to this project.

Install and Activate Ansible Offline

In this step, you will install the pinned Ansible Core release into .venv and make the Terminal use it.

Ansible Core provides the ansible command and built-in modules used in this course. Prepared wheel files are local Python packages, so installation does not depend on downloading from the internet. The requirements file specifies compatible, pinned versions.

The following command uses the project Python to run pip. install installs packages, --no-index disables online package indexes, --find-links selects the local wheel directory, and -r reads the requirements file:

.venv/bin/python -m pip install --no-index --find-links /opt/labex/ansible-course/wheels -r /opt/labex/ansible-course/requirements.txt

The output lists packages processed from the local cache and ends with Successfully installed ..., including ansible-core-2.21.5. A repeat installation may instead say Requirement already satisfied.

Activation puts the environment's command directory first in this shell's search path. source runs the activation script in the current Terminal, so the change persists for later commands:

source .venv/bin/activate

The prompt may now begin with (.venv). The Terminal uses zsh; rehash refreshes its cached command locations after installing new executables:

rehash

command -v shows which executable a command name selects:

command -v ansible

Expect /home/labex/project/.venv/bin/ansible. A shared path such as /usr/local/bin/ansible does not prove your project installation is active. If you open a new Terminal, return to the project, activate again, and run rehash.

Now inspect the running Ansible version:

ansible --version

Look for these fields among the output:

ansible [core 2.21.5]
  config file = /home/labex/project/ansible.cfg
  ...
  ansible python module location = /home/labex/project/.venv/lib/python3.12/site-packages/ansible
  executable location = /home/labex/project/.venv/bin/ansible
  python version = 3.12.15 ...
  ...

The executable and module paths show that you are running the project installation. The configuration file is supplied by the lab; you will learn to configure it in the next capability group.

Finally, pip's check command tests whether installed package dependencies are compatible:

.venv/bin/python -m pip check

Expect No broken requirements found. You now have a working controller without changing system packages or the shared toolchain.

Connect to Both Managed Nodes

In this step, you will inspect the prepared inventory and run a Python-based connection test on both SSH targets.

An inventory tells Ansible which hosts it can manage. cat prints a file's contents; read the supplied project inventory:

cat inventory.ini

Its shape is:

web1 ansible_host=...
web2 ansible_host=...

[all:vars]
ansible_user=automation
ansible_ssh_private_key_file=/home/labex/.local/share/ansible-course/id_ed25519
ansible_python_interpreter=/usr/bin/python3

The actual addresses replace ...; use the existing values. web1 and web2 are inventory names, while ansible_host gives each SSH destination. [all:vars] applies the following connection settings to both hosts. automation is the ordinary account inside the managed nodes, not your control-node labex login. The lab supplies a fresh training key and trusted host keys; you do not need to generate keys or change SSH settings.

The target Python path /usr/bin/python3 belongs to each managed node. It is different from the controller's project Python: most Ansible modules need Python on the target as well as on the controller.

The supplied ansible.cfg selects this inventory when commands run from the project. all selects every inventory host; --list-hosts previews that selection without running a module:

ansible all --list-hosts

Expect exactly these two host names; their order may differ:

  hosts (2):
    web1
    web2

The -m option selects a module, a unit of Ansible work. ansible.builtin.ping is the fully qualified name of the built-in ping module. It connects over SSH and runs Python on each target; it is not a network ICMP ping:

ansible all -m ansible.builtin.ping

Both hosts should return results with these stable fields; result order may differ:

web1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
web2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

A pong proves that SSH authentication and target Python execution worked for that host. changed: false means this connection test did not request a configuration change. One successful host is not enough: look for both names. UNREACHABLE means the connection failed; FAILED means a task failed. An empty host list also does not demonstrate connectivity.

If the command fails, first check that you are still in the project directory, that command -v ansible selects .venv/bin/ansible, and that the supplied inventory still contains both original destinations. Keep the platform's login, SSH and sudo configuration unchanged.

Summary

You created an isolated controller environment, installed pinned Ansible Core packages from an offline cache, activated the project commands, and checked their paths and dependencies. You read the supplied inventory and received a Python pong from each distinct SSH-managed node. You can now distinguish the controller Python, target Python and inventory names before learning project configuration.