Network Automation with Ansible for Absolute Beginners

This lab brings together everything covered in the series so far and applies it to a real routing protocol deployment. We configure OSPF across eight routers spanning five areas – area zero for the named core routers and areas one through four for the edge devices – using a combination of Jinja2 templates, host variable files and the cisco.ios.ios_config module. By the end of this session you will have a fully automated OSPF deployment that can be extended, modified and re-run without touching the CLI on a single device.

The scenario reflects how automation is actually used in production. Management has requested that OSPF is configured across all routers in preparation for the network going live. Rather than logging into each device individually and typing out the same router ospf stanzas by hand, we build a template once, populate the variables per device in host variable files, and let Ansible do the rest.

The lab works through four tasks.

The first task covers host variable and group variable file structure in detail. At this point in the series the host file has been growing steadily and it is time to adopt the proper Ansible variable hierarchy. Each named router gets its own host_vars file containing its interface details, OSPF process ID, router ID, the networks it will advertise, their wildcard masks and the OSPF area each network belongs to. The lab also covers a get_interfaces script that automates the collection of interface data directly from the devices using Ansible facts and an interface template, generating the host variable file structure automatically rather than building each file by hand. This is the approach used in real environments where you have an inventory system such as NetBox providing source of truth data and you need to translate that into Ansible variable files programmatically.

The variable hierarchy is explained in detail here. Ansible checks variables in this order – playbook variables first, then host_vars files, then group_vars files, then the hosts file itself. Understanding this precedence is what allows you to set a baseline configuration in group_vars and then override individual devices in host_vars without touching the playbook or the group configuration. The NTP override example – where all routers inherit a common NTP server from group_vars but a single router can be given a different server via its host_vars file – makes this concrete and immediately applicable.

The second task is building the Jinja2 template for OSPF. The template file uses the .j2 extension and does not follow YAML syntax – it mirrors the actual IOS configuration commands directly. The template starts with router ospf and pulls the process ID from the ospf_process variable in the host_vars file. It then pulls the router ID. The networks section uses a Jinja2 for loop to iterate over the networks list in the host_vars file, generating a network address wildcard area line for each entry. This is the key difference from how OSPF has been configured manually before – instead of hardcoding the network statements for each device, the template loops through however many networks are defined in the variable file and generates the correct number of lines automatically. The for loop opens with the percent brace syntax and closes with endfor, following standard Jinja2 templating conventions. All variable references use double curly brace syntax throughout.

The third task is writing the OSPF deployment playbook. Rather than specifying interface or protocol configuration directly in the playbook, the entire configuration payload comes from two sources – the host_vars file for the device-specific data and the Jinja2 template for the configuration structure. The playbook uses cisco.ios.ios_config with the src parameter pointing at the template file, and a vars block that passes the ospf_process variable from the host_vars file into the template using Jinja2 notation with a zero index to select the first OSPF process defined. This approach keeps the playbook itself compact and reusable – the same playbook works across all eight routers regardless of their individual OSPF configuration because all the differences live in the variable files.

The fourth task is the OSPF verification playbook using cisco.ios.ios_command to run show ip ospf neighbor and show ip route ospf across all routers simultaneously. The output confirms that OSPF neighbourships have formed correctly, inter-area routes are being learned and advertised, and each edge router is sitting in the correct OSPF area matching its router number. A manual CLI check on one of the edge routers confirms the process ID, router ID, network statements and area assignments all match what was defined in the host_vars file.

By the end of this lab you will understand how to build Jinja2 templates that mirror IOS configuration syntax, how to use for loops in Jinja2 to generate repeated configuration blocks from a list of variables, how to reference host_vars data inside a Jinja2 template via the src and vars parameters in cisco.ios.ios_config, how to apply the Ansible variable hierarchy to manage per-device and per-group configuration separately, and how to automate multi-area OSPF deployment across a mixed edge and core topology without touching the CLI on any device.

All host variable files, group variable files, Jinja2 templates and playbooks from this lab are available on the GitHub repo lab 6 linked in the course resources.

Lab 6: OSPF Configuration with Ansible and Jinja2 Templates
Scroll to top