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