Deploy a Configuration Change
Configuration Session
  • Introduction
  • Dev Setup
  • NX-API Overview
  • NX-API Python
  • Ansible NXOS
  • pyATS
  • NetDevOps
  • Configuration Session
  • Terraform
  • Bonus: YANG
  • Reference: Postman

Deploying a Real Configuration Change with Ansible

In the previous section you drove the Configuration Session lifecycle using a "golden config" round-trip — saving the running configuration, importing it back into a candidate session, and verifying that the diff was minimal. That pattern validates the mechanism itself. In this section you will use the same mechanism to deploy a real configuration change: adding a Loopback99 interface with OSPF and PIM configuration to staging-leaf1.

The workflow remains identical to what you already know: open candidate session → stage commands → diff → human approval → commit/abort. The difference is that instead of importing a file, you will stage CLI configuration commands directly into the candidate session, just as you would type them at the switch console.


Step 1 - Create the config-session-change.yml Playbook

Return to your VSCode Terminal window and make sure you are in your ansible-nxos directory. Create a new playbook called config-session-change.yml that stages a Loopback99 interface into a candidate session, displays the diff, waits for your approval, and then commits (or aborts) the change.


cd /home/pod19/workspace/nxapilab/ansible-nxos

The playbook will:

  1. Generate a unique candidate-session name
  2. Open the candidate session and stage the Loopback99 configuration
  3. Diff the candidate against the running-config
  4. Pause for human approval
  5. Commit or abort the session
  6. Show the commit list
  7. Verify the interface exists in the running-config

As with the previous playbook, this play overrides the connection plugin to ansible.netcommon.network_cli (SSH) because Configuration Session is a stateful CLI feature that requires prompt context to persist across commands.


touch /home/pod19/workspace/nxapilab/ansible-nxos/config-session-change.yml
cat <<'EOF' > /home/pod19/workspace/nxapilab/ansible-nxos/config-session-change.yml
---
- name: Configuration Session - Deploy Loopback99
  hosts: leafs
  gather_facts: false

  vars:
    ansible_connection: ansible.netcommon.network_cli
    ansible_user: "{{ lookup('ansible.builtin.env', 'NXOS_USERNAME') }}"
    ansible_password: "{{ lookup('ansible.builtin.env', 'NXOS_PASSWORD') }}"
    ansible_host_key_checking: false

  tasks:
    - name: Generate unique candidate-session name for this run
      ansible.builtin.set_fact:
        session_name: "loopback_session_{{ lookup('pipe', 'date +%s') }}"
      run_once: true
      delegate_to: localhost
      vars:
        ansible_connection: local

    - name: Show the session name that will be used
      ansible.builtin.debug:
        msg: "Using candidate session name: {{ session_name }}"

    - name: Open candidate session and stage Loopback99 configuration
      cisco.nxos.nxos_command:
        commands:
          - "configure candidate name {{ session_name }}"
          - "interface loopback99"
          - "  description Routing Loopback"
          - "  ip address 99.99.99.1/32"
          - "  ip ospf network point-to-point"
          - "  ip router ospf UNDERLAY area 0.0.0.0"
          - "  ip pim sparse-mode"
          - end

    - name: Show candidate-config diff vs running-config
      cisco.nxos.nxos_command:
        commands:
          - "show candidate-config name {{ session_name }} diff"
      register: diff_result

    - name: Display the diff for review
      ansible.builtin.debug:
        msg: "{{ diff_result.stdout_lines[0] }}"

    - name: Pause for human approval
      ansible.builtin.pause:
        prompt: |
          Review the candidate-config diff above.
          Type 'yes' to COMMIT the session, anything else to ABORT
      register: user_decision

    - name: Commit the candidate session
      cisco.nxos.nxos_command:
        commands:
          - "configure candidate name {{ session_name }} commit"
      when: user_decision.user_input | lower == 'yes'

    - name: Abort the candidate session
      cisco.nxos.nxos_command:
        commands:
          - command: "configure candidate name {{ session_name }} abort"
            prompt: '\(y/n\)'
            answer: 'y'
      when: user_decision.user_input | lower != 'yes'

    - name: Show configuration commit list
      cisco.nxos.nxos_command:
        commands:
          - show configuration commit list
      register: commit_list

    - name: Display the commit list
      ansible.builtin.debug:
        msg: "{{ commit_list.stdout_lines[0] }}"

    - name: Verify Loopback99 exists in running-config
      cisco.nxos.nxos_command:
        commands:
          - "show ip interface brief | include Lo99"
      register: verify_result
      when: user_decision.user_input | lower == 'yes'

    - name: Display verification
      ansible.builtin.debug:
        msg: "{{ verify_result.stdout_lines[0] }}"
      when: user_decision.user_input | lower == 'yes'
