In this lab we step away from copy and paste and start writing Ansible host files and playbooks from scratch. This is where things start to click – once you understand the structure and can build it yourself, you can automate anything.
The lab is scenario-based. Your company has deployed four new Cisco routers across four pop sites and your task is to get them configured and verified using Ansible before they go live. The routers are not passing customer traffic yet, which makes this a realistic first-use scenario – get the automation in place before the network is live rather than scrambling to apply changes manually once it is.
We work through four tasks in sequence.
The first task is building the host inventory file from scratch in VS Code rather than copying a pre-built one. We cover the INI format for inventory files, how to define individual hosts with their management IP addresses under a named group, and why using group variables is the right approach rather than repeating credentials on every single host line. You will see exactly how ansible_host, ansible_user, ansible_ssh_pass, ansible_port, ansible_connection and ansible_network_os are defined in the vars block and what each one does.
The second task introduces Ansible ad hoc commands – single-line commands you run directly from the terminal without writing a playbook. We use the ping module to test connectivity to individual devices and then to the entire inventory at once. It is important to understand that the Ansible ping module is not an ICMP ping. It establishes a full SSH session using the credentials in your host file and sends an application-level ping. If you get a pong back you know SSH is working, authentication is good and Ansible can reach the device – which means any playbook you run against that host should be able to connect without issue.
The third task is writing a playbook to pull the routing table from all four routers simultaneously using the cisco.ios.ios_command module. We cover the full playbook structure in YAML – the three-dash file header, name, hosts, gather_facts, tasks, the command block and how to use register to capture command output into a variable. We then add a second task using the debug module to print that output back to the terminal using stdout_lines. Without this second task the command runs but you see nothing, which is a common point of confusion when starting out with Ansible.
Running the playbook reveals a real problem – routers three and four are missing a default static route and have no gateway of last resort set, meaning they cannot reach anything outside their directly connected networks. This is exactly the kind of issue automation helps you find quickly across multiple devices at once rather than logging into each one individually.
The fourth task is writing a second playbook to fix the problem – using the cisco.ios.ios_config module to push a default static route to all four routers. We re-run the routing table playbook after applying the fix to confirm routers three and four now show a gateway of last resort. We also see idempotency in action again – routers one and two already had the static route in place so Ansible reports no change on those devices while making the correction on the others.
We also cover a YAML indentation error mid-lab – the playbook fails on the first run due to incorrect indentation and we work through fixing it. YAML is whitespace-sensitive and indentation errors are the most common cause of playbook failures, so seeing one happen and understanding how to read the error output is a genuinely useful part of the lesson.
By the end of this lab you will be able to write an Ansible inventory file and a working playbook entirely from scratch, use ad hoc commands to verify connectivity before running playbooks, pull structured output from network devices and display it in the terminal, push configuration changes using ios_config, and confirm that changes have been applied correctly.
Grab the files at the GitHub lab 2 repo