Cisco Config Generator
Deep Dive: Cisco Config GeneratorΒΆ
"From Intent Workbook to Production-Ready Configurations."ΒΆ
Version Alignment
This tutorial reflects the current main branch state (May 2026) of Cisco Config Generator and aligns with the current pack model, workbook flow, TUI experience, and headless CLI options.
The Cisco Config Generator turns structured Excel intent into per-device Cisco IOS-XE configuration files using a reusable Python core and Jinja2 templates. It is engineered as a repeatable delivery platform where customer-specific rules live in data and templates, not hardcoded branching logic.
If your team needs "complete transparency", this guide is built for exactly that. Every section explains:
- What happens
- Why it is designed that way
- How to run or verify it in practice
- Where to customise safely
π§ How to Read This Deep DiveΒΆ
This page is written as a tutorial for engineers who want to understand both the output and the code structure behind it. Read it using four lenses:
- What the tool does from workbook ingest through rendered configuration output
- Why the implementation is split this way across CLI, workbook, rendering, and pack layers
- How to run and validate it safely with repeatable commands and review steps
- Where to customise behaviour without breaking the stable core engine
β¨ Why This Tool MattersΒΆ
Most configuration-generation projects become brittle because they mix customer policy directly into Python logic. Cisco Config Generator avoids that trap:
- Intent is data (Excel workbook)
- Policy is data (pack YAML)
- Rendering is code (stable Python + Jinja2)
This separation means you can change site standards quickly without rewriting core engine code.
π― PRIME Philosophy in PracticeΒΆ
1. Transparency by DesignΒΆ
Workbook input, template selection, and rendered output are all traceable. Nothing important is hidden behind opaque generation steps.
2. Hardened for DeliveryΒΆ
Validation happens before rendering, which reduces the chance of bad intent becoming bad production configuration.
3. Policy Before PythonΒΆ
Customer-specific behaviour belongs in packs, YAML, and templates. The engine stays stable while delivery standards evolve.
4. Reusable EngineeringΒΆ
One codebase can serve multiple estates because intent, policy, and rendering layers are cleanly separated.
π§ Tutorial RoadmapΒΆ
Follow this page in order for a practical, end-to-end understanding:
- Understand the architecture and runtime flow.
- Run the tool once with the sample workbook.
- Learn each workbook sheet and how values map to rendered CLI.
- Understand packs and templates so customisation is controlled.
- Use headless mode and tests for repeatable operations.
- Apply troubleshooting and safety checks before production rollout.
π Transparency ContractΒΆ
To align with Nautomation Prime principles, this deep dive explicitly covers:
- System boundaries and data flow
- Inputs, outputs, and transformation logic
- Operational commands that can be executed as shown
- Common failure modes and recovery paths
- Safe extension points for customer-specific behaviour
π§± Project ArchitectureΒΆ
Cisco-Config-Generator/
ββ cisco_config_generator/ # Core Python package
β ββ workbook/ # Workbook parsing and validation
β ββ rendering/ # Jinja2 rendering and output writer
β ββ tui/ # Interactive Textual UI
ββ packs/ # Customer packs (YAML + templates)
ββ scripts/ # Utilities (e.g. workbook generation)
ββ assets/ # Sample and generated workbooks
ββ output/ # Generated configuration files
ββ tests/ # pytest suite
ββ setup.bat # First-time setup (portable Python runtime)
ββ run.bat # Daily launcher
Runtime FlowΒΆ
graph TD
A[run.bat or python -m cisco_config_generator] --> B[Load selected pack]
B --> C[Read and validate workbook]
C --> D[Build per-device context]
D --> E[Render Jinja2 templates]
E --> F[Write output/hostname.cfg]
F --> G[Operator review and lab validation]
Why This DesignΒΆ
- Pack isolation keeps customer policy modular.
- Workbook validation first prevents half-generated outputs from bad data.
- Per-device rendering makes outputs deterministic and easy to review.
𧬠Code Walkthrough: What the Python Layers Actually Do¢
This is the part that turns the generator from a useful tool into a reusable engineering pattern. The value is not only that it emits .cfg files, but that the code is split so each layer does one job clearly and predictably.
1. Entry Layer: __main__.py and cli.pyΒΆ
The package entry point is intentionally minimal: __main__.py imports main() from cli.py and exits. That tells you immediately where real control flow starts.
cli.py then owns only process concerns:
- parsing
--pack,--workbook,--output,--no-tui, and--version - deciding whether to launch the Textual interface or the headless execution path
- requiring
--workbookonly when running without the TUI - routing both modes into the same orchestration layer
Why this matters:
- the CLI stays thin and testable
- interactive mode and automation mode do not diverge into separate products
- exit behaviour is explicit and suitable for both operators and pipelines
That split is a strong production pattern: argument handling should decide how the tool starts, not how network intent is interpreted.
2. TUI Layer: Operator UX Without Engine SprawlΒΆ
The Textual app in tui/app.py is deliberately a front end, not a second implementation of the generator.
Before it runs anything expensive, it performs cheap operator-facing checks:
- workbook path present
- workbook file exists
- selected output path resolved
When the operator presses Run, the app starts a background thread and calls _run_orchestrator(...) rather than running the build directly on the UI thread. Log updates and status changes are pushed back into the interface with call_from_thread(...).
Why this design matters:
- the terminal UI stays responsive while configs are being built
- progress messages can stream live without corrupting the UI state
- validation failures are caught separately from unexpected exceptions, so the operator sees actionable workbook issues instead of a generic crash
This is a reusable pattern for any production automation tool with a terminal UI: the UI should gather intent, validate local inputs, launch the worker safely, and report progress. It should not become the business-logic layer.
3. Workbook Loader: Excel Becomes Typed Intent OnceΒΆ
The workbook loader is where the loose, human-edited spreadsheet is converted into structured Python objects.
The important helpers are small but intentional:
_header_map(...)normalises column names so sheet parsing does not depend on fragile manual indexing_validate_required_headers(...)fails fast if a required workbook column is missing_parse_int_cell(...)raises row-aware and field-aware errors instead of vague conversion failures_cell_value(...)and_list_field(...)centralise common coercion behaviour
The sheet loaders then each do one domain job:
_load_devices(...)_load_vlans(...)_load_interfaces(...)_load_global_settings(...)_load_feature_selection(...)_load_acls(...)
Finally, load_workbook(...) assembles those pieces into one Intent object.
Why this design matters:
- Excel ambiguity is resolved once, near the input boundary
- downstream code works with structured data instead of reopening spreadsheets repeatedly
- error messages stay anchored to the operator's real artefact: workbook sheet, row, and column
That is a core production-grade lesson: turn messy human input into stable internal models as early as possible.
4. Hardware Expansion: Omitted Ports Still Become Explicit StateΒΆ
One of the most important design decisions lives in _expand_interfaces_from_hardware(...).
The generator does not assume that only workbook rows matter. Instead, it builds the full port inventory from:
- the selected device model
- the selected uplink module
- the hardware catalogue
- the available port profiles
It then applies a strict merge policy:
- build the expected inventory for that hardware profile
- preserve any explicit workbook override for a matching interface
- create an
unusedinterface object for every omitted port - keep out-of-inventory rows rather than silently deleting them
Why this design matters:
- operators can document only the exceptions instead of every port manually
- generated output stays deterministic because every interface is accounted for
- shutdown/unused intent is enforced by construction rather than by hope
This is exactly the sort of design choice that separates a demo generator from a delivery-safe one. Missing workbook rows are not treated as "unknown"; they are normalised into a governed default state.
5. Rendering Engine: Templates Stay Declarative, Python Owns ValidationΒΆ
The rendering engine is intentionally tiny:
create_jinja_env(...)builds a Jinja environment withFileSystemLoaderrender_template(...)renders a named template with a prepared context
The most important line is the use of StrictUndefined.
That means if a template references a value that is not present in the context, rendering fails immediately instead of silently producing a partial or broken configuration.
Why this design matters:
- template mistakes are caught during generation, not during change implementation
- pack authors cannot accidentally rely on undefined variables
- the logic boundary remains clean: Python validates and assembles context; Jinja formats output
This is a textbook production choice. Silent template fallback is convenient in prototypes and dangerous in real delivery tooling.
6. Pack and Orchestrator Boundary: Customer Variation Lives in DataΒΆ
Both the CLI path and the TUI path resolve the selected pack, load pack settings, construct an Orchestrator, and call run().
The orchestrator is given the minimum runtime contract it needs:
pack_pathworkbook_pathoutput_dir- optional progress callback
It then returns the written output paths.
Why this design matters:
- the same engine is reused whether the operator clicks through the TUI or runs headless in a script
- customer-specific behaviour stays in YAML and templates under
packs/ - the Python core remains stable while packs vary per customer or per standard
This is the PRIME philosophy in code form: policy and site variation belong in governed data, not in customer-specific Python forks.
7. Failure Model and Safe Extension PathΒΆ
The current code shape makes failure handling and extension more predictable than they would be in a monolithic generator.
Failure handling is layered:
- workbook/path issues are caught near the operator boundary
- validation issues are surfaced as structured
ValidationErrormessages - template/context mismatches fail during rendering rather than producing silent output drift
- unexpected exceptions are logged as tool errors rather than being confused with workbook mistakes
Safe extension points are also clear:
- change templates when output syntax or style changes
- change pack YAML when hardware, defaults, or feature mapping changes
- change workbook content when site intent changes
- change Python loaders/models only when you are introducing a genuinely new intent field or runtime behaviour
In practical terms, if you want to add a new per-port intent value, the clean path is:
- add it to the workbook schema
- parse it in the loader
- carry it through the intent model and template context
- render it in the correct Jinja template
What you should not do is hide business logic directly inside a template. That makes the pack harder to reason about, harder to test, and easier to break silently.
π¦ Requirements and Installation PathsΒΆ
Standard Operator Workflow (Windows)ΒΆ
- Windows 10/11 (64-bit)
- Internet access for first-time setup only
- No system Python needed
:: One-time setup
setup.bat
:: Daily use
run.bat
Developer Workflow (Optional)ΒΆ
If you prefer a normal development environment:
python -m venv .venv
.venv\Scripts\activate
pip install -e .[dev]
π First Run Tutorial (Hands-On)ΒΆ
Step 1: Run setupΒΆ
setup.bat
What this does:
- Downloads portable Python into
python_runtime\ - Installs dependencies inside the project folder
- Leaves system Python and global packages untouched
Step 2: Open the sample workbookΒΆ
Use assets/sample_intent.xlsx first.
Why: it provides a known-good reference for sheet structure and realistic values.
Step 3: Launch the TUIΒΆ
run.bat
In the UI:
- Select pack (usually
defaultfirst). - Select workbook path.
- Run generation.
- Review output summary.
TUI PreviewΒΆ

