You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
This final lesson brings together everything you have learned into a practical, end-to-end network automation workflow. We will design a complete system for managing network configuration changes — from planning and development through testing, deployment, and validation.
graph LR
PLAN["Plan — Ticket, Design, Review"] --> DEV["Develop — Data, Template, Git"]
DEV --> TEST["Test — Lint, pytest, Batfish"]
TEST --> DEP["Deploy — Ansible, Nornir, NAPALM"]
DEP --> VAL["Validate — pyATS, Diff, Alerts"]
Every automation workflow starts with understanding what needs to change and why:
| Activity | Description |
|---|---|
| Ticket / Request | Document the change request (e.g., "Add VLAN 100 to all access switches") |
| Impact analysis | Which devices are affected? What could go wrong? |
| Rollback plan | How do you undo the change if it fails? |
| Change window | When will the change be applied? |
| Approval | Get peer review and management sign-off |
graph TD
ROOT["network-automation/"]
ROOT --> INV["inventory/"]
INV --> INV1["production.yaml"]
INV --> INV2["lab.yaml"]
ROOT --> HV["host_vars/"]
HV --> HV1["core-rtr-01.yaml"]
HV --> HV2["access-sw-01.yaml"]
ROOT --> GV["group_vars/"]
GV --> GV1["routers.yaml"]
GV --> GV2["switches.yaml"]
ROOT --> TPL["templates/"]
TPL --> TPL1["base_config.j2"]
TPL --> TPL2["vlan_config.j2"]
TPL --> TPL3["ntp_config.j2"]
ROOT --> PB["playbooks/"]
PB --> PB1["deploy_vlans.yaml"]
PB --> PB2["backup_configs.yaml"]
PB --> PB3["validate_state.yaml"]
ROOT --> TST["tests/"]
TST --> TST1["test_templates.py"]
TST --> TST2["test_inventory.py"]
TST --> TST3["validation.yaml"]
ROOT --> CFG["configs/ (Generated configs for Batfish)"]
ROOT --> BAK["backups/ (Device config backups)"]
ROOT --> REQ["requirements.txt"]
ROOT --> ACFG["ansible.cfg"]
ROOT --> RM["README.md"]
The source of truth contains all the data about your intended network state:
# group_vars/switches.yaml
vlans:
- id: 10
name: SALES
- id: 20
name: ENGINEERING
- id: 30
name: MANAGEMENT
- id: 100
name: GUEST
ntp_servers:
- 10.0.0.50
- 10.0.0.51
dns_servers:
- 10.0.0.52
- 10.0.0.53
syslog_server: 10.0.0.60
snmp_community: readonly_community
{# templates/vlan_config.j2 #}
{% for vlan in vlans %}
vlan {{ vlan.id }}
name {{ vlan.name }}
{% endfor %}
# playbooks/deploy_vlans.yaml
---
- name: Deploy VLANs to switches
hosts: switches
gather_facts: false
tasks:
- name: Backup current configuration
cisco.ios.ios_config:
backup: true
backup_options:
dir_path: ../backups/
filename: "{{ inventory_hostname }}.cfg"
- name: Deploy VLAN configuration
cisco.ios.ios_config:
src: ../templates/vlan_config.j2
save_when: modified
register: config_result
- name: Display changes
ansible.builtin.debug:
msg: "{{ config_result.updates | default('No changes') }}"
# .github/workflows/network-ci.yaml (GitHub Actions)
name: Network CI
on:
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint YAML
run: |
pip install yamllint
yamllint host_vars/ group_vars/ inventory/
- name: Lint Jinja2
run: |
pip install j2lint
j2lint templates/
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run pytest
run: |
pip install -r requirements.txt
pytest tests/ -v
simulation:
runs-on: ubuntu-latest
services:
batfish:
image: batfish/batfish
ports:
- 9997:9997
steps:
- uses: actions/checkout@v4
- name: Analyse with Batfish
run: |
pip install pybatfish
python tests/batfish_analysis.py
| Strategy | Description | Risk |
|---|---|---|
| Canary | Deploy to one device first, validate, then roll out to the rest | Low |
| Rolling | Deploy to devices in batches (e.g., 5 at a time) | Medium |
| Blue-green | Prepare a new config set, switch over atomically | Low |
| Big bang | Deploy to all devices at once | High |
# deploy_canary.yaml
---
- name: Canary deployment - single device
hosts: access-sw-01
gather_facts: false
tasks:
- name: Deploy VLAN config
cisco.ios.ios_config:
src: ../templates/vlan_config.j2
save_when: modified
- name: Validate canary
cisco.ios.ios_command:
commands:
- show vlan brief
register: vlan_output
- name: Check VLANs exist
ansible.builtin.assert:
that:
- "'SALES' in vlan_output.stdout[0]"
- "'ENGINEERING' in vlan_output.stdout[0]"
fail_msg: "VLAN validation failed on canary device!"
success_msg: "Canary validation passed"
- name: Full rollout - all switches
hosts: switches
gather_facts: false
serial: 5 # Deploy 5 devices at a time
tasks:
- name: Deploy VLAN config
cisco.ios.ios_config:
src: ../templates/vlan_config.j2
save_when: modified
# validate_deployment.py
from genie.testbed import load
from genie.utils.diff import Diff
import json
import sys
testbed = load("testbed.yaml")
validation_passed = True
for device_name, device in testbed.devices.items():
print(f"\n{'='*60}")
print(f"Validating: {device_name}")
print(f"{'='*60}")
device.connect(log_stdout=False)
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.