Update switches via Code

A use case I was presented recently was how multiple switches can be upgraded via automation tooling.

Prerequesits

As with any automation, standardisation is key. For this to work the switches should be close to the same model, the ports should be configured the same way, and the configuration running should be the same.

This will configure the interface GigabitEthernet0/5 in trunk mode for VLANs 200-220, and 250

Terraform Code

Project Structure

terraform/
├── main.tf
├── variables.tf
├── terraform.tfvars

main.tf – Main Terraform Config

terraform {
  required_providers {
    ios = {
      source  = "netascode/ios"
      version = ">= 0.5.0"
    }
  }
}

provider "ios" {
  for_each = toset(var.switches)

  host     = each.value
  username = var.username
  password = var.password
}

resource "ios_vlan" "vlan_config" {
  for_each = {
    for switch in var.switches : switch => switch
  }

  vlan_id = flatten([
    for vlan in concat(range(200, 221), [250]) : [
      {
        id   = vlan
        name = "VLAN_${vlan}"
      }
    ]
  ])
  provider = ios[each.key]
}

resource "ios_interface_trunk" "port_config" {
  for_each = {
    for switch in var.switches : switch => switch
  }

  name             = var.trunk_port
  trunk_encapsulation = "dot1q"
  trunk_allowed_vlans = join(",", concat([1], range(200, 221), [250]))
  trunk_native_vlan   = 1

  provider = ios[each.key]
}

variables.tf – Variables and Descriptions

variable "switches" {
  description = "List of switch IPs"
  type        = list(string)
}

variable "username" {
  description = "SSH username"
  type        = string
  sensitive   = true
}

variable "password" {
  description = "SSH password"
  type        = string
  sensitive   = true
}

variable "trunk_port" {
  description = "Trunk interface name"
  type        = string
  default     = "GigabitEthernet0/5"
}

terraform.tfvars – Your Inputs

switches = [
  "192.168.1.101",
  "192.168.1.102",
  "192.168.1.103",
  "192.168.1.104",
  "192.168.1.105"
]

username = "admin"
password = "password"

How to Use It

cd terraform
terraform init
terraform plan
terraform apply

Python code

Whenever I google automation and Cisco switches it comes up that Python is the desired language.

Requirements:

  • Python 3.x
  • netmiko installed (pip install netmiko)
  • IPs/user/pass of the 5 switches
from netmiko import ConnectHandler

# List of switches with credentials
switches = [
    {'device_type': 'cisco_ios', 'host': '192.168.1.101', 'username': 'admin', 'password': 'password'},
    {'device_type': 'cisco_ios', 'host': '192.168.1.102', 'username': 'admin', 'password': 'password'},
    {'device_type': 'cisco_ios', 'host': '192.168.1.103', 'username': 'admin', 'password': 'password'},
    {'device_type': 'cisco_ios', 'host': '192.168.1.104', 'username': 'admin', 'password': 'password'},
    {'device_type': 'cisco_ios', 'host': '192.168.1.105', 'username': 'admin', 'password': 'password'}
]

# VLANs to add
vlans = list(range(200, 221)) + [250]

# Trunk port to modify
trunk_port = "GigabitEthernet0/5"

# Generate configuration commands
vlan_commands = [f"vlan {vlan}" for vlan in vlans]
trunk_command = f"interface {trunk_port}\nswitchport trunk allowed vlan add {','.join(map(str, vlans))}"

# Full config block
config_commands = vlan_commands + [f"interface {trunk_port}",
                                   f"switchport trunk allowed vlan add {','.join(map(str, vlans))}"]

# Process each switch
for switch in switches:
    print(f"Connecting to {switch['host']}...")
    try:
        connection = ConnectHandler(**switch)
        output = connection.send_config_set(config_commands)
        print(f"Configuration applied to {switch['host']}:\n{output}")
        connection.save_config()
        connection.disconnect()
    except Exception as e:
        print(f"Error with {switch['host']}: {e}")

Ansible Code

What about writing this in Ansible

Requirements

Make sure you install the Cisco Ansible Collection:

ansible-galaxy collection install cisco.ios

File Structure

.
├── inventory.yml
└── update_vlans.yml

inventory.yml – Your Inventory of Cisco Switches

all:
  hosts:
    switch1:
      ansible_host: 192.168.1.101
    switch2:
      ansible_host: 192.168.1.102
    switch3:
      ansible_host: 192.168.1.103
    switch4:
      ansible_host: 192.168.1.104
    switch5:
      ansible_host: 192.168.1.105
  vars:
    ansible_user: admin
    ansible_password: password
    ansible_network_os: cisco.ios.ios
    ansible_connection: network_cli

update_vlans.yml – The Ansible Playbook

---
- name: Update trunk port and VLANs on Cisco switches
  hosts: all
  gather_facts: no

  tasks:
    - name: Create VLANs 200-220 and 250
      cisco.ios.ios_vlan:
        vlan_id: "{{ item }}"
        name: "VLAN_{{ item }}"
        state: present
      loop: "{{ range(200, 221) | list + [250] }}"

    - name: Add VLANs to trunk port GigabitEthernet0/5
      cisco.ios.ios_config:
        lines:
          - "switchport trunk allowed vlan add {{ vlan_list | join(',') }}"
        parents: "interface GigabitEthernet0/5"
      vars:
        vlan_list: "{{ range(200, 221) | list + [250] }}"

Run the Playbook

ansible-playbook -i inventory.yml update_vlans.yml

Leave a Reply

Your email address will not be published. Required fields are marked *

Share on Social Media