NetAuto Academy

Your First Network Automation

Putting It All Together: The Complete Project

Beginner15 min read

What you'll learn

  • How the inventory, simulated connection, and validation stages combine into one complete project
  • How to read a multi-function script end to end, stage by stage
  • How to trace the sample inventory through to the exact terminal output and JSON report it produces
  • That this project's structure is exactly what 'Netmiko Fundamentals' builds on, not replaces
  • A small extension challenge to test your own understanding of the whole project
🎓 Teach This Lesson

Listen to a guided explanation of this lesson.

Spoken narration isn't supported in this browser.

Every stage, assembled

Three lessons built three separate pieces: an inventory layer that loads and validates devices, a simulated connection function that stands in for real SSH, and validation logic that turns raw state into a categorized report. This lesson wires all three together into one complete, real script — your first finished network automation tool.

  1. CSV inventorydevices.csv
  2. Load devicescsv.DictReader
  3. Process each devicefor device in valid_devices:
  4. Simulated connectionsimulate_connection(device)
  5. Simulated command / resultNTP + VLAN state returned
  6. Validationcheck_compliance(state)
  7. Categorize resultcompliant / non-compliant / unreachable
  8. JSON reportcompliance_report.json
  9. Terminal summarycounts + where to find the report

No real devices, no Netmiko

This script doesn't connect to anything real — simulate_connection() still stands in for that, exactly as Lesson 3 built it. It's runnable right now, on your own machine, with nothing beyond the Python standard library.

The complete script

network_health_check.py
import csv
import json


def is_valid_device(device):
  """A device record needs at least a hostname and an IP to be usable."""
  return bool(device.get("hostname")) and bool(device.get("ip"))


def simulate_connection(device):
  """
  Stands in for a real SSH connection — this exact function is what
  gets replaced with a real Netmiko call in the next module. Returns
  the device's current NTP and VLAN compliance state, or raises
  ConnectionError if the device can't be reached.
  """
  simulated_state = {
      "SW1": {"reachable": True, "ntp_synced": True, "vlan_compliant": True},
      "SW2": {"reachable": True, "ntp_synced": False, "vlan_compliant": True},
      "SW3": {"reachable": False, "ntp_synced": None, "vlan_compliant": None},
  }

  state = simulated_state.get(device["hostname"])

  if state is None or not state["reachable"]:
      raise ConnectionError(f"{device['hostname']} is unreachable")

  return state


def check_compliance(state):
  """Turn a device's raw state into a plain-language compliance status."""
  if state["ntp_synced"]:
      if state["vlan_compliant"]:
          return "compliant"
      else:
          return "vlan non-compliant"
  else:
      return "ntp non-compliant"


# --- Load and validate inventory ---
with open("devices.csv") as f:
  devices = list(csv.DictReader(f))

valid_devices = []
for device in devices:
  if is_valid_device(device):
      valid_devices.append(device)
  else:
      print(f"Skipping invalid inventory record: {device}")

# --- Process every device ---
results = []

for device in valid_devices:
  print(f"Checking {device['hostname']}...")

  try:
      state = simulate_connection(device)
  except ConnectionError:
      print(f"{device['hostname']}: unreachable")
      results.append({
          "hostname": device["hostname"],
          "ip": device["ip"],
          "status": "unreachable",
      })
      continue

  status = check_compliance(state)
  print(f"{device['hostname']}: {status}")
  results.append({
      "hostname": device["hostname"],
      "ip": device["ip"],
      "status": status,
  })

# --- Write the report ---
with open("compliance_report.json", "w") as f:
  json.dump(results, f, indent=4)

# --- Terminal summary ---
compliant = 0
needs_attention = 0
for result in results:
  if result["status"] == "compliant":
      compliant += 1
  else:
      needs_attention += 1

print()
print("--- Summary ---")
print(f"Checked:         {len(results)} devices")
print(f"Compliant:       {compliant}")
print(f"Needs attention: {needs_attention}")
print("Report written to compliance_report.json")

Save this as network_health_check.py alongside devices.csv and run 'python network_health_check.py' — no installs, no real switches.

Nothing here is new: every function, every loop, every try/except is exactly what the last three lessons built, copied in and called in order.

The sample input

