Skip to content
CONFIGLANE

Automation · Operations

Ansible on IOS XE: changes you can read before they run

Almost every company has that one script: a loop over forty switches, pushing commands in. It reliably does what was typed — what should be true, it does not know. The difference shows exactly when device twenty-three was configured differently from the rest.

By ConfiglanePublished Updated 6 min readNetwork Automation

The short version

  • The cisco.ios collection (currently version 11.5) ships resource modules such as ios_vlans and ios_interfaces alongside ios_config — the docs explicitly call it declarative management.
  • The states merged, replaced and overridden decide whether intent is added, an object is made exact, or the whole set is enforced — that is the step from command to state.
  • In check mode Ansible executes nothing and reports what would have changed; diff mode shows before and after. Together they are the dry run that precedes every change.
  • The offline states rendered, parsed and gathered work without a target device or read-only — configuration checks run in the pipeline, not in the maintenance window.

The loop script is not wrong — it is the wrong abstraction. It describes keystrokes, not state. Whether VLAN 30 exists everywhere afterwards, whether it already existed somewhere under a different name, whether one device quietly keeps a VLAN 99 alive: the script does not know, and neither does its log. Running auditable changes on that basis means reading forty terminal transcripts afterwards.

The cisco.ios collection (currently version 11.5) resolves this on two levels. For the transition there is ios_config — the docs describe it as working with IOS configuration sections “in a deterministic way”, including a backup option that saves the device's complete running-config before any change. And for the target way of working there are the resource modules: ios_vlans, ios_interfaces, ios_l2_interfaces, ios_l3_interfaces, plus ios_ospfv2, ios_acls, ios_ntp_global and more — declarative management instead of a command list.

Merged, replaced, overridden: three verbs, three contracts

Every resource module accepts the same parameter: state. It defines the contract the run makes with the device — and this is precisely where automation parts ways with scripting:

StateContract with the device
merged (default)The described intent is added; everything else is left untouched
replacedThe described objects are made exactly as the intent states them
overriddenThe whole set is enforced — anything not in the intent goes
deleted / purgedDescribed or orphaned configuration is removed
The online states of the resource modules, using ios_vlans as the example

Idempotency is not an add-on here but the consequence: describe state, and you can start the same run twice — the second one simply reports no change. Which, incidentally, is the easiest quality check for a playbook: a second run straight after the first has to come back empty.

No run without a dry run

Ansible ships the operating mode for it: in check mode it executes nothing on the target systems — modules that support it report the changes they would have made. Diff mode adds the before-and-after view, and both combine: read first, execute second.

In practice that diff is more than a precaution: it is the document you can discuss before the maintenance window — with the colleague, the customer, the auditor. The change stops being an announcement (“we are adjusting the VLANs”) and becomes a readable difference. Why we consider this artefact discipline the core of any network automation is covered in NetBox as the source of truth — that is where the intent comes from that the diff here is made against.

Testable without the production network: the offline states

The underrated part of the resource modules are the states that change no device: rendered produces the configuration from the intent data without touching a device; parsed turns existing configuration text into structured data; gathered reads the current state in as data. The module docs explicitly list rendered and parsed as offline variants.

  • rendered in the pipeline: every change to the intent produces the target configuration in CI — reviewable before anything sees a device.
  • gathered as the drift watch: a recurring, read-only run compares actual against intended state and turns “that should really be the case” into a result list.
  • parsed for the legacy estate: existing configurations become data — the most honest way to move a grown network into the intent world.
  • Backup before every write: the ios_config backup option saves the running-config before anything changes — the safety net costs one line.

From playbook to an accepted change

The Ansible NTP pilot template separates inputs, diff and actual time synchronisation. NetDevOps and a failed lab acceptance test then show why a successful configuration task does not prove a working service.

The chain, complete

Put together, this is the flow we describe as our way of working under network automation: the inventory comes from NetBox (dynamic, with groups from sites and roles), the intent comes from the source of truth, rendered and the check diff make it readable, the resource modules enforce it — and gathered watches the drift afterwards. Which IOS XE release belongs on the devices in the first place is its own decision with its own article: IOS XE release strategy.

Sources

Every evidenced claim in this article can be traced here. The retrieval date shows how fresh the check is.

  1. Cisco Ios Collection (Version 11.5.0)opens in a new tab

    Ansible Community Documentation · retrieved 27 August 2026

  2. cisco.ios.ios_vlans module — Resource module to configure VLANsopens in a new tab

    Ansible Community Documentation · retrieved 27 August 2026

  3. Validating tasks: check mode and diff modeopens in a new tab

    Ansible Community Documentation · retrieved 27 August 2026

FAQ

Frequently asked questions about Ansible on Cisco devices

What does idempotent mean for network changes, concretely?

An idempotent run describes a target state and establishes it — no matter how often it runs. The second run straight after the first reports no change. That is also the simplest quality test for a playbook: if two consecutive runs do not come back clean, it describes actions, not state.

Resource modules or ios_config — which one do we use?

Resource modules wherever they exist for the object (VLANs, interfaces, routing, ACLs): they are declarative, know the state contracts and return structured data. ios_config remains the controlled tool for the rest — with a backup option and deterministic handling of configuration sections.

Can we test this without touching the production network?

Yes, on two levels: check mode executes nothing on the devices and only reports what would change. And the offline states rendered and parsed need no device at all — configuration reviews become a pipeline step, not a maintenance window.

Do we need a full CI/CD landscape for this?

No. The first gain comes from two habits: no run without a check diff, and every diff goes into the change record. Pipeline steps such as rendered reviews come later — they automate the habit, they do not replace it.

How do we start in a grown network?

Reading: gathered and parsed turn the existing estate into structured data without changing anything. That produces the first honest intent in the source of truth — and only then comes the first writing run, with a small radius and a dry run.

Network Automation

Ansible automation. Repeatable changes, less repetitive work.

NTP, VLANs, interface standards or configuration backups: we turn recurring network tasks into readable, versioned code. The first workflow receives explicit inputs, a bounded scope and results your team can inspect.

Plan your first automation use case

Assessment → first dependable change