EOF

Key differences from the golden-config playbook in the previous section:

  • Instead of factory-reset candidate followed by import configuration bootflash:…, this playbook stages individual CLI commands directly into the candidate session. The commands in the commands: list are sent sequentially over the same SSH session, so the prompt context flows from (config-c-<session>)# into (config-c-<session>-if)# naturally — exactly as if you were typing them at the console.

  • A verification task at the end runs show ip interface brief | include Lo99 to confirm the Loopback99 interface is present in the running configuration after the commit. This task only runs when the user typed yes to commit.

Step 2 - Run the Playbook Against staging-leaf1

Run the playbook with ansible-playbook, using your existing staging.yml inventory and limiting execution to staging-leaf1.


ansible-playbook -i staging.yml config-session-change.yml --limit staging-leaf1


Step 3 - Review the Diff Output

When the Display the diff for review task runs, Ansible will print the candidate-config diff showing the new Loopback99 interface block as added lines. Your output should look similar to:

TASK [Display the diff for review] ************************************************************************************************************************************
ok: [staging-leaf1] => {
    "msg": [
        "--- running-config",
        "+++ candidate-config",
        "@@ -274,6 +274,13 @@",
        "@@ interface loopback1 @@",
        "   ip ospf network point-to-point",
        "   ip router ospf UNDERLAY area 0.0.0.0",
        "   ip pim sparse-mode",
        "+interface loopback99",
        "+  description Routing Loopback",
        "+  ip address 99.99.99.1/32",
        "+  ip ospf network point-to-point",
        "+  ip router ospf UNDERLAY area 0.0.0.0",
        "+  ip pim sparse-mode",
        " line console",
        " line vty",
        " boot nxos bootflash:/nxos64-cs-lite.10.6.3.F.bin"
    ]
}

The + lines confirm that Loopback99 with all its sub-commands will be added to the running configuration when you commit. No existing configuration is being removed or modified — this is a purely additive change.


Step 4 - Approve the Commit

After the diff is displayed, the playbook pauses and prompts you for a decision:

TASK [Pause for human approval] **************************************************************************************************************************
[Pause for human approval]
Review the candidate-config diff above.
Type 'yes' to COMMIT the session, anything else to ABORT
: 

Type yes and press Enter to commit the Loopback99 configuration to the running configuration.

  • If you type yes, the Commit the candidate session task runs, the change is applied atomically, and the verification task confirms Loopback99 exists.

  • If you type anything else (or just press Enter), the Abort task runs, discarding the candidate session without touching the running configuration.

Type yes for this exercise so you can see the commit succeed and then practice rolling it back in the next step.


Step 5 - Verify the Change

After committing, the playbook automatically verifies that Loopback99 is present in the running configuration. You should see output similar to:

TASK [Display verification] *****************************************************************************************************************************
ok: [staging-leaf1] => {
    "msg": [
        "Lo99             99.99.99.1      protocol-up/link-up/admin-up"
    ]
}

The interface is protocol-up/link-up/admin-up, confirming the configuration was committed successfully and the interface is operational.

Also note the commit list output — your new commit should appear at the top with the loopback_session_<epoch> session name. Take note of the commit ID (e.g. 2000000002) — you will use it in the next step to rollback the change.

TASK [Display the commit list] ***************************************************************************************************************************
ok: [staging-leaf1] => {
    "msg": [
        "SNo. Label/ID     User         Line         Client     Time Stamp                 Session Name",
        "---- ------------ ------------ ------------ ---------- -----------------------    -------------",
        "1    2000000002   admin        /dev/pts/4   CLI        Mon May 26 14:35:22 2026   loopback_session_1779823322",
        "2    2000000001   admin        /dev/pts/4   CLI        Mon May 26 04:21:11 2026   golden_session_1779769243"
    ]
}

