Ansible for Cisco Networks: NX-OS, NDFC and IOS-XE Automation
Introduction
Imagine you need to deploy a VXLAN EVPN fabric across dozens of Nexus switches, configure overlay networks on a Nexus Dashboard Fabric Controller, and push model-driven telemetry subscriptions to a fleet of Catalyst 9000 routers -- all before lunch. Doing this manually through CLI sessions would take days and introduce countless opportunities for human error. This is exactly the problem that Ansible Cisco automation solves. By treating your network infrastructure as code, you can define the desired state of every switch, controller, and router in version-controlled YAML files, then deploy those configurations reliably and repeatably with a single command.
In this article, we will walk through the complete landscape of using Ansible to automate three major Cisco platforms: NX-OS for data center switching, NDFC (Nexus Dashboard Fabric Controller) for fabric orchestration, and IOS-XE for enterprise routing and switching. You will learn how to set up your Ansible environment, understand the key collections and modules available for each platform, build playbooks with roles and variables for a full VXLAN EVPN deployment, and integrate everything into a CI/CD pipeline that moves your network operations from manual CLI work to true Infrastructure as Code.
Whether you are preparing for a CCNP or CCIE data center certification, or simply looking to modernize your network operations, this guide provides the foundational knowledge and practical examples you need to get started.
What Is Infrastructure as Code for Cisco Networks?
Before diving into Ansible specifics, it is important to understand the broader concept that makes network automation powerful: Infrastructure as Code (IaC). IaC is the practice of using code to provision and manage infrastructure. It is not tied to any specific automation engine or programming language. The core principle is that the intended configuration state of network devices is sourced from a source code management system (such as Git) instead of from the devices themselves.
This represents a fundamental shift in how network engineers think about configuration. Rather than logging into each switch and typing commands, you define what you want the network to look like in structured data files, commit those files to a repository, and let automation tools apply the changes.
The IaC Pipeline Workflow
A typical IaC workflow for Cisco networks follows these steps:
- A user commits changes to a source code management system (such as GitLab) that defines the IaC intent
- The SCM detects the change and activates a CI/CD pipeline
- Pipeline runners configure and test staging environments based on the declared intent
- Pipeline runners configure and test production environments, either automatically or via user intervention
This pipeline model introduces two critical concepts:
- Continuous Integration (CI): Code changes are linted, validated, and merged through pull or merge requests from feature branches into the main branch
- Continuous Delivery (CD): Validated changes are automatically deployed to staging and then production environments, with testing at each stage
The result is a workflow where network changes are reviewed, tested, and deployed through the same disciplined process that software development teams have used for years.
Why IaC Matters
The declarative approach of IaC differs fundamentally from the traditional imperative approach. With imperative configuration, you tell the device step by step what to do. With declarative configuration, you define the end result you want, and the automation tool determines how to get there. This declarative model is exactly what Ansible provides for Cisco networks.
How Does Ansible Work as a Network Automation Tool?
Ansible is an open-source automation, configuration, and orchestration tool with several characteristics that make it particularly well-suited for network automation:
| Characteristic | Description |
|---|---|
| Open Source | Freely available with a large community |
| Agentless | No software installation required on target devices |
| Push Model | Configuration is pushed from the control host to targets |
| Idempotent | Produces the same results no matter how many times it is executed |
| No Programming Required | Requires only data-structure manipulation knowledge |
| Multi-Protocol | Supports network CLI and REST API interaction |
The idempotent nature of Ansible is particularly important for network operations. You can run the same playbook multiple times, and it will only make changes when the device configuration differs from the declared intent. This means you can safely re-run deployments without fear of creating duplicate configurations or breaking existing setups.
Ansible Core Components
Understanding what makes up Ansible is essential before building your first playbook. The key components are:
- Ansible Core: The engine that executes automation tasks, built on Python
- Playbooks: YAML files that define what automation tasks to execute and against which hosts
- Inventory: Defines the target devices (the "where to do it")
- Roles: Reusable, organized collections of tasks (the "how to do it")
- Tasks: Individual units of work within a playbook or role
- Collections: Packaged sets of modules, plugins, and filters organized by vendor or platform
- Intent: The desired configuration state defined in variable files
The Ansible control host (which can run on Linux, macOS, or Windows Subsystem for Linux) communicates with target devices using different protocols depending on the platform. For NX-OS, it typically uses CLI over SSH or API over HTTP/HTTPS. For NDFC, it uses the HTTPAPI connection plugin to interact with the NDFC REST API.
Pro Tip: Ansible is described as a state-LESS tool for IOS-XE automation. This means Ansible does not inspect the current device configuration before making changes. If you need stateful configuration management that checks the current state before applying changes, consider pairing Ansible with additional verification tasks or using resource modules that support state management.
Setting Up Your Ansible Cisco Environment
Getting your Ansible environment ready for Cisco network automation involves several steps. Following best practices from the start will save you significant troubleshooting time later.
Python Virtual Environments
You should always use a virtual environment when working with Ansible. A virtual environment allows you to install Ansible inside a contained area with a specific version of Python. This makes it possible to run different Python scripts that require different versions of Python and libraries without conflicts.
Using pyenv to manage Python virtual environments provides control over which Python version executes, independent of the system version. Here is the setup process:
pyenv install 3.9.11 # Install a version of Python
pyenv virtualenv 3.9.11 ansible # Create virtual environment
mkdir my_ansible_dir # Create directory for Ansible development
pyenv local ansible # Set pyenv virtual environment
The pyenv virtualenv plugin is also needed for this workflow to function properly.
Installing Ansible: Core vs Full
There are two approaches to installing Ansible, each with distinct trade-offs:
| Installation Method | Command | Pros | Cons |
|---|---|---|---|
| ansible-core | pip install ansible-core | Smaller footprint, more control, assures latest collection versions | Collections must be installed separately |
| ansible (full) | pip install ansible | Complete package, community-curated collections included | Larger footprint, might not install the latest version of desired collections |
For Cisco network automation, ansible-core is the recommended approach because it gives you explicit control over which collections are installed and ensures you always have the latest versions.
Installing Cisco Collections
Ansible Collections were introduced in Ansible 2.9 and use Ansible Galaxy as their delivery vehicle. Collections contain modules, plugins, and filters, and importantly, their release schedules are not tied to Ansible's own release cycle. This allows Cisco to release collection updates aligned with their own product releases.
To install the Cisco NX-OS and NDFC collections:
ansible-galaxy collection install cisco.nxos cisco.dcnm
The key collections for Cisco automation are:
- cisco.nxos: Contains 85 modules for NX-OS device configuration (VLANs, interfaces, OSPF, BGP, and more)
- cisco.dcnm: Contains 17 modules for NDFC/DCNM fabric management
- ansible.builtin: Includes built-in modules like set_fact, import_roles, tasks, vars, and many filters for working with data sets (actively maintained by Red Hat)
Pro Tip: Always use the Fully Qualified Collection Name (FQCN) when referencing modules in your playbooks. For example, use
cisco.nxos.nxos_vlansinstead of justnxos_vlans. This eliminates ambiguity and ensures the correct module is always used, especially when multiple collections are installed.
How to Automate Cisco NX-OS with Ansible Playbooks
Automating NX-OS switches with Ansible follows a structured approach that can be broken down into four key questions: Where to do it (inventory), What to do (playbook), How to do it (roles), and Data to do it (variables). Let us walk through building a complete VXLAN EVPN fabric automation project.
Project Directory Structure
A well-organized Ansible project for NX-OS automation follows this directory layout:
ansible/
inventory.yml # Where to do it
group_vars/ # Data to do it (group-level)
leafs.yml
spines.yml
host_vars/ # Data to do it (host-level)
10.15.1.11.yml
10.15.1.12.yml
10.15.1.13.yml
10.15.1.14.yml
10.15.1.15.yml
vxlan.yml # What to do (main playbook)
roles/ # How to do it
common/
underlay/
overlay/
The Inventory File: Where to Do It
The inventory file defines the target devices and their connection parameters. For NX-OS automation, a typical inventory looks like this:
---
# main inventory file
all:
vars:
ansible_connection: ansible.netcommon.network_cli
ansible_user: "nxos_username"
ansible_password: "nxos_password"
ansible_network_os: cisco.nxos.nxos
children:
spines:
hosts:
10.15.1.11:
10.15.1.12:
leafs:
hosts:
10.15.1.13:
10.15.1.14:
10.15.1.15:
Several important details to note here:
- ansible_connection specifies the protocol used to communicate with devices. For NX-OS CLI access, use
ansible.netcommon.network_cli(CLI over SSH). For API access, useansible.netcommon.httpapi(API over HTTP/HTTPS) - ansible_network_os tells Ansible which platform-specific behaviors to use
- Devices are organized into groups (spines and leafs) which can be targeted independently in playbooks
- Both connection types require the
ansible_network_osvariable and use persistent connections
| ansible_connection Value | Protocol | Requires | Persistent |
|---|---|---|---|
ansible.netcommon.network_cli | CLI over SSH | ansible_network_os | Yes |
ansible.netcommon.httpapi | API over HTTP/HTTPS | ansible_network_os | Yes |
The Playbook: What to Do
The main playbook defines which roles execute against which inventory groups:
---
# main playbook
- hosts: spines, leafs
gather_facts: false
roles:
- role: common
- role: underlay
- hosts: leafs
gather_facts: false
roles:
- role: overlay
Notice how the playbook contains two plays. The first play applies the common and underlay roles to both spines and leafs. The second play applies the overlay role only to leaf switches, since overlay configurations like VRFs, VLANs with VNI mappings, and SVIs are typically leaf-specific in a VXLAN EVPN architecture.
Ansible Roles: How to Do It
Roles provide a clean way to organize tasks. You can initialize a new role using Ansible Galaxy:
ansible-galaxy init overlay
This creates a standardized directory structure:
overlay/
README.md
tasks/
main.yml
templates/
vars/
main.yml
Each role in the VXLAN EVPN project handles a specific layer of the fabric:
- Common Role -- Tasks: Hostname configuration, Feature enablement
- Underlay Role -- Tasks: Interface configuration, OSPF, PIM, BGP
- Overlay Role -- Tasks: NVE configuration, VRFs, VLANs/VNIs, SVIs
Here is an example of the common role configuring a hostname using the cisco.nxos.nxos_hostname module:
---
# tasks file for roles/common
- name: Configure Hostname
cisco.nxos.nxos_hostname:
config:
hostname: "{{ hostname }}"
state: merged
And here is the overlay role configuring VLAN-to-VNI mappings:
---
# tasks file for roles/overlay
- name: Configure VLAN-to-VNI Mappings
cisco.nxos.nxos_vlans:
config:
- name: Web Servers
vlan_id: 101
mapped_vni: 10101
- name: DB Servers
vlan_id: 102
mapped_vni: 10102
- name: vMotion
vlan_id: 103
mapped_vni: 10103
state: merged
Variables and Data: Data to Do It
Ansible variables can be defined at multiple levels. The two most common locations are group_vars and host_vars directories.
Group variables (in group_vars/leafs.yml) contain data common to all devices in a group:
---
# var file for leafs group
features:
- ospf
- pim
- bgp
- nv overlay
- vn-segment-vlan-based
- interface-vlan
networks:
- vrf_name: AnsibleVRF
vlan_name: AnsibleNet1
vlan_id: 101
vni_id: 10101
ip: 10.1.101.1/24
- vrf_name: AnsibleVRF
vlan_name: AnsibleNet2
vlan_id: 102
vni_id: 10102
ip: 10.1.102.1/24
Host variables (in host_vars/10.15.1.13.yml) contain data specific to individual devices:
# vars file for L1
hostname: L1
layer3_physical_interfaces:
- interface: ethernet1/11
description: To S1 Eth1/1
mode: layer3
ip_address: 10.1.1.1
mask: 31
mtu: 9216
- interface: ethernet1/12
description: To S2 Eth1/1
mode: layer3
ip_address: 10.2.2.1
mask: 31
mtu: 9216
Key rules for variable files: group_vars files must be named after the inventory group they correspond to, and host_vars files must be named after the device IP address or FQDN as it appears in the inventory.
Using Loops and Jinja2 Templates for Ansible NX-OS Automation
When you have lists of similar configuration items (like multiple VLANs), Ansible provides two powerful mechanisms to handle them: loops and Jinja2 templates.
With loops, you iterate over a list of dictionaries defined in your variables:
---
# tasks file for roles/overlay
- name: Configure VLAN-to-VNI Mappings
cisco.nxos.nxos_vlans:
config:
- name: "{{ item.vlan_name }}"
vlan_id: "{{ item.vlan_id }}"
mapped_vni: "{{ item.vni_id }}"
loop: "{{ networks }}"
Variables in Ansible use Jinja2 syntax with double curly braces wrapped in quotes: "{{ variable_name }}". When iterating with a loop, the current item is accessed using item.key_name.
For more complex scenarios, you can combine Jinja2 templates with Ansible builtin modules:
---
# tasks file for roles/overlay
- name: Generate VLAN Config Payload
ansible.builtin.set_fact:
nxos_vlans: "{{ lookup('template', 'vlans.j2') }}"
- name: Configure VLANs
cisco.nxos.nxos_vlans:
config: "{{ nxos_vlans | from_yaml }}"
state: merged
The corresponding Jinja2 template file (roles/overlay/templates/vlans.j2) generates the configuration data dynamically:
{% for network in networks %}
- vlan_id: {{ network.vlan_id }}
{% endfor %}
This template approach is particularly useful when you need to transform data between different structures or when the configuration payload requires complex conditional logic.
Understanding Ansible NX-OS Module Types and States
The cisco.nxos collection contains two categories of modules, each with different capabilities and intended use cases.
Legacy Modules vs Resource Modules
| Feature | Legacy Modules | Resource Modules |
|---|---|---|
| Scope | Specific to NX-OS devices | Consistent across different network platforms |
| Looping | Requires task loops for multiple items | Can leverage task loops or Jinja2 templating for config blocks |
| States | Simple states (present or absent) | Introduces new states for Ansible to be the source of truth |
| Scalability | Limited | Designed for scale |
Resource modules are the preferred approach for new automation projects because they offer more granular control over how Ansible interacts with the device configuration.
Resource Module States
Resource modules support four states that control how Ansible applies configuration changes:
- Merged: Ansible merges the on-device configuration with the provided configuration in the task. Existing configuration that is not specified in the task remains unchanged
- Replaced: Ansible replaces the on-device configuration subsection with the provided configuration subsection in the task. Only the targeted subsection is affected
- Overridden: Ansible overrides the on-device configuration for the entire resource with the provided configuration in the task. Use caution with this state as you could remove your access to the device (for example, by overriding the management interface configuration)
- Deleted: Ansible deletes the on-device configuration subsection and restores any default settings
Pro Tip: Start with the
mergedstate when you are learning and testing. Only graduate toreplacedoroverriddenwhen you have a thorough understanding of how these states affect existing configuration, and always test in a staging environment first.
Handling Missing Modules
When you need to configure a feature that does not have a dedicated module, the cisco.nxos.nxos_config module allows you to pass direct CLI configuration:
- name: Configure PIM Anycast RP
cisco.nxos.nxos_config:
lines:
- "ip pim anycast-rp {{ s1_loopback1 }} {{ s1_loopback0 }}"
- "ip pim anycast-rp {{ s2_loopback1 }} {{ s2_loopback0 }}"
save_when: modified
The save_when parameter controls when a copy running-config startup-config operation is performed, with four options: always, modified (copy only if changed since last save), changed (copy only if the task made a change), and never.
Similarly, the cisco.nxos.nxos_command module sends arbitrary commands like show commands:
- name: Get Show Commands
cisco.nxos.nxos_command:
commands:
- show version
- show ip ospf neighbor
- show ip pim neighbor
This module also supports prompt handling for interactive commands:
- name: Misc Commands
cisco.nxos.nxos_command:
commands:
copy ftp://nxos.bin bootflash:
prompt:
- "Username:"
- "Password:"
answer:
- <username>
- <password>
Running the Playbook
To execute your complete VXLAN EVPN automation playbook:
ansible-playbook -i inventory.yml vxlan.yml
This single command triggers the entire fabric deployment -- configuring hostnames, enabling features, setting up underlay routing, and deploying the VXLAN overlay across all spines and leafs defined in your inventory.
How to Automate NDFC with Ansible Collections
Nexus Dashboard Fabric Controller (NDFC), formerly known as Cisco Data Center Network Manager (DCNM), provides a controller-based approach to managing VXLAN EVPN fabrics. While direct NX-OS automation gives you switch-level control, NDFC automation operates at the fabric level, offering rapid deployment with best-practice templates and accelerating provisioning from days to minutes.
NDFC also provides configuration compliance, continuously monitoring whether the configuration is compliant with user intent, detecting errors, and flagging drifts for remediation. This ensures fabric consistency across all managed switches.
NDFC Ansible Architecture
The NDFC Ansible architecture differs from direct NX-OS automation in one critical way: instead of connecting to individual switches, Ansible connects to the NDFC controller via its REST API:
Ansible Core --> HTTPAPI Connection Plugin --> NDFC REST API --> NDFC
The cisco.dcnm collection uses the ansible.netcommon.httpapi connection plugin to communicate with NDFC over HTTP/HTTPS. This means your inventory points to the NDFC controller, not to individual switches.
NDFC Collection Modules
The cisco.dcnm collection (available on Ansible Galaxy) provides 17 modules covering the full lifecycle of fabric management:
| Module | Purpose |
|---|---|
cisco.dcnm.dcnm_rest | General-purpose "do anything" module for direct API calls |
cisco.dcnm.dcnm_fabric | Manage creation and configuration of NDFC fabrics (v3.5.0) |
cisco.dcnm.dcnm_inventory | Add devices to a fabric |
cisco.dcnm.dcnm_vpc_pair | Manage vPC switch pairs (v3.5.0) |
cisco.dcnm.dcnm_interface | Configure fabric interfaces |
cisco.dcnm.dcnm_vrf | Add overlay VRFs |
cisco.dcnm.dcnm_network | Add overlay networks and VLANs |
cisco.dcnm.dcnm_template | Create custom templates |
cisco.dcnm.dcnm_policy | Create policies based on templates |
cisco.dcnm.dcnm_links | Manage fabric links |
cisco.dcnm.dcnm_resource_manager | Manage fabric resources |
cisco.dcnm.dcnm_image_upload | Manage switch images (v3.5.0) |
cisco.dcnm.dcnm_image_policy | Manage image policies (v3.5.0) |
cisco.dcnm.dcnm_image_upgrade | Manage images for NX-OS switches (v3.5.0) |
cisco.dcnm.dcnm_service_node | Manage service nodes |
cisco.dcnm.dcnm_service_policy | Manage service policies |
cisco.dcnm.dcnm_service_route_peering | Manage service route peering |
Collection version 3.5.0 introduced eight new modules, including dcnm_fabric, dcnm_vpc_pair, and the image management modules. An important compatibility note: the same Ansible playbooks work with both NDFC version 11 and version 12 without requiring any playbook changes.
NDFC Inventory and Playbook Structure
The NDFC inventory file uses the httpapi connection plugin instead of network_cli:
---
# main inventory file
all:
vars:
ansible_connection: ansible.netcommon.httpapi
ansible_user: "ndfc_username"
ansible_password: "ndfc_password"
ansible_network_os: cisco.dcnm.dcnm
children:
ndfc:
hosts:
10.15.0.11:
dcnm:
hosts:
10.18.1.14:
The NDFC playbook follows a fabric lifecycle workflow with dedicated roles for each step:
---
# main NDFC playbook
- name: Build VXLAN EVPN Fabric on NDFC
hosts: ndfc
gather_facts: false
roles:
- create_fabric
- add_inventory
- setup_vpc
- manage_interfaces
- manage_overlay
- deploy
NDFC Automation Workflow: Step by Step
The NDFC fabric build process follows a logical sequence of steps, each handled by a dedicated Ansible role:
Step 1 -- Create the VXLAN Fabric: The dcnm_fabric module (new in v3.5.0) simplifies fabric creation by handling mutually exclusive properties and supporting multiple fabric types:
---
# tasks file for roles/create_fabric
- name: Create Fabric with Module
cisco.dcnm.dcnm_fabric:
state: merged
config:
- FABRIC_NAME: CL_STAGING
FABRIC_TYPE: VXLAN_EVPN
BGP_AS: 65000
ANYCAST_GW_MAC: 0001.aabb.ccdd
UNDERLAY_IS_V6: false
Step 2 -- Add Inventory: The dcnm_inventory module adds switches to the fabric with defined roles:
---
# tasks file for roles/add_inventory
- name: Add Fabric Devices
cisco.dcnm.dcnm_inventory:
fabric: "{{ fabric.name }}"
state: merged
config:
- seed_ip: 192.168.1.1
role: spine
- seed_ip: 192.168.1.2
role: leaf
- seed_ip: 192.168.1.4
role: border
Step 3 -- Setup vPC Pairs: The dcnm_vpc_pair module (new in v3.5.0) creates vPC pairs that automatically discover compatible devices and interfaces:
---
# tasks file for roles/setup_vpc
- name: Setup vPC Pairs
cisco.dcnm.dcnm_vpc_pair:
src_fabric: "{{ fabric.name }}"
state: merged
config:
- peerOneId: 192.168.1.1
peerTwoId: 192.168.1.2
Step 4 -- Manage Overlay: The dcnm_vrf and dcnm_network modules create VRFs and networks, then attach and deploy them to fabric leaf devices:
---
# tasks file for roles/manage_overlay
- name: Add Overlay VRFs
cisco.dcnm.dcnm_vrf:
fabric: "{{ fabric.name }}"
state: replaced
config:
- vrf_name: CL-VRF1
vrf_id: 470000
vlan_id: 2055
attach:
- 192.168.1.1
- 192.168.1.2
- 192.168.1.3
- 192.168.1.4
- name: Add Overlay Networks
cisco.dcnm.dcnm_network:
fabric: "{{ fabric.name }}"
state: overridden
config:
- net_name: CL-NET7000
vrf_name: CL-VRF1
net_id: 7000
vlan_id: 88
attach:
- 192.168.1.2
- 192.168.1.4
NDFC State Management: Who Is the Source of Truth?
When automating NDFC, the question of source of truth becomes critical. The merged, replaced, and overridden states behave slightly differently in the NDFC context compared to direct NX-OS automation:
-
Merged: The playbook configuration is merged with what already exists in NDFC. For example, if a network named CL-NET7000 exists in NDFC with certain properties, and the playbook specifies additional properties like
route_tag: 12345, those properties are added while existing ones remain unchanged. Other networks are untouched. -
Replaced: The playbook replaces the specific resource (such as CL-NET7000) in NDFC with exactly what is defined in the playbook. The playbook becomes the source of truth for that specific resource. Any properties not specified in the playbook are reset to defaults. Other networks remain untouched.
-
Overridden: The playbook becomes the source of truth for the entire resource type. Any networks not defined in the playbook are removed from NDFC. This is the most aggressive state and should be used with caution.
Pro Tip: When using the
overriddenstate with NDFC, make sure your playbook includes every network and VRF that should exist in the fabric. Any resources not explicitly defined in the playbook will be deleted. Always test overridden operations in a staging fabric first.
Ansible Cisco Automation for IOS-XE Networks
While NX-OS and NDFC focus on data center automation, Cisco IOS-XE powers the enterprise campus and WAN with platforms like the Catalyst 9000 series. Ansible can automate IOS-XE devices across their entire lifecycle, from Day 0 onboarding through Day N operations.
IOS-XE Programmability Interfaces
IOS-XE offers multiple programmable interfaces that Ansible can leverage. Understanding these interfaces helps you choose the right connection method:
| Interface | Min IOS-XE Version | Recommended Version | Default Port | Encoding | Transport |
|---|---|---|---|---|---|
| NETCONF | 16.6 | 17.6 | 830 | XML | SSH |
| RESTCONF | 16.7 | 17.6 | 443 | XML or JSON (RFC 7951) | HTTPS |
| gNMI | 16.8 | 17.7 | 9339 | JSON_IETF | HTTP/2 |
Each interface supports different operations:
- NETCONF:
<get>,<get-config>,<edit-config>,<establish-subscription> - RESTCONF: GET, POST, PUT, PATCH, DELETE
- gNMI: GET, SET, SUBSCRIBE
All three interfaces use YANG data models to define the data available for configuration and streaming telemetry. YANG models come in two flavors: Cisco Native models (specific to IOS-XE features) and OpenConfig models (vendor-neutral).
Ansible and IOS-XE: Key Characteristics
When using Ansible with IOS-XE, several important characteristics apply:
- Ansible is agentless -- there are no installation requirements on the target IOS-XE device, other than having an accessible API or interface
- Ansible is state-LESS for IOS-XE -- it does not inspect the current configuration state before making changes
- Ansible can interact with IOS-XE via the NETCONF or RESTCONF API
- Ansible provides a secure and reliable way to interact with remote devices
- It is highly adaptable and commonly used with other automation tools to accomplish complex workflows
Ansible with IOS-XE is typically used for device configuration rather than infrastructure management. For use cases where you need to verify the current state before making changes (stateful management), Terraform may be a complementary or alternative tool, as it checks the current configuration before applying changes.
IOS-XE Tooling Comparison
When deciding between automation tools for IOS-XE, consider this comparison:
| Use Case | Ansible | Terraform |
|---|---|---|
| Configure a network device | Yes | Yes |
| Stateless configuration | Yes (follows procedural steps) | No |
| Stateful configuration | No | Yes (reviews current config first) |
| Non-Cisco device support | Yes | Yes |
| Scalable within the tooling | Yes | Yes |
| Protocols Supported | NETCONF, RESTCONF | RESTCONF |
IOS-XE devices also support the show run | format netconf-xml and show run | format restconf-json commands, which can help you translate existing CLI configurations into the structured data formats that Ansible and Terraform expect.
Pro Tip: YANG Suite is a valuable companion tool for Ansible automation. You can use it to explore YANG models, construct and test YANG-based APIs over NETCONF, RESTCONF, gRPC, and gNMI, and understand the data structures your Ansible playbooks need to provide. It supports IOS-XE, IOS-XR, and NX-OS platforms.
Building a CI/CD Pipeline for Ansible Cisco Automation
The ultimate goal of Infrastructure as Code is to embed your Ansible automation into a CI/CD pipeline that automates the entire change management process. Based on the NDFC automation workflow, here is how a production pipeline operates:
Pipeline Workflow
- Add or modify network definitions in your group_vars files (for example, adding new overlay networks with the
overriddenstate) - Commit and push changes to a staging branch in your Git repository
- Open a Merge Request which triggers the staging pipeline to deploy and verify the changes against the staging fabric
- Review and approve the merge request after staging validation passes
- Click Merge to merge into the main branch, which triggers the production pipeline to deploy and verify against the production fabric
This workflow ensures that every network change goes through the same review, test, and deploy process. The staging environment catches configuration errors before they reach production, and the Git history provides a complete audit trail of every change.
Ansible Tags for Pipeline Control
When running playbooks in a CI/CD pipeline, Ansible tags provide granular control over which tasks execute. Tags allow you to run specific subsets of tasks without executing the entire playbook:
- A tag like
cf_vxlanruns only the VXLAN fabric management tasks - A tag like
cf_externalruns only the external fabric management tasks
This is particularly useful in pipeline scenarios where you may need to deploy only overlay changes without re-running the entire fabric build.
Frequently Asked Questions
What is the difference between ansible-core and ansible when automating Cisco devices?
Installing ansible-core via pip install ansible-core gives you a minimal installation where you must install collections (like cisco.nxos and cisco.dcnm) separately. This provides a smaller footprint, more control, and ensures you get the latest collection versions. Installing the full ansible package via pip install ansible includes a community-curated selection of collections but has a larger footprint and might not include the latest version of each collection. For Cisco network automation, ansible-core with explicitly installed collections is the preferred approach.
Can the same Ansible playbooks work with both DCNM and NDFC?
Yes. The Cisco DCNM Ansible collection (cisco.dcnm) is designed to work with both NDFC version 11 and version 12 without requiring any playbook changes. The collection namespace remained cisco.dcnm even after the product was renamed from DCNM to NDFC, ensuring backward compatibility.
What Ansible connection type should I use for NX-OS versus NDFC?
For direct NX-OS switch automation, use ansible.netcommon.network_cli (CLI over SSH) or ansible.netcommon.httpapi (API over HTTP/HTTPS). Both require the ansible_network_os variable and use persistent connections. For NDFC automation, use ansible.netcommon.httpapi with ansible_network_os: cisco.dcnm.dcnm, since Ansible communicates with the NDFC controller's REST API rather than with individual switches.
What is the difference between merged, replaced, and overridden states?
The merged state adds or updates configuration without removing existing items. The replaced state replaces a specific configuration subsection entirely, making the playbook the source of truth for that subsection while leaving other subsections untouched. The overridden state makes the playbook the source of truth for the entire resource type, removing any configuration not defined in the playbook. Use caution with overridden as it can remove critical configuration such as management interfaces.
How do I configure NX-OS features that do not have a dedicated Ansible module?
Use the cisco.nxos.nxos_config module to pass direct CLI configuration lines. This module also supports saving the configuration with the save_when parameter (options: always, modified, changed, never). For sending show commands or other non-configuration commands, use the cisco.nxos.nxos_command module, which also supports interactive prompt handling.
Is Ansible stateful or stateless when automating IOS-XE devices?
Ansible is a state-LESS tool when automating IOS-XE devices. It does not inspect the current device configuration before making changes. If you need stateful management that checks the current configuration before applying modifications, you may want to consider Terraform as a complementary tool, which uses the RESTCONF API and checks the current state before making changes.
Conclusion
Automating Cisco networks with Ansible transforms network operations from manual, error-prone CLI sessions into repeatable, version-controlled, and auditable code deployments. In this article, we covered the three major platforms where Ansible delivers significant value:
- NX-OS automation using the
cisco.nxoscollection with its 85 modules, structured playbooks, roles, and variable hierarchies to build complete VXLAN EVPN fabrics - NDFC automation using the
cisco.dcnmcollection with 17 modules that operate at the fabric controller level, providing rapid deployment, configuration compliance, and lifecycle management from fabric creation through overlay deployment - IOS-XE automation leveraging NETCONF and RESTCONF interfaces with YANG data models to configure enterprise Catalyst platforms at scale
The key to success with Ansible Cisco automation is starting with a well-organized project structure, understanding the difference between module states (merged, replaced, overridden, deleted), and progressively building toward a CI/CD pipeline that makes your Git repository the single source of truth for your entire network infrastructure.
Whether you are managing a handful of switches in a lab or orchestrating hundreds of devices across multiple data centers, the patterns and practices covered here provide a solid foundation for your Infrastructure as Code journey. Explore the automation and network programmability courses available on NHPREP to deepen your hands-on skills and accelerate your certification preparation.