devices.csv
hostname,ip,platform
SW1,10.10.10.1,cisco_ios
SW2,10.10.10.2,cisco_ios
SW3,10.10.10.3,cisco_ios

The same three-device inventory used throughout this module — SW1 fully healthy, SW2 reachable but out of NTP sync, SW3 unreachable.

Each stage, explained

  • Load and validate inventory — csv.DictReader reads devices.csv; is_valid_device filters out any incomplete record before anything tries to use it. (Lesson 2.)
  • Process each device — one loop, visiting every valid device exactly once. (The core "ONE workflow, MANY devices" mechanic from the Python module.)
  • Simulated connection — simulate_connection(device) returns the device's state, or raises ConnectionError — the seam that becomes a real Netmiko call next module. (Lesson 3.)
  • Validation — check_compliance(state) turns raw NTP/VLAN facts into one plain-language status. (Lesson 4.)
  • Categorize result — every device, reachable or not, gets exactly one entry appended to results.
  • JSON report — json.dump(results, f, indent=4) saves the full, categorized result set to compliance_report.json.
  • Terminal summary — a short, human-readable count, so nobody has to open the JSON file just to know whether anything needs attention.
Quick checkIdentify the loop
Which loop in this script is the one that visits every device and decides its final compliance status?
Quick checkIdentify the function
How many functions does this script define, and what does each one do?
Quick checkIdentify the exception handling
Which part of this script prevents SW3 being unreachable from stopping SW1 and SW2 from being checked?

Running it: expected terminal output

terminal
Checking SW1...
SW1: compliant
Checking SW2...
SW2: ntp non-compliant
Checking SW3...
SW3: unreachable

--- Summary ---
Checked:         3 devices
Compliant:       1
Needs attention: 2
Report written to compliance_report.json

One 'Checking...' / result line per device, then a short summary — three checked, one fully compliant, two needing attention (SW2's NTP drift, SW3 being down).

The generated report

compliance_report.json
[
  {
      "hostname": "SW1",
      "ip": "10.10.10.1",
      "status": "compliant"
  },
  {
      "hostname": "SW2",
      "ip": "10.10.10.2",
      "status": "ntp non-compliant"
  },
  {
      "hostname": "SW3",
      "ip": "10.10.10.3",
      "status": "unreachable"
  }
]

Every device from the inventory has exactly one entry — nothing silently dropped, every status traceable back to what simulate_connection() and check_compliance() actually decided for it.

Quick checkIdentify the correct code
Which snippet correctly matches how this script builds one results entry after a successful (simulated) connection?

A small challenge

Try this yourself, without looking at the script above while you predict the answer first:

Add a fourth device to devices.csv — SW4,10.10.10.4,cisco_ios — and add a matching entry to simulated_state inside simulate_connection(): "SW4": {"reachable": True, "ntp_synced": True, "vlan_compliant": False}.

What to change: exactly those two additions — one CSV row, one dictionary entry. Nothing else in the script needs to change.

Expected behavior: the terminal should print a fourth "Checking SW4..." line followed by SW4: vlan non-compliant, compliance_report.json should gain a fourth entry with that status, and the summary should update to Checked: 4 devices, Compliant: 1, Needs attention: 3.

Quick checkScenario
After making the challenge's changes, what would the terminal summary's 'Needs attention' count be?

What's ahead

One function changes; everything else stays

"Netmiko Fundamentals," next, replaces simulate_connection() with a function that opens a real SSH session and runs real commands — everything else in this script (the inventory layer, check_compliance, result collection, the JSON report, the terminal summary) carries over unchanged. You're not about to learn automation from scratch with a real device — you're about to plug a real connection into a tool you already built and understand completely.

Key takeaways

  • This project combines three functions — is_valid_device, simulate_connection, check_compliance — with a main loop, exception handling, and JSON reporting into one complete, runnable tool.
  • Every device gets exactly one categorized result, whether it was compliant, non-compliant, or unreachable — the report and the terminal summary both reflect that honestly.
  • You can trace any device's final status back through the exact function calls that produced it — nothing in this script is a black box.
  • simulate_connection() is the one piece designed to be replaced — everything downstream of it (validation, reporting) doesn't change when a real device enters the picture.
  • This is a complete, working automation tool, built and fully understood before touching any real network hardware.