Why this matters: the TUI gives operators a guided execution path, which reduces avoidable CLI mistakes during day-to-day use.
Step 4: Verify generated filesΒΆ
Expected result:
- Per-device files in
output\ - File naming pattern:
output\<hostname>.cfg
Rendered Output CalloutΒΆ
For the workbook example later in this guide, the generated artefact should conceptually look like this:
output/
βββ BRN1-ACC-01.cfg
! Base Configuration
hostname BRN1-ACC-01
...
! Access Port Configuration
interface GigabitEthernet1/0/10
description Finance desk phone + PC
switchport mode access
switchport access vlan 20
switchport voice vlan 30
...
The exact lines vary with workbook values, feature toggles, and selected pack, but the file-per-device pattern is stable.
Step 5: Run headless mode (automation path)ΒΆ
python_runtime\python.exe -m cisco_config_generator --no-tui --workbook assets\sample_intent.xlsx
Why this matters: headless mode is the path for CI/CD and repeatable, non-interactive runs.
βοΈ CLI Reference and Working ExamplesΒΆ
run.bat [OPTIONS]
python_runtime\python.exe -m cisco_config_generator [OPTIONS]
Options:
-p, --pack TEXT Pack name (folder under packs/) or full path [default: default]
-w, --workbook PATH Path to the intent workbook (.xlsx)
-o, --output TEXT Directory to write generated config files [default: output]
--no-tui Run headless without the interactive TUI
--version Print version and exit
Examples:
:: Default pack, headless mode
python_runtime\python.exe -m cisco_config_generator --no-tui --workbook assets\sample_intent.xlsx
:: Explicit pack and output directory
python_runtime\python.exe -m cisco_config_generator --no-tui --pack default --workbook assets\sample_intent.xlsx --output output
:: Check installed version
python_runtime\python.exe -m cisco_config_generator --version
π Workbook Deep Dive (Every Sheet Explained)ΒΆ
The workbook is the source-of-truth input model.
| Sheet | What It Controls | Why It Exists | Common Pitfall |
|---|---|---|---|
| Devices | Device identity, model, uplink module, timezone metadata | Defines target inventory and hardware assumptions | Model/uplink mismatch to actual hardware |
| Global Settings | Shared controls (NTP, DNS, SNMP, AAA, banners, ACL refs) | Centralises site-wide standards | Referencing ACL names not defined in ACLs sheet |
| VLANs | VLAN IDs, names, descriptions | Builds deterministic VLAN config blocks | Missing VLAN used by interface profiles |
| Interfaces | Per-port intent for non-default behaviour | Keeps workbook concise while allowing exceptions | Assuming omitted interfaces are ignored (they default to unused) |
| ACLs | ACL statements referenced elsewhere | Keeps ACL logic structured and reusable | Invalid action/wildcard combinations |
| Feature Selection | Toggle config sections on/off | Allows phased adoption and scoped output | Forgetting a feature toggle is off |
Key Behaviour: Omitted InterfacesΒΆ
If an interface is not explicitly listed in the Interfaces sheet, the generator still derives full interface inventory from hardware definitions and treats omitted ports as unused/shutdown according to pack defaults.
Why this is important: you can model intent by exception instead of maintaining thousands of spreadsheet rows.
Workbook-to-Config Mapping FlowΒΆ
This is the core data path inside the tool:
graph LR
A[Devices sheet] --> G[Workbook validation]
B[Global Settings sheet] --> G
C[VLANs sheet] --> G
D[Interfaces sheet] --> G
E[ACLs sheet] --> G
F[Feature Selection sheet] --> G
G --> H[Per-device context objects]
H --> I[template_map.yaml chooses render order]
I --> J[Jinja2 templates from packs/default/templates]
J --> K[Rendered config fragments]
K --> L[output/hostname.cfg]
Why this matters: each stage has a clear responsibility. If output is wrong, you can trace whether the problem came from workbook input, validation, context building, template selection, or the template itself.
Worked Example: One Workbook Row to Cisco CLIΒΆ
The example below uses the current default pack and the interfaces_access.j2 template.
Example workbook inputsΒΆ
Devices sheet
| Hostname | Model | Uplink Module |
|---|---|---|
BRN1-ACC-01 |
C9200-24P |
C9200-NM-4X |
Interfaces sheet
| Device | Interface | Profile | Description | Access VLAN | Voice VLAN |
|---|---|---|---|---|---|
BRN1-ACC-01 |
GigabitEthernet1/0/10 |
access-voip |
Finance desk phone + PC |
20 |
30 |
How the generator interprets itΒΆ
Profile = access-voipmaps totemplate_hint: interfaces_accessinport_profiles.yamlinterfaces_accessmaps tointerfaces_access.j2intemplate_map.yamlaccess-voipsetsqos_trust_dscp: true, so the QoS line is renderedAccess VLANandVoice VLANbecomeiface.access_vlanandiface.voice_vlanin template context
Rendered CLI from the default templateΒΆ
interface GigabitEthernet1/0/10
description Finance desk phone + PC
switchport mode access
switchport access vlan 20
switchport voice vlan 30
switchport nonegotiate
load-interval 30
auto qos trust dscp
storm-control broadcast level 1.00 0.70
storm-control multicast level 1.00 0.70
spanning-tree portfast
spanning-tree guard root
spanning-tree bpduguard enable
no shutdown
Why this example is usefulΒΆ
It shows the exact separation of concerns:
- The workbook defines intent
- The port profile chooses behaviour class
- The template map chooses the render path
- The Jinja template emits the final IOS-XE syntax
That separation is what makes the tool explainable, testable, and safe to extend.
π ACL Workflow (Transparent Path)ΒΆ
ACL handling is explicit and validated:
- Define ACL entries in ACLs sheet.
- Reference ACL names in Global Settings.
- Generator validates that referenced ACL names exist.
Example pattern:
ACL_VTY_ACCESS permit 10.0.0.0 0.0.0.255
ACL_VTY_ACCESS deny any
If ACLs are not required for a run, disable ACL rendering via Feature Selection -> ACLs -> No.
π§© Pack System Deep DiveΒΆ
All customer-specific behaviour lives in packs/<name>/.
packs/default/
ββ settings.yaml # Defaults (unused VLAN, native VLAN, log level)
ββ hardware_catalog.yaml # Switch models and uplink modules
ββ port_profiles.yaml # Port profile definitions
ββ template_map.yaml # Maps profiles/features to Jinja2 templates
ββ features.yaml # Feature toggle defaults
ββ templates/
ββ acls.j2
ββ base.j2
ββ vlans.j2
ββ interfaces_access.j2
ββ interfaces_access_server.j2
ββ interfaces_trunk.j2
ββ interfaces_trunk_portchannel.j2
ββ interfaces_trunk_server.j2
ββ interfaces_ap_trunk.j2
ββ interfaces_unused.j2
Safe Customer Onboarding PatternΒΆ
- Copy
packs/default/topacks/<customer-name>/. - Update YAML defaults and mapping files first.
- Modify templates only where policy differs.
- Run against sample workbook.
- Diff output before production adoption.
Why this pattern works: it preserves a stable baseline and makes customer-specific deltas explicit in version control.
π§ Template Context (What Templates Actually Receive)ΒΆ
All Jinja2 templates receive a structured context like:
{
"device": Device,
"vlans": [VLAN],
"interfaces": [Interface],
"global": GlobalSettings,
"hardware": HardwareProfile,
"acls": [ACLEntry],
"features": FeatureSelection,
"settings": {"defaults": {}}
}
Why This MattersΒΆ
- Templates remain declarative.
- Logic stays centralised in Python validation/rendering layers.
- Outputs are predictable across engineers and environments.
ποΈ Hardware Catalogue and Port ProfilesΒΆ
Hardware CatalogueΒΆ
hardware_catalog.yaml defines supported switch models and uplink modules. This drives automatic port inventory derivation.
Port ProfilesΒΆ
Profiles in port_profiles.yaml express intent classes such as:
access-useraccess-voipaccess-ap-trunktrunk-uplinktrunk-uplink-portchannelunused
Why profiles are powerful: they let teams apply standard policy repeatedly without rewriting line-by-line interface config in every workbook row.
π§ͺ Testing, Validation, and Quality GatesΒΆ
Run test suiteΒΆ
python_runtime\python.exe -m pytest tests/ -v
Recommended delivery gate before deploymentΒΆ
- Generate configs in headless mode.
- Run tests.
- Diff outputs against prior baseline.
- Perform lab validation on representative hardware.
- Promote to production only after peer review.
π Security and Operational SafetyΒΆ
- Treat workbooks and generated configs as sensitive operational artefacts.
- Keep repository and output access role-scoped.
- Avoid storing secrets in workbooks where possible.
- Use controlled change windows and peer review before production push.
π οΈ Troubleshooting GuideΒΆ
| Symptom | Likely Cause | Resolution |
|---|---|---|
setup.bat fails |
No internet or restricted proxy | Run from a network with package access or pre-stage dependencies |
| No output files generated | Workbook validation failed or wrong workbook path | Verify workbook path and required sheets/headers |
| Unexpected interface output | Model/uplink selection mismatch | Confirm Devices sheet model/uplink values |
| ACLs not rendered | Feature disabled or ACL names unresolved | Check Feature Selection and ACL name references |
| Pack not found | Wrong --pack value |
Use folder name under packs/ or full path |
β What Good Looks LikeΒΆ
A production-ready generation workflow should have:
- Version-controlled pack files
- Reviewed workbook changes
- Deterministic output diffs between versions
- Test and lab validation evidence
- Clear approval trail before deployment
π Learning OutcomesΒΆ
After completing this tutorial, an engineer should be able to:
- Explain the full intent-to-config pipeline end to end
- Build and validate workbooks confidently
- Extend packs without editing core engine logic
- Run both interactive and headless execution modes
- Troubleshoot common generation failures quickly
- Use headless mode for CI/CD pipelines and repeatable generation jobs
- Validate rendered output in a lab before production deployment
Related ResourcesΒΆ
- Technical Deep Dives β Browse the full set of code-first walkthroughs
- Cisco IOS-XE Compliance Audit Deep Dive β Learn how generated standards are later audited and governed
- Access Switch Port Audit Deep Dive β Study parallel collection, enrichment, and Excel reporting patterns
- PRIME Framework β See the operating model behind the tool design choices
Mission Fit: This deep dive supports the PRIME framework by showing how to turn operational intent into repeatable, governable outputs without sacrificing transparency or maintainability.
Need help applying this in a live Cisco environment?
If you want this pattern implemented, governed, or adapted for your estate, use the contact page to start a discovery conversation or review how Nautomation Prime delivers engagements.