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
- Python3.14.4
- Control nodeUbuntu 26.04 LTS
- Managed hostsRHEL 10.0
- requests2.34.2
- paramiko5.0.0
- pytest9.1.1
- PyYAML6.0.3
- boto3 / botocore1.43.78
- TimeAbout 14 min
- Reviewed24 August 2026
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.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| RUNNER01 | 192.168.0.27 | Ubuntu 26.04 LTS | Control node - every script in this path runs here | 2 Core | 4 GB | 50 GB |
| RHCSA-A01 | 192.168.0.31 | RHEL 10.0 | Managed host - reached over SSH from the control node | 2 Core | 4 GB | 50 GB |
| RHCSA-B01 | 192.168.0.33 | RHEL 10.0 | Second managed host - so an inventory has more than one row | 2 Core | 4 GB | 50 GB |
Before you start
- You can read Python: functions, dictionaries, exceptions, a
forloop. If not, guide 1 is the path for that. - Nothing else. This guide changes nothing.
-
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.
-
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/autoExpected resultA scratch directory. Everything this path writes goes here and is removed again.
Success conditionYou know whether this track is the one you want.
-
One script, all five domains
diskcheck.pyreports 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.pyExpected resultAn inventory of three hosts and a 65-line script.
Success conditionYou have seen the shape of the thing this track builds towards.
-
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: 1Expected resultTwo
INFOlines with disk figures, oneERROR, a summary, and exit status 1.Success conditionYou can see partial failure handled rather than crashed on.
-
The same run, with the volume turned up
--verboseswitches the logging level. Note that nothing about the script changed - thelog.debugcalls 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 failedExpected resultThe same lines plus two
DEBUGlines showing each result being posted.Success conditionYou can change how much a script says without editing it.
-
Two arguments, two different failures
argparsewrote the--helpoutput 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: 2Expected resultThe help text; then a
FileNotFoundErrortraceback 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.
-
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: 0Expected resultTwo results,
2 ok, 0 failed, and exit status 0.Success conditionThe script reports success as clearly as it reports failure.
-
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 ofsubprocess, 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.pyExpected resultThe scratch directory back to just the lab's API fixture.
Success conditionYou know what each track takes on.
Troubleshooting
Expecting exam questions like PCEP's "what does this print?".
Why: Different kind of assessment. This track examines whether a script works unattended, not whether you can read a snippet.
Fix:If you want the reading skill and a certificate, guide 1 is the path. Do it first if the language itself is shaky.
Looking for the certificate this track leads to.
Why: There isn't one. CertGrid publishes it as a vendor-neutral practical track.
Fix:The skills are real and the mock measures them; the credential to pair them with is PCEP or PCAP.
diskcheck.pyreports every host as unreachable.Why: No key-based SSH from the control node to the targets.
Fix:guide 32 sets it up.
ssh -o BatchMode=yes host trueis the one-command test - it must succeed with no prompt.The report call fails with a connection error.
Why: The lab's local API is not running.
Fix:It is a fixture this path starts on the control node - see guide 24. Nothing in this track calls the internet.