Step 6 - Rollback the Change

Configuration Session stores every commit with a unique ID, allowing you to roll back to any previous state. You will now create a small rollback playbook that reverts the Loopback99 change by rolling back to the commit that preceded it.

Create the config-session-rollback.yml playbook:


cat <<'EOF' > /home/pod19/workspace/nxapilab/ansible-nxos/config-session-rollback.yml
---
# Rollback a Configuration Session commit by ID.
#
# Usage:
#   ansible-playbook -i staging.yml config-session-rollback.yml \
#     --limit  -e commit_id=
#
# The commit_id variable must be passed as an extra var. Use the output of
# 'show configuration commit list' to find the ID you want to roll back to.

- name: Configuration Session - Rollback
  hosts: leafs
  gather_facts: false

  vars:
    ansible_connection: ansible.netcommon.network_cli
    ansible_user: "{{ lookup('ansible.builtin.env', 'NXOS_USERNAME') }}"
    ansible_password: "{{ lookup('ansible.builtin.env', 'NXOS_PASSWORD') }}"
    ansible_host_key_checking: false

  tasks:
    - name: Validate that commit_id was provided
      ansible.builtin.assert:
        that:
          - commit_id is defined
          - commit_id | length > 0
        fail_msg: "You must pass -e commit_id= when running this playbook."

    - name: Roll back to commit {{ commit_id }}
      cisco.nxos.nxos_command:
        commands:
          - "rollback configuration commit {{ commit_id }}"
      register: rollback_result

    - name: Display rollback result
      ansible.builtin.debug:
        msg: "{{ rollback_result.stdout_lines[0] }}"

    - name: Show configuration commit list after rollback
      cisco.nxos.nxos_command:
        commands:
          - show configuration commit list
      register: commit_list

    - name: Display the commit list
      ansible.builtin.debug:
        msg: "{{ commit_list.stdout_lines[0] }}"

    - name: Verify Loopback99 is removed
      cisco.nxos.nxos_command:
        commands:
          - "show ip interface brief | include Lo99"
      register: verify_result

    - name: Display verification
      ansible.builtin.debug:
        msg: "{{ verify_result.stdout_lines[0] | default('Lo99 not found - rollback successful') }}"
EOF

Now run the rollback playbook, replacing <COMMIT_ID> with the commit ID from the previous commit (the one before your Loopback99 commit — typically 2000000001 if this is your second commit, or whatever ID appeared in the row below your loopback session in the commit list):


ansible-playbook -i staging.yml config-session-rollback.yml --limit staging-leaf1 -e commit_id=2000000001

The rollback output should confirm the operation was successful:

TASK [Display rollback result] **************************************************************************************************************************
ok: [staging-leaf1] => {
    "msg": [
        "Rolling back to commitID : 2000000001",
        "04:45:05 26 May 2026 Initializing rollback db",
        "04:45:10 26 May 2026 Importing config from file",
        "04:45:11 26 May 2026 Imported config successfully",
        "04:45:13 26 May 2026 Committing config",
        "04:45:19 26 May 2026 Commit successful",
        "rollback is successful",
        "Configuration committed by user 'admin' using Commit ID : 2000000003"
    ]
}

The verification task should show that Loopback99 is no longer present in the running configuration — confirming the rollback removed the interface cleanly.

The rollback itself creates a new commit (e.g. 2000000003). This means you can always roll forward again if needed — every state transition is recorded in the commit history.


Step 7 - Recap

In this section you used Ansible to deploy a real configuration change through the Configuration Session workflow:

  1. Staged CLI commands directly into a named candidate session — adding Loopback99 with OSPF and PIM configuration.
  2. Diffed the candidate against the running-config to confirm the exact change that would be applied.
  3. Paused for human approval — the operator (you) made the final commit/abort decision.
  4. Committed the change atomically to the running configuration.
  5. Verified the interface was operational in the running-config.
  6. Rolled back the change using a separate playbook, demonstrating the full reversibility that Configuration Session provides.

This pattern — stage → diff → approve → commit → verify → rollback if needed — is the foundation of safe, auditable network change management. Whether you are deploying a single loopback interface or a complex multi-feature change, the workflow remains the same: the candidate session isolates your changes, the diff gives you confidence, and the commit history gives you a path back.