CertGrid CertGrid
Installation·Python Automation for IT

Python Automation Practice Lab

One control node where scripts run and two hosts they act on. The second target is not padding: with a single remote host an inventory loop runs once and every partial-failure case disappears, which is most of what makes automation hard. This guide also sets up the two things the rest of the path assumes - a virtual environment, because Ubuntu refuses to let pip near the system Python, and key-based SSH to both targets.

Start Here Guide 2 of 39 Beginner

Written against the versions above. The control node runs **Ubuntu 26.04 with Python 3.14.4**; the managed hosts run **RHEL 10 with Python 3.12.9**. That mismatch is deliberate and is the normal case in real work - a script written on one is not guaranteed to run on the other, and this path says so wherever it matters.

One control node and two managed hosts. The targets keep the hostnames the RHCSA path gave them - they are ordinary Linux boxes reachable over SSH, and nothing here is Red Hat specific.
Server NameIP AddressOSRolesCPURAMHDD
RUNNER01192.168.0.27Ubuntu 26.04 LTSControl node - every script in this path runs here2 Core4 GB50 GB
RHCSA-A01192.168.0.31RHEL 10.0Managed host - reached over SSH from the control node2 Core4 GB50 GB
RHCSA-B01192.168.0.33RHEL 10.0Second managed host - so an inventory has more than one row2 Core4 GB50 GB

Before you start

  1. The control node

    One machine where every script in this path runs. It needs Python and nothing else - no agent, no daemon, no privileged access to anything.

    That is the control node pattern, and it is worth naming because it is how Ansible, Salt and most CI runners work too: one place holds the code and the credentials, and the machines being managed hold nothing.

    bash Example session
    hostname; python3 --version; cat /etc/os-release | head -2ahm-runner01Python 3.14.4PRETTY_NAME="Ubuntu 26.04 LTS"NAME="Ubuntu"mkdir -p ~/auto && ls -d ~/auto/home/sysadmin/auto

    Expected resultahm-runner01, Python 3.14.4, Ubuntu 26.04, and a scratch directory.

    Success conditionYou know which machine the scripts run on.

  2. Why the system Python refuses to install anything

    First real obstacle, and it is not a misconfiguration. Ubuntu 26.04 marks its system Python as externally managed - PEP 668 - and pip install into it is refused outright.

    bash Example session
    python3 -m pip install requests 2>&1 | head -8error: 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,ls -l /usr/lib/python3.14/EXTERNALLY-MANAGED-rw-r--r-- 1 root root 645 Jun 18 14:25 /usr/lib/python3.14/EXTERNALLY-MANAGED

    Expected resulterror: externally-managed-environment, and the marker file that causes it.

    Success conditionYou can recognise this error and know it is not your fault.

  3. The virtual environment

    A directory with its own site-packages and its own python. Nothing is activated and nothing needs to be - the path's scripts invoke ~/autoenv/bin/python directly, which is also the only way a cron job or a systemd unit can invoke it.

    bash Example session
    ls -d ~/autoenv && ~/autoenv/bin/python --version/home/sysadmin/autoenvPython 3.14.4~/autoenv/bin/python -c "import requests, paramiko, pytest, yaml; print('requests', requests.__version__); print('paramiko', paramiko.__version__); print('pytest', pytest.__version__); print('PyYAML', yaml.__version__)"requests 2.34.2paramiko 5.0.0pytest 9.1.1PyYAML 6.0.3~/autoenv/bin/python -c "import sys; print('sys.prefix     ', sys.prefix); print('sys.base_prefix', sys.base_prefix)"sys.prefix      /home/sysadmin/autoenvsys.base_prefix /usr

    Expected resultThe four dependencies with their versions, then sys.prefix pointing at the venv while sys.base_prefix still points at /usr.

    Success conditionYou have somewhere to install things.

  4. The two managed hosts

    Two ordinary Linux boxes, reachable over SSH with a key and no password prompt. BatchMode=yes is the flag that makes that testable: it disables every interactive prompt, so the command either works or fails immediately rather than hanging waiting for a password.

    bash Example session
    for ip in 192.168.0.31 192.168.0.33; do ssh -o BatchMode=yes sysadmin@$ip 'echo "$(hostname) $(cat /etc/redhat-release)"'; donerhcsa-a01 Red Hat Enterprise Linux release 10.0 (Coughlan)rhcsa-b01 Red Hat Enterprise Linux release 10.0 (Coughlan)

    Expected resultrhcsa-a01 and rhcsa-b01, both RHEL 10.

    Success conditionThe control node can reach both targets without a prompt.

  5. The targets do not run your Python

    This is the one lab fact most worth carrying forward. The control node has 3.14.4. The targets have 3.12.9.

    bash Example session
    ssh -o BatchMode=yes sysadmin@192.168.0.31 'python3 --version'Python 3.12.9ssh -o BatchMode=yes sysadmin@192.168.0.31 'python3 --version'Python 3.12.9

    Expected resultPython 3.12.9 on the target, Python 3.14.4 on the control node.

    Success conditionYou will not assume a script that runs here runs there.

  6. What is already there

    Eighteen standard library modules, imported on the plain system Python with nothing installed. This is most of the track.

    bash Example session
    cat > ~/auto/stdlib.py <<'PY'mods = ["pathlib", "csv", "json", "configparser", "tomllib", "sqlite3", "re",        "subprocess", "os", "shutil", "tempfile", "logging", "argparse",        "urllib.request", "unittest.mock", "signal", "shlex", "http.server"] for name in mods:    __import__(name) print(len(mods), "standard library modules imported, nothing installed:")for name in mods:    print("  ", name)PYpython3 ~/auto/stdlib.py18 standard library modules imported, nothing installed:   pathlib   csv   json   configparser   tomllib   sqlite3   re   subprocess   os   shutil   tempfile   logging   argparse   urllib.request   unittest.mock   signal   shlex   http.server

    Expected resultEighteen module names, no errors.

    Success conditionYou know how much needs no dependency at all.

Troubleshooting

Official sources