Ansible - VLAN Provisioning
Note: There is a newer guide for VLAN provisioning with Ansible 2.5
Ansible is such a powerfull tool that it can be easy to get lost in all the possibilities. Running your "network infrastructure as code" with full configs auto-generated and checked into git is the dream, but we can start simple with automating time-consuming tasks. This post will focus on getting ansible up and running with a playbook to configure new vlans across your switches.
If you have not installed Ansible yet, please see my guide for installing Ansible on Ubuntu 16.04
First, we need to build our Ansible hosts file which is an inventory list of our network devices. We can categorize and group devices within the hosts file, which makes configuring the right devices easier down the road.
root@ubuntu:~# cd /etc/ansible/
root@ubuntu:/etc/ansible# vi hosts
# This is the default ansible 'hosts' file.
#
[networkdevices:children]
switches
routers
[switches]
switch01.thenetworkstack.com
switch02 ansible_host=10.170.200.1
10.170.200.2
[routers]
I created a parent group called networkdevices, and then made two "child" groups called switches and routers. For the VLAN playbook we are going to create, we only really care about the switches group, but I wanted to show how you can easily create a grouping structure. For defining your actual network devices, there are three main options shown above in the switches group:
- The fully-qualified domain name of the device (assuming you have DNS working)
- A local device name, and define the device ip address with the "ansible_host" variable
- The ip address of the device
After you configure your Ansible hosts file with a network device or two, we can start building a playbook. A playbook is Ansible's way of defining a set of tasks. Within a playbook, we will use network modules to run different types of commands (for example, show vs config) on our devices. Ansible comes with support for most major network devices, listed here: http://docs.ansible.com/ansible/latest/list_of_network_modules.html
Let's get started with our playbook. First, give your playbook a name, and make sure it ends in ".yml". To start your YAML file, the first line should be "---", and remember spaces matter. If you get syntax errors, check your spacing on each line:
root@ubuntu:/etc/ansible# vi network_new-vlan.yml
---
- hosts: switches
connection: local
tasks:
- name: Show VLAN
ios_command:
commands: show vlan brief
register: show_vlan
- debug: var=show_vlan.stdout_lines
This simple playbook just runs a command (show vlan brief), registers the output of that command to a variable (show_vlan) and then displays the formatted value of the variable (stdout_lines).
You may notice we don't have any credentials stored in this playbook - we will provide them when we run the playbook (I'm using cisco as my username):
root@ubuntu:/etc/ansible# ansible-playbook network_new-vlan.yml -u cisco -k
SSH password:
PLAY [switches] *******************************************************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [10.170.200.2]
TASK [Show VLAN] *****************************************************************************************************************************************************************
ok: [10.170.200.2]
TASK [debug] **********************************************************************************************************************************************************************
ok: [10.170.200.2] => {
"show_vlan.stdout_lines": [
[
"VLAN Name Status Ports",
"---- -------------------------------- --------- -------------------------------",
"1 default active Gi0/10, Gi0/11, Gi0/12",
"10 FW_VLAN active Gi0/3, Gi0/4, Gi0/5, Gi0/6, Gi0/7",
"20 Data_VLAN active Gi0/1, Gi0/2",
"30 Voice_VLAN active Gi0/1, Gi0/2, Gi0/3, Gi0/4, Gi0/5, Gi0/6, Gi0/7",
"1002 fddi-default act/unsup ",
"1003 token-ring-default act/unsup ",
"1004 fddinet-default act/unsup ",
"1005 trnet-default act/unsup"
]
]
}
PLAY RECAP ************************************************************************************************************************************************************************
10.170.200.2 : ok=3 changed=0 unreachable=0 failed=0
Pretty simple! I currently only have a single device in my Ansible hosts file, but with multiple devices you can issue show commands across every switch in your network. You can also modify the playbook above with any "show" command.
We used the "ios_command" network module above, but to actually make changes, we will need to use the "ios_config" network module.
When creating playbooks for common tasks (like creating a vlan), I try to use variables as much as possible so the playbook is easy to reuse. You can see below that I define the vlan_id and vlan_name variable, which makes them very easy to change when needed - you don't need to edit anything else in the playbook.
---
- hosts: switches
connection: local
vars:
vlan_id: 998
vlan_name: Ansible_VLAN
tasks:
- name: Configure VLAN ID
ios_config:
lines:
- vlan {{ vlan_id }}
- name: Configure VLAN Name
ios_config:
lines:
- name {{ vlan_name }}
parents: vlan {{ vlan_id }}
- name: Show VLAN
ios_command:
commands: show vlan brief
register: show_vlan
- debug: var=show_vlan.stdout_lines
As you can see, we added the VLAN configuration before our "Show VLAN" task. There are two parts to configuring a VLAN - the VLAN ID and the VLAN name. The first task configures the VLAN ID on the switch, then the second task tells Ansible to configure the VLAN name under the VLAN ID we just created. Since the VLAN name is part of a heirarchical configuration in Cisco IOS, we can use the "parents" feature in Ansible to let it know that the name is placed under the VLAN ID configuration.
After the VLAN configuration is completed, our "Show VLAN" task will display the VLAN database with our newly created VLAN.
root@ubuntu:/etc/ansible# ansible-playbook network_new-vlan.yml -u admin -k
SSH password:
PLAY [switches] *******************************************************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [10.170.200.2]
TASK [Configure VLAN ID] **********************************************************************************************************************************************************
changed: [10.170.200.2]
TASK [Configure VLAN Name] ********************************************************************************************************************************************************
changed: [10.170.200.2]
TASK [Show VLAN] ******************************************************************************************************************************************************************
ok: [10.170.200.2]
TASK [debug] **********************************************************************************************************************************************************************
ok: [10.170.200.2] => {
"show_vlan.stdout_lines": [
[
"VLAN Name Status Ports",
"---- -------------------------------- --------- -------------------------------",
"1 default active Gi0/10, Gi0/11, Gi0/12",
"10 FW_VLAN active Gi0/3, Gi0/4, Gi0/5, Gi0/6, Gi0/7",
"20 Data_VLAN active Gi0/1, Gi0/2",
"30 Voice_VLAN active Gi0/1, Gi0/2, Gi0/3, Gi0/4, Gi0/5, Gi0/6, Gi0/7",
"998 Ansible_VLAN active ",
"1002 fddi-default act/unsup ",
"1003 token-ring-default act/unsup ",
"1004 fddinet-default act/unsup ",
"1005 trnet-default act/unsup"
]
]
}
PLAY RECAP ************************************************************************************************************************************************************************
10.170.200.2 : ok=5 changed=2 unreachable=0 failed=0
root@ubuntu:/etc/ansible#
Success!!
Now when a new VLAN needs to be provisioned, just change the vlan_id and vlan_name vars, and you can configure a VLAN across your entire network in less than a minute.
You can also find the hosts file and playbook on my github: https://github.com/dkuchenski/thenetworkstack-ansible/tree/master/vlan-provisioning