Ansible and IOSXE - IOS modules
Introduction
In this blog post, we will use Ansible to interact with our IOS XE devices. Ansible has a very extensive set of Network Modules for various devices and vendors. To interact with IOS XE devices, one could use the IOS module. You can find the documentation here.
We will go through a small number of use cases to see how this module works.
For all the examples, we will use a Cisco sandbox environment delivered by Cisco Devnet. Go check out Devnet, really brilliant. To get a list of all sandboxes, check out this link. For this blog post, we will use the IOS XE sandbox.
Show version and show interfaces
Let’s start with a simple use case, which is showing the version and a list of interfaces on our IOS XE device. To do so, we will be using the ios-command
network module which allows us to run arbitrary commands on IOS devices and display the response from the device. This module is documented here.
Let’s start with defining a hosts
file. For this example, it will just include a single entry, which points to the FQDN of the sandbox IOSXE device.
---
[iosxe]
ios-xe-mgmt-latest.cisco.com
Next, let’s define the variables in a group_vars
file called iosxe.yaml
---
ansible_connection: local
ansible_python_interpreter: /usr/bin/python3
host_key_checking: False
ansible_ssh_user: developer
ansible_ssh_pass: ***
ansible_port: 8181
Next, let’s create the script. You will see that we issue the show version
and show ip interface brief
commands one by one and we capture the response in a variable through Ansible register
method. We then use the debug
method to visualize the response.
---
- name: Show examples
hosts: iosxe
gather_facts: no
tasks:
- name: GATHERING FACTS
ios_facts:
gather_subset: hardware
- name: run show version
ios_command:
commands: show version
register: version
- name: display version
debug:
var: version["stdout_lines"][0]
- name: run show ip int brief
ios_command:
commands: show ip interface brief
register: interfaces
- name: display interfaces
debug:
var: interfaces["stdout_lines"][0]
The above script can be run as follows. As expected, we will get back the output of both commands.
wauterw@WAUTERW-M-65P7 ios_modules % ansible-playbook command_show.yaml -i hosts
PLAY [Show examples] ******************************************************************************************************************************************************
TASK [GATHERING FACTS] ****************************************************************************************************************************************************
ok: [ios-xe-mgmt-latest.cisco.com]
TASK [run show version] ***************************************************************************************************************************************************
ok: [ios-xe-mgmt-latest.cisco.com]
TASK [display version] ****************************************************************************************************************************************************
ok: [ios-xe-mgmt-latest.cisco.com] => {
"version[\"stdout_lines\"][0]": [
"Cisco IOS XE Software, Version 16.11.01a",
***Truncated***
"Configuration register is 0x2102"
]
}
TASK [run show ip int brief] **********************************************************************************************************************************************
ok: [ios-xe-mgmt-latest.cisco.com]
TASK [display interfaces] *************************************************************************************************************************************************
ok: [ios-xe-mgmt-latest.cisco.com] => {
"interfaces[\"stdout_lines\"][0]": [
"Interface IP-Address OK? Method Status Protocol",
"GigabitEthernet1 10.10.20.48 YES NVRAM up up ",
"GigabitEthernet2 unassigned YES NVRAM administratively down down ",
"GigabitEthernet3 unassigned YES NVRAM administratively down down ",
"Loopback192 192.0.1.2 YES manual up up"
]
}
PLAY RECAP ****************************************************************************************************************************************************************
ios-xe-mgmt-latest.cisco.com : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
In previous example, we issues both commands seperately (in a seperate tasks that is). We can also write it as follows, which is a little shorter.
---
- name: Getting started
hosts: iosxe
gather_facts: no
tasks:
- name: GATHERING FACTS
ios_facts:
gather_subset: hardware
- name: run multiple commands
ios_command:
commands:
- show version
- show ip interface brief
register: output
- name: display all
debug:
var: output["stdout_lines"]
With the above script, you will get back a list of responses. If you would only be interested to see the output of the show ip interface brief
, you could do this by looking at the output["stdout_lines"][1]
output.
In the above playbook, we added the commands under each other in the playbook itself. Sometimes it’s preferable to read in these commands from the group_vars folder. The following script does exactly that.
---
- name: Getting started
hosts: iosxe
gather_facts: no
tasks:
- name: GATHERING FACTS
ios_facts:
gather_subset: hardware
- name: run multiple commands
ios_command:
commands: "{{ commands }}"
register: output
- name: display all
debug:
var: output["stdout_lines"]
You see that we use a {{ commands}}
reference here. This reference in facts points to the commands
object in the group_vars/iosxe.yaml file. So we need to add these as follows:
ansible_connection : local
ansible_python_interpreter : /usr/bin/python3
host_key_checking : False
ansible_ssh_user : developer
ansible_ssh_pass : C1sco12345
ansible_port: 8181
commands:
- "show version"
- "show ip interface brief"
We have seen three different variations here on the same subject. Pick the one that suits you most.
Add/Delete single interface
Next, we will add and delete some interfaces (instead of just reading them from our device as we did in the use case above). Let’s start with adding some interfaces.
A) Add single interface
In the below Ansible script, we will create a loopback interface on our IOS XE device. We will use the ios_interface
module for that. Information can be found here. As you can see, it allows you to specify the MTU size, the duplex mode and many other things. There is no option to specify an IP address. There is another module for that, called ios_l3_interface
(see documentation). This one takes care of all layer 3 configuration on your interfaces. Hence we are using that one to set the IP address of our interface.
---
- name: Configure loopback using ios_interface
hosts: iosxe
gather_facts: no
tasks:
- name: Creating loopback
ios_interface:
name: Loopback100
enabled: True
description: Loopback interface 100 created with Ansible
- name: Assign IP to loopback
ios_l3_interface:
name: Loopback100
ipv4: 10.10.10.10/32
Let’s execute this playbook:
wauterw@WAUTERW-M-65P7 ios_modules % ansible-playbook -i hosts loopback_create_single.yaml
PLAY [Configure loopback using ios_interface] *****************************************************************************************************************************
TASK [Creating loopback] **************************************************************************************************************************************************
changed: [ios-xe-mgmt-latest.cisco.com]
TASK [Assign IP to loopback] **********************************************************************************************************************************************
changed: [ios-xe-mgmt-latest.cisco.com]
PLAY RECAP ****************************************************************************************************************************************************************
ios-xe-mgmt-latest.cisco.com : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
When logging into the device using SSH, you can verify that the interfaces was added successfully.
csr1000v-1#show ip interface brief
Interface IP-Address OK? Method Status Protocol
GigabitEthernet1 10.10.20.48 YES NVRAM up up
GigabitEthernet2 unassigned YES NVRAM administratively down down
GigabitEthernet3 unassigned YES NVRAM administratively down down
Loopback100 10.10.10.10 YES manual up up
B) Delete interface
Next, let’s delete the interface we just created through an Ansible script. It’s easy as we will use the ios_interface
module again and put the state to absent. Ansible will take care of the removal of the interface.
---
- name: Delete interfaces
hosts: iosxe
gather_facts: no
tasks:
- name: Delete loopback 100
ios_interface:
name: Loopback100
state: absent
Let’s execute this playbook:
wauterw@WAUTERW-M-65P7 ios_modules % ansible-playbook -i hosts loopback_remove_single.yaml
PLAY [Delete interfaces] **************************************************************************************************************************************************
TASK [Delete loopback 100] ************************************************************************************************************************************************
changed: [ios-xe-mgmt-latest.cisco.com]
PLAY RECAP ****************************************************************************************************************************************************************
ios-xe-mgmt-latest.cisco.com : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And indeed, when we login through SSH, you will see the interface got successfully deleted.
csr1000v-1#show ip interface brief
Interface IP-Address OK? Method Status Protocol
GigabitEthernet1 10.10.20.48 YES NVRAM up up
GigabitEthernet2 unassigned YES NVRAM administratively down down
GigabitEthernet3 unassigned YES NVRAM administratively down down
Add/Delete multiple interfaces
Next, let’s make it a bit more complex. Let’s add multiple interfaces to our IOSXE device. We will do so through iterating over some interfaces we list in a variables file.
If you want to follow along, create a file called vars/loopback.yaml
with the following content:
---
loopbacks:
- loopbackname: "Loopback100"
ip: "10.16.100.100"
mask: "32"
- loopbackname: "Loopback101"
ip: "10.16.100.101"
mask: "32"
- loopbackname: "Loopback102"
ip: "10.16.100.102"
mask: "32"
- loopbackname: "Loopback103"
ip: "10.16.100.103"
mask: "32"
A) Add multiple interfaces
In order to add the interfaces from the above variable file, we will loop over them. We start by specifying the vars_files
. Next, we create the loopback interfaces by looping over the loopbacks
defined in the variables file with the with_items
method. We repeat the same method for assigning IP addresses to the interfaces.
---
- name: Configure loopback using ios_interface
hosts: iosxe
gather_facts: no
vars_files:
- vars/loopbacks.yaml
tasks:
- name: Creating loopback
ios_interface:
name: "{{ item.loopbackname }}"
enabled: True
description: Loopback interface 100 created with Ansible
with_items: "{{ loopbacks }}"
- name: Assign IP to loopback
ios_l3_interface:
name: "{{ item.loopbackname }}"
ipv4: "{{ item.ip }}/{{ item.mask }}"
with_items: "{{ loopbacks }}"
- name: Make interface down
ios_interface:
name: "{{ item.loopbackname }}"
enabled: False
with_items: "{{ loopbacks }}"
Let’s execute the Ansible script:
wauterw@WAUTERW-M-65P7 ios_modules % ansible-playbook -i hosts loopback_create_multiple.yaml
PLAY [Configure loopback using ios_interface] *****************************************************************************************************************************
TASK [Creating loopback] **************************************************************************************************************************************************
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback100', 'ip': '10.16.100.100', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback101', 'ip': '10.16.100.101', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback102', 'ip': '10.16.100.102', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback103', 'ip': '10.16.100.103', 'mask': '32'})
TASK [Assign IP to loopback] **********************************************************************************************************************************************
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback100', 'ip': '10.16.100.100', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback101', 'ip': '10.16.100.101', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback102', 'ip': '10.16.100.102', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback103', 'ip': '10.16.100.103', 'mask': '32'})
TASK [Make interface down] ************************************************************************************************************************************************
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback100', 'ip': '10.16.100.100', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback101', 'ip': '10.16.100.101', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback102', 'ip': '10.16.100.102', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback103', 'ip': '10.16.100.103', 'mask': '32'})
PLAY RECAP ****************************************************************************************************************************************************************
ios-xe-mgmt-latest.cisco.com : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And let’s verify on the device itself. You will see indeed our interfaces got added successfully and the correct IP addresses were configured on them.
csr1000v-1#show ip interface brief
Interface IP-Address OK? Method Status Protocol
GigabitEthernet1 10.10.20.48 YES NVRAM up up
GigabitEthernet2 unassigned YES NVRAM administratively down down
GigabitEthernet3 unassigned YES NVRAM administratively down down
Loopback100 10.16.100.100 YES manual administratively down down
Loopback101 10.16.100.101 YES manual administratively down down
Loopback102 10.16.100.102 YES manual administratively down down
Loopback103 10.16.100.103 YES manual administratively down down
B) Remove multiple interfaces
Next, let’s quickly remove these interface as well. Pretty straightforward, we loop again over the interfaces defined in the variables file and we delete them (cfr state: absent).
---
- name: Delete interfaces
hosts: iosxe
gather_facts: no
vars_files:
- vars/loopbacks.yaml
tasks:
- name: Delete loopback interfaces
ios_interface:
name: "{{ item.loopbackname }}"
state: absent
with_items: "{{ loopbacks }}"
Let’s execute the playbook:
wauterw@WAUTERW-M-65P7 ios_modules % ansible-playbook -i hosts loopback_remove_multiple.yaml
PLAY [Delete interfaces] **************************************************************************************************************************************************
TASK [Delete loopback interfaces] *****************************************************************************************************************************************
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback100', 'ip': '10.16.100.100', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback101', 'ip': '10.16.100.101', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback102', 'ip': '10.16.100.102', 'mask': '32'})
changed: [ios-xe-mgmt-latest.cisco.com] => (item={'loopbackname': 'Loopback103', 'ip': '10.16.100.103', 'mask': '32'})
PLAY RECAP ****************************************************************************************************************************************************************
ios-xe-mgmt-latest.cisco.com : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And let’s verify that the interfaces are really removed.
csr1000v-1#show ip interface brief
Interface IP-Address OK? Method Status Protocol
GigabitEthernet1 10.10.20.48 YES NVRAM up up
GigabitEthernet2 unassigned YES NVRAM administratively down down
GigabitEthernet3 unassigned YES NVRAM administratively down down
Change interface description
Next, let’s change the description of an interface. I’m merely showing this example to get familiar with the ios_config
module for which you can find the information here.
In below script, we will change the description of an existing interface.
---
- name: Change interface description
hosts: iosxe
gather_facts: no
tasks:
- name: configure interface settings
ios_config:
lines:
- description Changed through Ansible
- ip address 172.31.1.1 255.255.255.0
parents: interface GigabitEthernet3
Let’s execute the playbook:
wauterw@WAUTERW-M-65P7 ios_modules % ansible-playbook -i hosts change_description.yaml
PLAY [Change interface description] ***************************************************************************************************************************************
TASK [configure interface settings] ***************************************************************************************************************************************
changed: [ios-xe-mgmt-latest.cisco.com]
PLAY RECAP ****************************************************************************************************************************************************************
ios-xe-mgmt-latest.cisco.com : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And indeed, you will see the interface got changed.
csr1000v-1#show inter descr
Interface Status Protocol Description
Gi1 up up MANAGEMENT INTERFACE - DON'T TOUCH ME
Gi2 admin down down Network Interface
Gi3 admin down down Changed through Ansible
Change multiple interface descriptions
Lastly, let’s change multiple interfaces simultaneously. We first create the interfaces by defining them in a variable file (see use case above where this has been covered already) and next, we will change the description. To achieve that, we define a list of interface commands under the with_items
section and we use that information in the parents
attribute.
---
- name: Configure loopback using ios_interface
hosts: iosxe
gather_facts: no
vars_files:
- vars/loopbacks.yaml
tasks:
- name: Creating loopback
ios_interface:
name: "{{ item.loopbackname }}"
enabled: True
description: Loopback interface created with Ansible
with_items: "{{ loopbacks }}"
- name: Change description
ios_config:
lines:
- description Loopback interface changed through Ansible
parents: "{{item}}"
with_items:
- interface Loopback100
- interface Loopback101
- interface Loopback102
- interface Loopback103
Execute the playbook:
wauterw@WAUTERW-M-65P7 ios_modules % ansible-playbook -i hosts change_description_multipleinterfaces.yaml
And look on the device through SSH. The interface description got changed indeed.
csr1000v-1#show interface description
Interface Status Protocol Description
Gi1 up up MANAGEMENT INTERFACE - DON'T TOUCH ME
Gi2 admin down down Network Interface
Gi3 admin down down Network Interface
Lo100 up up Loopback interface changed through Ansible
Lo101 up up Loopback interface changed through Ansible
Lo102 up up Loopback interface changed through Ansible
Lo103 up up Loopback interface changed through Ansible
Change multiple interface descriptions – variant
The above example listed all the interfaces individually under the with_items
section. That was a bit clumsy. So I created also a small variant which handles this a bit more elegant. I won’t go over the script as with the information you learned above it should be pretty straigthforward.
---
- name: Configure loopback using ios_interface
hosts: iosxe
gather_facts: no
vars_files:
- vars/loopbacks.yaml
- vars/loopbacks_interfaces.yaml
tasks:
- name: Creating loopback
ios_interface:
name: "{{ item.loopbackname }}"
enabled: True
description: Loopback interface created with Ansible
with_items: "{{ loopbacks }}"
- name: Change description
ios_config:
lines:
- description Loopback interface changed through Ansible
parents: "{{item.loopbackinterface}}"
with_items: "{{ loopbacks_interfaces }}"
Hope you enjoyed working with Ansible. The scripts I have used in this post, can be found on my Github repo.