The Chaos of the Multi-Vendor Network
Last year, our infrastructure team hit a breaking point. We were managing a backbone of 40 Juniper MX routers, 150 Cisco Catalyst switches, and 72 remote branch offices running Mikrotik CCRs. Every time we needed to provision a new VLAN or update an NTP server, three different engineers had to log into three separate CLI environments. The syntax for a Cisco description differs from a Juniper description, which is worlds apart from a Mikrotik comment.
Simple typos caused frequent outages. A missed commit on a Juniper box or a forgotten write memory on a Cisco switch meant configurations vanished after a reboot. Manual management wasn’t just slow; it was a liability. We needed to stop treating our routers like pets and start treating them like code.
Why Manual CLI and Custom Scripting Hit a Ceiling
Our first attempt at a solution involved custom Python scripts using Paramiko and Netmiko. These libraries are powerful, but we quickly found ourselves maintaining over 1,400 lines of fragile boilerplate code. We were spending more time handling SSH timeouts and vendor-specific error parsing than actually managing the network. We weren’t building a network; we were building a software application just to keep the lights on.
The real bottleneck was the lack of an abstraction layer. In a mixed environment, your “intent”—such as setting a DNS server to 8.8.8.8—is universal. However, the implementation is fragmented across vendors. We needed a tool that defined the desired state of the network without requiring us to be syntax experts for every single operating system.
Choosing the Right Tooling: Python vs. Ansible
We evaluated three primary paths:
- Manual CLI: No upfront cost, but high operational risk and zero scalability.
- Custom Python Scripts: Highly flexible, but they require professional-grade programming skills that junior admins often lack.
- Ansible: Agentless, uses human-readable YAML, and features a massive ecosystem of Network Modules maintained directly by vendors.
Ansible won because it separates data (like IP addresses and VLAN IDs) from logic (the specific commands needed to apply them). This separation is where the combination of Ansible Network Modules and Jinja2 templates becomes a critical advantage.
The Production Workflow: Modules + Templates
After six months in a live environment, our workflow has settled into a clean, three-tier architecture. This approach shifts your focus from “how do I type this?” to “what should the network look like?”
1. Defining a Multi-Vendor Inventory
Ansible needs to know which driver to use for each device. We define this in our hosts.ini. By setting the ansible_network_os variable, we tell Ansible exactly which module set to load for each hardware type.
[campus_switches]
sw-cisco-01 ansible_host=10.0.1.10 ansible_network_os=cisco.ios.ios
sw-juniper-01 ansible_host=10.0.1.20 ansible_network_os=junipernetworks.junos.junos
sw-mikrotik-01 ansible_host=10.0.1.30 ansible_network_os=community.general.routeros
2. Abstracting Configurations with Jinja2
We avoid vendor-specific playbooks by using Jinja2 templates. We create one YAML data structure and map it to the specific syntax of each vendor. For example, consider a standard system banner.
First, we define the variables in group_vars/all.yml:
system_banner: "Authorized Access Only. All activities are logged."
snmp_community: "itfromzero_readonly"
Then, we create specific templates. Here is the Cisco version (templates/cisco_ios.j2):
banner motd ^
{{ system_banner }}
^
And the Juniper version (templates/juniper_junos.j2):
set system login message "{{ system_banner }}"
3. The Playbook: Where Everything Connects
The playbook uses specialized config modules to push the rendered templates. These modules are idempotent. They check the current state and only send commands if the device configuration doesn’t match your template.
- name: Deploy Multi-Vendor System Config
hosts: campus_switches
gather_facts: false
tasks:
- name: Push Cisco Configuration
cisco.ios.ios_config:
src: templates/cisco_ios.j2
when: ansible_network_os == 'cisco.ios.ios'
- name: Push Juniper Configuration
junipernetworks.junos.junos_config:
load: merge
src: templates/juniper_junos.j2
when: ansible_network_os == 'junipernetworks.junos.junos'
- name: Push Mikrotik Configuration
community.general.routeros_command:
commands:
- /system identity set name={{ inventory_hostname }}
- /snmp community set [find default=yes] name={{ snmp_community }}
when: ansible_network_os == 'community.general.routeros'
Hard-Won Lessons from the Field
Moving to this model wasn’t perfectly smooth. If you are starting this journey, keep these four points in mind:
- Trust but verify with
check_mode: Always run playbooks with--checkfirst. This performs a dry run, showing you the exact diff of what would change without touching the production traffic. - Fact gathering is a performance killer: Network devices are slow. Disable
gather_factsby default unless you specifically need hardware serial numbers. This single change reduced our execution time by nearly 40%. - Lock down your secrets: Never store SSH passwords or SNMP strings in plain text. Use
ansible-vaultto encrypt sensitive variables. It takes five minutes to set up and prevents major security leaks. - Standardize your naming conventions: Automation works best when interface names follow a strict pattern. If one switch uses
GigabitEthernet0/1and another usesge-0/0/0, your Jinja2 logic will quickly become a mess of nested if-statements.
The Results: Infrastructure as Code
The biggest transformation wasn’t the software—it was our mindset. We no longer “log into switches.” Instead, we modify a YAML file, submit a Git Pull Request, and let a CI/CD pipeline trigger the Ansible playbook. This creates a perfect audit trail of every change.
By leveraging Ansible’s specialized modules, we slashed our deployment time for new branch offices from two days to just twenty minutes. More importantly, we eliminated the human error responsible for 90% of our previous downtime. If you are still managing multi-vendor environments manually, you are essentially waiting for a disaster. Start small by automating something simple, like your DNS settings. The benefits will be obvious by the end of the week.

