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.
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:
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:
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.
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.
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
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.
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.
yes, the Commit the candidate session task runs, the change is applied atomically,
and the verification task confirms Loopback99 exists.Type yes for this exercise so you can see the commit succeed and then practice rolling it back in the next step.
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"
]
}
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.
In this section you used Ansible to deploy a real configuration change through the Configuration Session workflow:
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.