CertGrid CertGrid
Concepts·Python Automation for IT

Python Automation for IT Overview

Python Automation for IT is a practical track rather than a certification: five domains, a 787-question pool, a 50-question mock and a 70% readiness threshold, all of it CertGrid's own. It assumes you can already read Python and asks whether you can make a machine do something with it. This page sets out the five domains and then runs one script that touches all five, so the destination is visible before the first lesson.

Start Here Guide 1 of 39 Beginner

Written against the versions above. Written against **Python 3.14.4** on the control node and **RHEL 10** on the managed hosts. Almost nothing here is version-sensitive - the standard library modules this track uses have been stable for years - and where a release matters the guide names it.

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 five domains, and what each is worth

    | Domain | Questions | Weight | |---|---|---| | 1. Python Scripting Foundations for Automation | 153 | 19% | | 2. Files, Data Formats, and Text Processing | 163 | 21% | | 3. OS, Process, and Task Automation | 158 | 20% | | 4. APIs, Web, and Cloud Automation | 159 | 20% | | 5. Network Automation, Testing, and CI/CD | 154 | 20% |

    787 questions in the pool, and the weights are almost flat. That is unusual and it changes how to study: there is no 34%-sized section to over-serve the way PCAP's object-oriented block demands. Five roughly equal subjects, and being weak in any one of them costs about the same as being weak in any other.

    The mock is 50 questions with a 70% readiness threshold - 35 correct, so fifteen wrong. Across five domains that is three per domain before it starts to hurt.

    The machine this all runs on is one control node with two hosts to act on, and guide 2 is the next guide.

    bash Example session
    hostname; python3 --version; cat /etc/os-release | head -2ahm-runner01Python 3.14.4PRETTY_NAME="Ubuntu 26.04 LTS"NAME="Ubuntu"

    Expected resultThe control node, its Python, and its distribution.

    Success conditionYou know how the material is weighted and what passing the mock costs.

  2. What it does not ask

    This is worth saying early, because it decides whether the track is for you.

    It does not ask what code prints. PCEP and PCAP are reading exams: given a snippet, name the exception, count the iterations, spot the syntax error. Nothing here works that way.

    It assumes the language. Functions, dictionaries, comprehensions, try/except, classes when they help - all taken as read. If any of that is shaky, do guide 1 first; it is cheaper than being lost here.

    It is about scripts that run unattended. Which is a different skill from writing code that works when you are watching it. A script that runs at 3am has no terminal to print to, no one to answer a prompt, and a caller that only sees an exit code - so logging, exit codes, timeouts and partial failure are the subject rather than the housekeeping.

    That last point is the whole track, and the next step is what it looks like.

    bash Example session
    mkdir -p ~/auto && ls -d ~/auto/home/sysadmin/auto

    Expected resultA scratch directory. Everything this path writes goes here and is removed again.

    Success conditionYou know whether this track is the one you want.

  3. One script, all five domains

    diskcheck.py reports free disk across an inventory of hosts. It is 65 lines, it uses one third-party library (none - all standard library), and every domain in the table above appears in it.

    Read the imports first: each one is a later track.

    bash Example session
    mkdir -p ~/auto/tour && cat > ~/auto/tour/inventory.json <<'JSON'{  "hosts": [    {"name": "rhcsa-a01", "address": "192.168.0.31"},    {"name": "rhcsa-b01", "address": "192.168.0.33"},    {"name": "decommissioned", "address": "192.168.0.99"}  ]}JSONcat > ~/auto/tour/diskcheck.py <<'PY'"""Report free disk on every host in an inventory. All five domains, one script."""import argparse           # domain 1: argumentsimport json               # domain 2: data formatsimport logging            # domain 1: loggingimport subprocess         # domain 3: processesimport sysimport urllib.request     # domain 4: APIsfrom pathlib import Path  # domain 2: files log = logging.getLogger("diskcheck")  def free_percent(address, timeout):    """domain 5: run a command on a remote host and parse what comes back."""    out = subprocess.run(        ["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=%d" % timeout,         "sysadmin@" + address, "df --output=pcent / | tail -1"],        capture_output=True, text=True, timeout=timeout + 10,    )    if out.returncode != 0:        raise RuntimeError(out.stderr.strip().splitlines()[-1])    return int(out.stdout.strip().rstrip("%"))  def report(url, payload):    """domain 4: send the result somewhere."""    body = json.dumps(payload).encode()    req = urllib.request.Request(url, data=body,                                headers={"Content-Type": "application/json"})    with urllib.request.urlopen(req, timeout=5) as resp:        return resp.status  def main():    ap = argparse.ArgumentParser(description="Check free disk across an inventory.")    ap.add_argument("inventory", type=Path)    ap.add_argument("--api", default="http://127.0.0.1:8000/reports")    ap.add_argument("--timeout", type=int, default=5)    ap.add_argument("--verbose", action="store_true")    args = ap.parse_args()     logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO,                        format="%(levelname)-7s %(message)s")     hosts = json.loads(args.inventory.read_text())["hosts"]    log.info("checking %d host(s)", len(hosts))     failed = 0    for host in hosts:        try:            used = free_percent(host["address"], args.timeout)        except Exception as exc:                      # domain 3: partial failure            log.error("%-14s unreachable: %s", host["name"], exc)            failed += 1            continue        log.info("%-14s %d%% used", host["name"], used)        log.debug("posting result for %s", host["name"])        report(args.api, {"host": host["name"], "used_percent": used})     log.info("%d ok, %d failed", len(hosts) - failed, failed)    return 1 if failed else 0            # domain 1: an exit code a caller can act on  if __name__ == "__main__":    sys.exit(main())PYwc -l ~/auto/tour/diskcheck.py65 /home/sysadmin/auto/tour/diskcheck.py

    Expected resultAn inventory of three hosts and a 65-line script.

    Success conditionYou have seen the shape of the thing this track builds towards.

  4. Run it

    Two hosts answer, one does not, and the exit status says so.

    bash Example session
    cd ~/auto/tour && python3 diskcheck.py inventory.json; echo "exit status: $?"INFO    checking 3 host(s)INFO    rhcsa-a01      11% usedINFO    rhcsa-b01      11% usedERROR   decommissioned unreachable: ssh: connect to host 192.168.0.99 port 22: No route to hostINFO    2 ok, 1 failedexit status: 1

    Expected resultTwo INFO lines with disk figures, one ERROR, a summary, and exit status 1.

    Success conditionYou can see partial failure handled rather than crashed on.

  5. The same run, with the volume turned up

    --verbose switches the logging level. Note that nothing about the script changed - the log.debug calls were always there and were simply below the threshold.

    bash Example session
    cd ~/auto/tour && python3 diskcheck.py inventory.json --verbose 2>&1 | head -12INFO    checking 3 host(s)INFO    rhcsa-a01      11% usedDEBUG   posting result for rhcsa-a01INFO    rhcsa-b01      11% usedDEBUG   posting result for rhcsa-b01ERROR   decommissioned unreachable: ssh: connect to host 192.168.0.99 port 22: No route to hostINFO    2 ok, 1 failed

    Expected resultThe same lines plus two DEBUG lines showing each result being posted.

    Success conditionYou can change how much a script says without editing it.

  6. Two arguments, two different failures

    argparse wrote the --help output with no work from anybody, and it validates types before your code runs. What it does not do is check that a file exists - so the two failures below come from different places and exit with different statuses.

    bash Example session
    cd ~/auto/tour && python3 diskcheck.py --helpusage: diskcheck.py [-h] [--api API] [--timeout TIMEOUT] [--verbose] inventory Check free disk across an inventory. positional arguments:  inventory options:  -h, --help         show this help message and exit  --api API  --timeout TIMEOUT  --verbosecd ~/auto/tour && python3 diskcheck.py nosuch.json; echo "exit status: $?"Traceback (most recent call last):  File "/home/sysadmin/auto/tour/diskcheck.py", line 65, in <module>    sys.exit(main())             ~~~~^^  File "/home/sysadmin/auto/tour/diskcheck.py", line 45, in main    hosts = json.loads(args.inventory.read_text())["hosts"]                       ~~~~~~~~~~~~~~~~~~~~~~~~^^  File "/usr/lib/python3.14/pathlib/__init__.py", line 788, in read_text    with self.open(mode='r', encoding=encoding, errors=errors, newline=newline) as f:         ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  File "/usr/lib/python3.14/pathlib/__init__.py", line 772, in open    return io.open(self, mode, buffering, encoding, errors, newline)           ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^FileNotFoundError: [Errno 2] No such file or directory: 'nosuch.json'exit status: 1cd ~/auto/tour && python3 diskcheck.py inventory.json --timeout abc; echo "exit status: $?"usage: diskcheck.py [-h] [--api API] [--timeout TIMEOUT] [--verbose] inventorydiskcheck.py: error: argument --timeout: invalid int value: 'abc'exit status: 2

    Expected resultThe help text; then a FileNotFoundError traceback with exit status 1; then an argparse error with exit status 2.

    Success conditionYou can tell an argument error from a runtime error by its exit status.

  7. And with a clean inventory, exit zero

    Same script, an inventory with only the two real hosts.

    bash Example session
    cd ~/auto/tour && python3 diskcheck.py good.json; echo "exit status: $?"INFO    checking 2 host(s)INFO    rhcsa-a01      11% usedINFO    rhcsa-b01      11% usedINFO    2 ok, 0 failedexit status: 0

    Expected resultTwo results, 2 ok, 0 failed, and exit status 0.

    Success conditionThe script reports success as clearly as it reports failure.

  8. How the path is ordered

    Each track is one part of that script, done properly rather than briefly.

    | Track | Domain | What it takes out of diskcheck.py | |---|---|---| | Start Here | all | this track | | Scripting Foundations | 1 (19%) | argparse, logging, the exit code, and the environment that pins it | | Files and Data Formats | 2 (21%) | json, pathlib - and CSV, YAML, INI, TOML and regex | | OS and Process Automation | 3 (20%) | subprocess, and the permissions, signals and scheduling around it | | APIs and Cloud | 4 (20%) | the report call - properly, with auth, paging and retries | | Remote Hosts and CI/CD | 5 (20%) | the SSH, with Paramiko instead of subprocess, plus tests | | Readiness | all | the mock, and the mistakes that fail a script in production | | Command Cheat Sheets | all | two searchable references |

    If you only have time for one track, make it Scripting Foundations. Everything else assumes it, it is the only free one besides this, and its subjects - exit codes, logging, configuration - are what separate a script somebody can schedule from one they cannot.

    guide 2 is next.

    bash Example session
    rm -rf ~/auto/tour && ls -A ~/autolabapi.loglabapi.pidlabapi.py

    Expected resultThe scratch directory back to just the lab's API fixture.

    Success conditionYou know what each track takes on.

Troubleshooting

Official sources