Introduction
In the rapidly evolving landscape of network infrastructure, traditional manual configuration of VLANs is prone to errors, inconsistency, and slow deployment cycles. As networks scale and business demands accelerate, a more robust, auditable, and automated approach becomes indispensable. This chapter introduces the GitOps workflow for VLAN configuration management, a paradigm that brings the best practices of modern software development to network operations.
GitOps, at its core, leverages Git as the single source of truth for declarative infrastructure and application configurations. For VLANs, this means defining desired VLAN states in version-controlled files, with automated processes ensuring that the actual network state continuously converges with the state declared in Git.
What this chapter covers:
- Core GitOps Principles: Understanding the foundational concepts that enable declarative, version-controlled network configuration.
- VLAN Configuration as Code (IaC): Representing VLANs in structured, human-readable data formats.
- CI/CD Pipeline Integration: How continuous integration and continuous deployment principles apply to network changes.
- Multi-Vendor Automation: Practical examples using tools like Ansible and Terraform for Cisco, Juniper, and Arista devices.
- Advanced VLAN Concepts: Incorporating IEEE 802.1Q and 802.1ad standards within an automated framework.
- Security, Verification, and Troubleshooting: Ensuring robust, secure, and resilient VLAN deployments.
- Performance Optimization: Strategies to fine-tune VLAN performance in large-scale environments.
Why it’s important:
Implementing a GitOps workflow for VLANs dramatically enhances operational efficiency, reduces human error, provides an immutable audit trail, simplifies disaster recovery, and ensures consistency across diverse network estates. This approach is critical for modern enterprises aiming for agility, reliability, and security in their network operations.
What you’ll be able to do after reading this chapter:
- Design and implement a GitOps-driven pipeline for VLAN configuration.
- Create declarative VLAN configurations for multi-vendor environments.
- Automate VLAN deployment, modification, and deletion using Ansible and Terraform.
- Apply advanced security best practices for VLANs in an automated fashion.
- Effectively verify and troubleshoot VLAN issues within a GitOps framework.
Technical Concepts
GitOps Principles for Network Configuration
GitOps extends the DevOps philosophy by using Git as the declarative single source of truth for both infrastructure and applications. For network engineering, this means:
- Declarative Configuration: All VLAN configurations (IDs, names, port assignments, IP interfaces) are described in a declarative manner, specifying what the desired state is, not how to achieve it.
- Version Control (Git): Git acts as the central repository for all network configurations. Every change, review, and approval is tracked, providing an immutable audit log. This enables easy rollbacks to previous states.
- Pull Requests (PRs): Network changes are proposed via PRs, triggering reviews and automated checks (CI). This fosters collaboration and prevents unauthorized or erroneous configurations from being deployed.
- Automated Synchronization: A Continuous Delivery (CD) process automatically applies approved configuration changes from Git to the network devices. This can be a “push” model (CI/CD pipeline pushes changes) or a “pull” model (an agent on the network, or a controller, pulls changes from Git).
- Continuous Reconciliation: The system continuously monitors the actual state of the network and compares it to the desired state in Git. Any drift is detected and automatically rectified to maintain the desired configuration.
Benefits of GitOps for VLANs:
- Increased Reliability: Version control and PRs reduce human error.
- Faster MTTR (Mean Time To Recovery): Easy rollback to any known good state.
- Enhanced Security: All changes are reviewed, audited, and approved.
- Improved Compliance: An immutable audit trail of all configurations.
- Scalability: Automate repetitive tasks across hundreds or thousands of devices.
VLAN Configuration as Code (IaC)
Infrastructure as Code (IaC) is fundamental to GitOps. For VLANs, this involves representing the desired state of VLANs and their assignments using structured data formats like YAML or JSON. This declarative approach allows automation tools to interpret the desired state and apply it to various network devices.
# Example: VLANs definition in a YAML file
---
vlans:
- id: 10
name: "PRODUCTION_SERVERS"
description: "VLAN for critical production servers"
ip_interface: "10.10.10.1/24"
devices:
- name: "core_switch_1"
ports:
- "GigabitEthernet1/0/1"
- "GigabitEthernet1/0/2"
trunk_ports:
- "GigabitEthernet1/0/24"
- name: "access_switch_2"
ports:
- "GigabitEthernet0/1"
- id: 20
name: "DEVELOPMENT_LAB"
description: "VLAN for development environment"
ip_interface: "10.20.20.1/24"
devices:
- name: "core_switch_1"
ports: []
trunk_ports:
- "GigabitEthernet1/0/24"
- name: "access_switch_1"
ports:
- "GigabitEthernet0/5"
CI/CD Pipeline for Network Automation
A CI/CD pipeline automates the lifecycle of network changes, from code commit to deployment.
- Commit: A network engineer pushes a change (e.g., a new VLAN definition) to a Git repository.
- CI (Continuous Integration):
- Linting/Syntax Check: Ensures the configuration files adhere to proper YAML/JSON syntax.
- Validation: Custom scripts or tools validate the proposed VLAN changes against business rules (e.g., VLAN ID ranges, naming conventions, preventing overlaps).
- Pre-Flight Checks: (Optional) Simulate changes against device configurations or network state using tools like Batfish or network testing frameworks.
- PR Review & Approval: The proposed changes are reviewed by peers.
- CD (Continuous Deployment):
- Deployment: Upon approval and merge to the main branch, automation tools (Ansible, Terraform) execute, applying the new VLAN configurations to the network devices. This might involve dry runs, phased rollouts, and pre/post-checks.
- Verification: Automated tests confirm the deployed VLANs are operational and meet requirements (e.g., ping tests,
showcommand parsing). - Rollback: If verification fails or issues are detected, the pipeline can automatically revert to the previous known-good configuration.
Protocol Specifications: IEEE 802.1Q and 802.1ad (QinQ)
GitOps manages the desired state of VLANs, which are defined by these IEEE standards.
- IEEE 802.1Q (VLAN Tagging): This standard defines the architecture for Virtual Local Area Networks and specifies the VLAN tag format that is inserted into an Ethernet frame. The tag includes a 12-bit VLAN Identifier (VID), allowing for 4094 unique VLANs (0 and 4095 are reserved).
- RFC Reference: While 802.1Q is an IEEE standard, its principles are widely implemented and understood in network RFCs related to Layer 2 and Layer 3 operations. The latest version is IEEE 802.1Q-2022.
- IEEE 802.1ad (QinQ / Provider Bridging): An amendment to 802.1Q, also known as Q-in-Q, allows for the insertion of multiple 802.1Q tags into a single Ethernet frame. This is crucial for service providers to transport customer VLANs while maintaining their own network’s segmentation, effectively providing “VLANs within VLANs.” The outer tag is often called the “Service VLAN ID” (S-VID) and the inner the “Customer VLAN ID” (C-VID).
GitOps Workflow Architecture Diagram
@startuml
!theme mars
' Define all elements first
actor "Network Engineer" as NE
rectangle "Git Repository (Single Source of Truth)" as Git
cloud "CI/CD Pipeline" as CICD
component "Ansible Automation Engine" as Ansible
component "Terraform Provisioner" as Terraform
node "Cisco Switch" as CiscoSW
node "Juniper Router" as JuniperRT
node "Arista Switch" as AristaSW
rectangle "Monitoring & Alerting" as Monitor
' Then connect them
NE -- Git : 1. Commit/Push Config Changes
Git --> CICD : 2. Webhook/Poll (New Changes)
CICD -- Git : 3. CI: Linting, Validation (Pull Config)
CICD --> Ansible : 4. CD: Trigger Ansible Playbook
CICD --> Terraform : 4. CD: Trigger Terraform Apply
Ansible --> CiscoSW : 5. Apply VLAN Config
Ansible --> JuniperRT
Ansible --> AristaSW
Terraform --> CiscoSW : 5. Provision Network Resources
Terraform --> JuniperRT
Terraform --> AristaSW
CiscoSW --> Monitor : 6. Telemetry/Logs
JuniperRT --> Monitor
AristaSW --> Monitor
Monitor -- NE : 7. Alerts on Drift/Issues
@enduml
802.1Q VLAN Tag Packet Structure
When an Ethernet frame is tagged with 802.1Q, a 4-byte tag header is inserted between the Source Address and the EtherType/Length fields.
packetdiag {
colwidth = 32
0-47: Destination MAC Address
48-95: Source MAC Address
96-111: EtherType (0x8100 for 802.1Q)
112-114: Priority Code Point (PCP) (3 bits)
115: Drop Eligible Indicator (DEI) (1 bit)
116-127: VLAN Identifier (VID) (12 bits)
128-143: Length/EtherType of original frame
144-X: Original Ethernet Payload
X-Y: Frame Check Sequence (FCS)
}
Configuration Examples
This section provides example configurations for creating VLANs and assigning them to ports on Cisco, Juniper, and Arista devices. These are the target configurations that an automation tool would push.
Scenario: Creating VLAN 10 (PRODUCTION_SERVERS) and VLAN 20 (DEVELOPMENT_LAB)
- VLAN 10:
PRODUCTION_SERVERS, IP10.10.10.1/24(on L3 switch/router) - VLAN 20:
DEVELOPMENT_LAB, IP10.20.20.1/24(on L3 switch/router) - Access Port: GigabitEthernet1/0/1 for VLAN 10, GigabitEthernet1/0/5 for VLAN 20
- Trunk Port: GigabitEthernet1/0/24 (allowing VLANs 10, 20)
Cisco IOS XE Configuration
! Configure VLANs
vlan 10
name PRODUCTION_SERVERS
!
vlan 20
name DEVELOPMENT_LAB
!
! Configure Layer 3 VLAN interfaces (SVI)
interface Vlan10
description IP Interface for PRODUCTION_SERVERS VLAN
ip address 10.10.10.1 255.255.255.0
no shutdown
!
interface Vlan20
description IP Interface for DEVELOPMENT_LAB VLAN
ip address 10.20.20.1 255.255.255.0
no shutdown
!
! Configure an access port for VLAN 10
interface GigabitEthernet1/0/1
description Access port for Production Server
switchport mode access
switchport access vlan 10
switchport nonegotiate
spanning-tree portfast
!
! Configure an access port for VLAN 20
interface GigabitEthernet1/0/5
description Access port for Development Lab PC
switchport mode access
switchport access vlan 20
switchport nonegotiate
spanning-tree portfast
!
! Configure a trunk port allowing VLANs 10 and 20
! WARNING: Ensure native VLAN is secure and not VLAN 1.
! Best practice: Set an unused VLAN as native or explicitly remove native VLAN from trunk.
interface GigabitEthernet1/0/24
description Trunk to other Core Switch
switchport mode trunk
switchport trunk allowed vlan 10,20
switchport trunk native vlan 999 ! Assign an unused VLAN as native
switchport nonegotiate
!
Juniper Junos OS Configuration
# Configure VLANs
set vlans PRODUCTION_SERVERS vlan-id 10
set vlans DEVELOPMENT_LAB vlan-id 20
#
# Configure Layer 3 VLAN interfaces (IRB - Integrated Routing and Bridging)
set interfaces irb unit 10 family inet address 10.10.10.1/24
set interfaces irb unit 20 family inet address 10.20.20.1/24
#
# Bind IRB interfaces to VLANs
set vlans PRODUCTION_SERVERS l3-interface irb.10
set vlans DEVELOPMENT_LAB l3-interface irb.20
#
# Configure an access port for VLAN 10
set interfaces ge-0/0/1 unit 0 family ethernet-switching interface-mode access
set interfaces ge-0/0/1 unit 0 family ethernet-switching vlan members PRODUCTION_SERVERS
#
# Configure an access port for VLAN 20
set interfaces ge-0/0/5 unit 0 family ethernet-switching interface-mode access
set interfaces ge-0/0/5 unit 0 family ethernet-switching vlan members DEVELOPMENT_LAB
#
# Configure a trunk port allowing VLANs 10 and 20
# WARNING: Ensure native VLAN is secure and not VLAN 1.
# Best practice: Assign an unused VLAN as native or disable native VLAN if possible.
set interfaces ge-0/0/24 unit 0 family ethernet-switching interface-mode trunk
set interfaces ge-0/0/24 unit 0 family ethernet-switching vlan members [ PRODUCTION_SERVERS DEVELOPMENT_LAB ]
set interfaces ge-0/0/24 unit 0 family ethernet-switching native-vlan-id 999
#
Arista EOS Configuration
! Configure VLANs
vlan 10
name PRODUCTION_SERVERS
!
vlan 20
name DEVELOPMENT_LAB
!
! Configure Layer 3 VLAN interfaces (SVI)
interface Vlan10
description IP Interface for PRODUCTION_SERVERS VLAN
ip address 10.10.10.1/24
no shutdown
!
interface Vlan20
description IP Interface for DEVELOPMENT_LAB VLAN
ip address 10.20.20.1/24
no shutdown
!
! Configure an access port for VLAN 10
interface Ethernet1/1
description Access port for Production Server
switchport mode access
switchport access vlan 10
switchport host
!
! Configure an access port for VLAN 20
interface Ethernet1/5
description Access port for Development Lab PC
switchport mode access
switchport access vlan 20
switchport host
!
! Configure a trunk port allowing VLANs 10 and 20
! WARNING: Ensure native VLAN is secure and not VLAN 1.
! Best practice: Set an unused VLAN as native or explicitly remove native VLAN from trunk.
interface Ethernet1/24
description Trunk to other Core Switch
switchport mode trunk
switchport trunk allowed vlan 10,20
switchport trunk native vlan 999
!
Network Diagrams
Multi-Vendor Core & Access Layer Topology
This diagram illustrates a common enterprise setup with core and access layer switches, demonstrating where VLANs are typically configured and how they span the network.
nwdiag {
network core_network {
address = "10.0.0.0/16"
core_switch_cisco [address = "10.0.0.1", shape = cisco];
core_switch_juniper [address = "10.0.0.2", shape = juniper];
}
network access_network_1 {
address = "10.10.10.0/24"
access_switch_arista [address = "10.10.10.254", shape = arista];
access_switch_arista -- core_switch_cisco [label = "Trunk (VLAN 10, 20)"];
}
network access_network_2 {
address = "10.20.20.0/24"
access_switch_cisco [address = "10.20.20.254", shape = cisco];
access_switch_cisco -- core_switch_juniper [label = "Trunk (VLAN 10, 20)"];
}
group {
label = "VLAN 10: Production Servers";
color = "#CCFFCC";
server1 [address = "10.10.10.10"];
server2 [address = "10.10.10.11"];
server1 -- access_switch_arista [label = "Access Port (VLAN 10)"];
server2 -- access_switch_arista [label = "Access Port (VLAN 10)"];
}
group {
label = "VLAN 20: Development Lab";
color = "#FFCCCC";
dev_pc1 [address = "10.20.20.10"];
dev_pc2 [address = "10.20.20.11"];
dev_pc1 -- access_switch_cisco [label = "Access Port (VLAN 20)"];
dev_pc2 -- access_switch_cisco [label = "Access Port (VLAN 20)"];
}
}
GitOps Configuration Flow
This diagram visualizes the end-to-end GitOps flow for managing VLAN configurations.
# GitOps Configuration Flow
direction: right
engineer: Network Engineer
git_repo: Git Repository {
label: "Network Config (YAML)"
shape: cylinder
}
ci_cd_pipeline: CI/CD Pipeline {
validate: Validation & Linting
deploy: Deployment
}
ansible_engine: Ansible Engine
devices: Network Devices {
cisco_sw: Cisco Switch
juniper_rt: Juniper Router
arista_sw: Arista Switch
}
monitor: Monitoring & Telemetry {
shape: cloud
}
engineer -> git_repo: 1. Push Config Changes (PR)
git_repo -> ci_cd_pipeline.validate: 2. Trigger CI
ci_cd_pipeline.validate -> ci_cd_pipeline.deploy: 3. CI Pass (Approved Merge)
ci_cd_pipeline.deploy -> ansible_engine: 4. Trigger Playbook
ansible_engine -> cisco_sw: 5. Apply Config
ansible_engine -> juniper_rt
ansible_engine -> arista_sw
devices -> monitor: 6. Telemetry & Logs
monitor -> engineer: 7. Alerts (Drift, Issues)
cisco_sw.label: "VLANs Configured"
juniper_rt.label: "VLANs Configured"
arista_sw.label: "VLANs Configured"
engineer.style.font-color: black
Automation Examples
The power of GitOps for VLAN management comes from automation. Here, we demonstrate how Ansible and Terraform can be used.
Ansible Playbook for Multi-Vendor VLAN Configuration
This Ansible playbook reads VLAN data from the group_vars/all/vlans.yml file and applies it to Cisco, Juniper, and Arista devices using their respective network modules.
inventory.ini:
[cisco_devices]
cisco_core_switch_1 ansible_host=192.168.1.11
[juniper_devices]
juniper_core_router_1 ansible_host=192.168.1.12
[arista_devices]
arista_access_switch_1 ansible_host=192.168.1.13
[network_devices:children]
cisco_devices
juniper_devices
arista_devices
[all:vars]
ansible_user=admin
ansible_password=Cisco123!
ansible_network_os_cisco_devices=ios
ansible_network_os_juniper_devices=junos
ansible_network_os_arista_devices=eos
ansible_become=yes
ansible_become_method=enable
ansible_connection=network_cli
group_vars/all/vlans.yml:
---
# Define desired VLAN states across the network
# This file serves as the source of truth for VLANs
network_vlans:
- id: 10
name: PRODUCTION_SERVERS
description: VLAN for critical production servers
ip_address: 10.10.10.1/24
devices:
cisco_core_switch_1:
access_ports: ['GigabitEthernet1/0/1', 'GigabitEthernet1/0/2']
trunk_ports: ['GigabitEthernet1/0/24']
trunk_allowed_vlan: '10,20'
trunk_native_vlan: 999
arista_access_switch_1:
access_ports: ['Ethernet1/1']
trunk_ports: ['Ethernet1/24']
trunk_allowed_vlan: '10,20'
trunk_native_vlan: 999
juniper_core_router_1:
access_ports: ['ge-0/0/1']
trunk_ports: ['ge-0/0/24']
trunk_allowed_vlan: '10,20' # Juniper uses members [VLAN_NAME]
trunk_native_vlan: 999
- id: 20
name: DEVELOPMENT_LAB
description: VLAN for development environment
ip_address: 10.20.20.1/24
devices:
cisco_core_switch_1:
access_ports: ['GigabitEthernet1/0/5']
trunk_ports: ['GigabitEthernet1/0/24']
trunk_allowed_vlan: '10,20'
trunk_native_vlan: 999
arista_access_switch_1:
access_ports: ['Ethernet1/5']
trunk_ports: ['Ethernet1/24']
trunk_allowed_vlan: '10,20'
trunk_native_vlan: 999
juniper_core_router_1:
access_ports: ['ge-0/0/5']
trunk_ports: ['ge-0/0/24']
trunk_allowed_vlan: '10,20' # Juniper uses members [VLAN_NAME]
trunk_native_vlan: 999
configure_vlans.yml playbook:
---
- name: Apply VLAN Configuration using GitOps Workflow
hosts: network_devices
connection: network_cli
gather_facts: false
tasks:
- name: Ensure VLANs exist (Cisco, Arista, Juniper)
tags: configure_vlans
block:
- name: Configure VLAN on Cisco IOS/IOS-XE devices
ansible.netcommon.cli_config:
config: |
vlan
name
save_when: modified
when:
- ansible_network_os == 'ios'
- inventory_hostname in item.devices | dict2items | map(attribute='key') | list
loop: ""
loop_control:
label: "Cisco VLAN "
- name: Configure VLAN on Juniper Junos devices
juniper.device.vlans:
config:
- name: ""
vlan_id: ""
state: merged
when:
- ansible_network_os == 'junos'
- inventory_hostname in item.devices | dict2items | map(attribute='key') | list
loop: ""
loop_control:
label: "Juniper VLAN "
- name: Configure VLAN on Arista EOS devices
arista.eos.vlan:
vlan_id: ""
name: ""
state: present
when:
- ansible_network_os == 'eos'
- inventory_hostname in item.devices | dict2items | map(attribute='key') | list
loop: ""
loop_control:
label: "Arista VLAN "
- name: Configure VLAN Interfaces (SVI/IRB) and Port Assignments
tags: configure_interfaces
block:
- name: Configure VLAN Interfaces and ports on Cisco IOS/IOS-XE
ansible.netcommon.cli_config:
config: |
{% for vlan in network_vlans %}
{% if inventory_hostname in vlan.devices %}
interface Vlan
description IP Interface for VLAN
ip address
no shutdown
{% for port in vlan.devices[inventory_hostname].access_ports %}
interface
description Access port for
switchport mode access
switchport access vlan
switchport nonegotiate
spanning-tree portfast
{% endfor %}
{% for port in vlan.devices[inventory_hostname].trunk_ports %}
interface
description Trunk to other switch
switchport mode trunk
switchport trunk allowed vlan
switchport trunk native vlan
switchport nonegotiate
{% endfor %}
{% endif %}
{% endfor %}
save_when: modified
when: ansible_network_os == 'ios'
- name: Configure VLAN Interfaces and ports on Juniper Junos
juniper.device.config:
lines:
{% for vlan in network_vlans %}
{% if inventory_hostname in vlan.devices %}
- "set interfaces irb unit family inet address "
- "set vlans l3-interface irb."
{% for port in vlan.devices[inventory_hostname].access_ports %}
- "set interfaces unit 0 family ethernet-switching interface-mode access"
- "set interfaces unit 0 family ethernet-switching vlan members "
{% endfor %}
{% for port in vlan.devices[inventory_hostname].trunk_ports %}
- "set interfaces unit 0 family ethernet-switching interface-mode trunk"
- "set interfaces unit 0 family ethernet-switching vlan members [ {% for member_vlan_id in vlan.devices[inventory_hostname].trunk_allowed_vlan.split(',') %} {% endfor %} ]"
- "set interfaces unit 0 family ethernet-switching native-vlan-id "
{% endfor %}
{% endif %}
{% endfor %}
config_mode: exclusive
comment: "Ansible: Configure VLAN Interfaces and ports"
when: ansible_network_os == 'junos'
- name: Configure VLAN Interfaces and ports on Arista EOS
ansible.netcommon.cli_config:
config: |
{% for vlan in network_vlans %}
{% if inventory_hostname in vlan.devices %}
interface Vlan
description IP Interface for VLAN
ip address
no shutdown
{% for port in vlan.devices[inventory_hostname].access_ports %}
interface
description Access port for
switchport mode access
switchport access vlan
switchport host
{% endfor %}
{% for port in vlan.devices[inventory_hostname].trunk_ports %}
interface
description Trunk to other switch
switchport mode trunk
switchport trunk allowed vlan
switchport trunk native vlan
{% endfor %}
{% endif %}
{% endfor %}
save_when: modified
when: ansible_network_os == 'eos'
- name: Run verification commands
ansible.builtin.include_tasks: verification_tasks.yml
tags: verify_config
verification_tasks.yml (included in the main playbook):
- name: Verify VLANs on Cisco IOS/IOS-XE
ansible.netcommon.cli_command:
command: "show vlan brief"
register: cisco_vlan_output
when: ansible_network_os == 'ios'
- name: Display Cisco VLANs
ansible.builtin.debug:
msg: ""
when: ansible_network_os == 'ios'
- name: Verify VLANs on Juniper Junos
ansible.netcommon.cli_command:
command: "show vlans"
register: juniper_vlan_output
when: ansible_network_os == 'junos'
- name: Display Juniper VLANs
ansible.builtin.debug:
msg: ""
when: ansible_network_os == 'junos'
- name: Verify VLANs on Arista EOS
ansible.netcommon.cli_command:
command: "show vlan"
register: arista_vlan_output
when: ansible_network_os == 'eos'
- name: Display Arista VLANs
ansible.builtin.debug:
msg: ""
when: ansible_network_os == 'eos'
Terraform for Network Infrastructure Provisioning
While Ansible excels at configuration management on existing devices, Terraform is ideal for provisioning the underlying network infrastructure itself, especially in cloud or SDN/ACI environments. Terraform can define VPCs, subnets, and even virtual network appliances, which then form the foundation for VLAN-like segmentation or where Ansible would then configure VLANs on physical devices.
Example: Terraform for AWS VPC and Subnet Provisioning (conceptual VLAN segmentation)
# main.tf for AWS VPC provisioning
provider "aws" {
region = "us-east-1"
}
resource "aws_vpc" "prod_vpc" {
cidr_block = "10.10.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "Production-VPC"
ManagedBy = "Terraform-GitOps"
}
}
resource "aws_subnet" "prod_subnet_servers" {
vpc_id = aws_vpc.prod_vpc.id
cidr_block = "10.10.10.0/24" # Corresponds to VLAN 10 functionally
availability_zone = "us-east-1a"
tags = {
Name = "Production-Servers-Subnet"
VLAN_ID_Concept = "10" # Conceptual mapping for documentation
}
}
resource "aws_subnet" "prod_subnet_dev" {
vpc_id = aws_vpc.prod_vpc.id
cidr_block = "10.10.20.0/24" # Corresponds to VLAN 20 functionally
availability_zone = "us-east-1a"
tags = {
Name = "Development-Lab-Subnet"
VLAN_ID_Concept = "20" # Conceptual mapping for documentation
}
}
output "prod_vpc_id" {
value = aws_vpc.prod_vpc.id
description = "The ID of the production VPC"
}
output "prod_subnet_servers_id" {
value = aws_subnet.prod_subnet_servers.id
description = "The ID of the production servers subnet"
}
This Terraform example provisions cloud resources that functionally replicate the isolation provided by VLANs. Ansible could then connect to instances within these subnets and perform further configuration.
Python for State Synchronization (Netmiko)
Python scripts, often using libraries like Netmiko, NAPALM, or Nornir, are crucial for custom automation tasks, fetching device state, or as components within larger CI/CD pipelines.
Example: Python script to fetch VLAN configuration (Netmiko)
import os
from netmiko import ConnectHandler
# Device details (ideally from a secure config management system or environment variables)
cisco_device = {
"device_type": "cisco_ios",
"host": os.getenv("CISCO_HOST"),
"username": os.getenv("CISCO_USER"),
"password": os.getenv("CISCO_PASSWORD"),
"secret": os.getenv("CISCO_ENABLE_SECRET"),
}
juniper_device = {
"device_type": "juniper_junos",
"host": os.getenv("JUNIPER_HOST"),
"username": os.getenv("JUNIPER_USER"),
"password": os.getenv("JUNIPER_PASSWORD"),
}
def get_vlan_config(device):
"""Connects to a device and retrieves VLAN configuration."""
print(f"Connecting to {device['host']}...")
try:
with ConnectHandler(**device) as net_connect:
if device['device_type'] == 'cisco_ios':
output = net_connect.send_command("show vlan brief", use_textfsm=True)
elif device['device_type'] == 'juniper_junos':
output = net_connect.send_command("show vlans", use_textfsm=True)
elif device['device_type'] == 'arista_eos': # Assuming similar command to Cisco
output = net_connect.send_command("show vlan", use_textfsm=True)
else:
print(f"Unsupported device type: {device['device_type']}")
return None
print(f"VLAN configuration from {device['host']}:\n{output}")
return output
except Exception as e:
print(f"Error connecting to {device['host']}: {e}")
return None
if __name__ == "__main__":
# Ensure environment variables are set for actual execution
# For testing, you might hardcode values or use a .env file
# Example:
# export CISCO_HOST='192.168.1.11'
# export CISCO_USER='admin'
# export CISCO_PASSWORD='password'
# export CISCO_ENABLE_SECRET='enable_password'
print("Fetching Cisco VLANs:")
cisco_vlans = get_vlan_config(cisco_device)
print("\nFetching Juniper VLANs:")
juniper_vlans = get_vlan_config(juniper_device)
# You can add Arista device details and call get_vlan_config as well
Security Considerations
VLANs are a foundational element for network segmentation and security, but they are not inherently secure. A GitOps approach helps by enforcing review and auditability, but specific security measures must be part of the declarative configuration.
Attack Vectors
- VLAN Hopping:
- Switch Spoofing (DTP): An attacker sends Dynamic Trunking Protocol (DTP) messages to a switch, tricking it into forming a trunk link. Once a trunk, the attacker can access all VLANs allowed on that trunk.
- Double Tagging (802.1Q Tunneling): An attacker encapsulates a frame with two 802.1Q tags. If the first tag matches the native VLAN of the trunk, the switch strips it, exposing the second tag which then allows the frame to enter a targeted VLAN.
- MAC Flooding: Overwhelming the switch’s MAC address table, forcing it into a hub-like state where it floods all traffic, making it visible to attackers.
- DHCP Snooping Bypass: Exploiting DHCP to gain unauthorized IP addresses or launch denial-of-service attacks.
- ARP Spoofing: Intercepting traffic by falsely associating MAC addresses with IP addresses.
Mitigation Strategies
- Disable DTP: Manually configure trunk links (e.g.,
switchport mode trunkon Cisco,interface-mode trunkon Juniper/Arista) and explicitly disable DTP (switchport nonegotiateon Cisco). - Change Native VLAN: Assign an unused, dedicated VLAN ID (e.g., VLAN 999) as the native VLAN on all trunks, and ensure no devices are on this VLAN. Better yet, configure trunks to not have a native VLAN if the platform supports it and it’s compatible with connected devices.
- Remove Unused VLANs from Trunks: Implement VLAN pruning (e.g.,
switchport trunk allowed vlan <list>on Cisco) to restrict which VLANs are propagated over trunk links. - Port Security: Limit the number of MAC addresses allowed on an access port. Dynamically learn MACs or statically configure them. (e.g.,
switchport port-securityon Cisco). - Disable Unused Ports: Shut down all unused physical switch ports (
shutdowncommand). - Storm Control: Prevent broadcast, multicast, and unicast storms on interfaces.
- DHCP Snooping: Enable DHCP snooping to validate DHCP messages and prevent rogue DHCP servers.
- Dynamic ARP Inspection (DAI): Mitigate ARP spoofing attacks by validating ARP packets.
- IP Source Guard: Prevent IP spoofing by filtering traffic based on source IP and MAC addresses.
- Private VLANs (PVLANs): Isolate ports within the same VLAN, preventing communication between devices on specific ports even if they are in the same subnet. This provides micro-segmentation at Layer 2.
Security Configuration Examples (Cisco IOS XE)
! Mitigation: Disable DTP, change native VLAN, prune allowed VLANs
interface GigabitEthernet1/0/24
description Secure Trunk
switchport mode trunk
switchport nonegotiate
switchport trunk allowed vlan 10,20,30,40 ! Only explicitly allowed
switchport trunk native vlan 999 ! Unused VLAN for native
! Mitigation: Port Security on Access Ports
interface GigabitEthernet1/0/1
description Secure Access Port
switchport mode access
switchport access vlan 10
switchport port-security
switchport port-security maximum 2 ! Allow max 2 MAC addresses
switchport port-security violation restrict ! Drop packets, send SNMP trap
switchport port-security mac-address sticky ! Learn MACs dynamically
!
! Mitigation: Disable unused ports
interface GigabitEthernet1/0/10
description Unused port
shutdown
!
! Mitigation: DHCP Snooping (globally and per VLAN)
ip dhcp snooping
ip dhcp snooping vlan 10,20
ip dhcp snooping database flash:dhcp_snooping_db
!
! Mitigation: Trust DHCP Snooping on Uplinks (Trunks)
interface GigabitEthernet1/0/24
ip dhcp snooping trust
!
! Mitigation: Dynamic ARP Inspection (globally and per VLAN)
ip arp inspection vlan 10,20
!
! Mitigation: Trust DAI on Uplinks (Trunks)
interface GigabitEthernet1/0/24
ip arp inspection trust
!
! Mitigation: IP Source Guard (on access ports)
interface GigabitEthernet1/0/1
ip verify source port-security
!
Security Warning: Improper configuration of VLANs, especially native VLANs on trunks, DTP settings, and port security, can lead to network vulnerabilities. Always validate changes thoroughly in a lab environment before deploying to production. GitOps helps by making such validations part of the automated workflow.
Verification & Troubleshooting
Even with GitOps, issues can arise from incorrect declarative state, automation tool failures, or unexpected device behavior. A robust verification and troubleshooting strategy is essential.
Verification Commands
These commands confirm the operational status of VLANs and their interfaces.
Cisco IOS XE:
show vlan brief
show interface status
show interface trunk
show interface GigabitEthernet1/0/1 switchport
show ip interface brief
show run | section vlan|interface
Juniper Junos OS:
show vlans
show interfaces terse | grep ethernet-switching
show interfaces ge-0/0/1
show interfaces descriptions
show interfaces irb
Arista EOS:
show vlan
show interfaces status
show interfaces trunk
show interfaces Ethernet1/1 switchport
show ip interface brief
show running-config | section vlan|interface
Expected Output (Example for Cisco show vlan brief)
VLAN Name Status Ports
---- -------------------------------- --------- -------------------------------
1 default active Gi1/0/10, Gi1/0/11, Gi1/0/12
10 PRODUCTION_SERVERS active Gi1/0/1, Gi1/0/2, Gi1/0/24
20 DEVELOPMENT_LAB active Gi1/0/5, Gi1/0/24
999 UNUSED_NATIVE_VLAN active
- Annotation: This output confirms that VLANs 10, 20, and 999 are active and correctly associated with their respective access and trunk ports.
Troubleshooting Common Issues
| Issue | Probable Cause | Debug Commands / Verification Steps | Resolution Steps |
|---|---|---|---|
| VLAN ID Mismatch | Inconsistent VLAN ID configured across devices. | show vlan brief, show interfaces trunk (Cisco) | Ensure VLAN IDs are uniform across all devices in the declarative config. |
| Trunk Misconfiguration | Mode mismatch (access/trunk), allowed VLANs. | show interfaces trunk (all vendors) | Correct trunk mode, ensure allowed VLANs list is correct, check native VLAN. |
| Native VLAN Mismatch | Native VLAN differs on connected trunk ports. | show interfaces trunk (Cisco/Arista), show interfaces (Juniper) | Standardize native VLAN across the trunk, ideally to an unused VLAN. |
| Access Port Incorrect VLAN | Port assigned to wrong VLAN or configured as trunk. | show interfaces <port> switchport (Cisco/Arista) | Set port mode to access and assign correct access VLAN. |
| Layer 3 SVI/IRB Issues | IP address mismatch, no shutdown missing, no IP. | show ip interface brief (Cisco/Arista), show interfaces irb (Juniper) | Configure correct IP, ensure interface is no shutdown, verify VLAN exists. |
| Spanning Tree Protocol (STP) Issues | Root bridge election, blocking due to loop. | show spanning-tree active (Cisco/Arista) | Verify STP configuration per VLAN, ensure root bridge is as intended. |
| VLAN Hopping Attack (post-mortem) | DTP enabled, default native VLAN. | show interfaces trunk, show cdp neighbors detail (Cisco) | Implement all VLAN security mitigations (disable DTP, secure native VLAN). |
Root Cause Analysis in GitOps: When an issue is identified, the first step is to verify the desired state in Git. If the desired state is correct, but the actual state is not, the issue lies in the automation pipeline or the device itself. If the desired state in Git is incorrect, then the issue lies in the design or the PR approval process, requiring a new PR to fix the declarative configuration.
Performance Optimization
Optimizing VLAN performance is critical for large enterprise networks to ensure efficient traffic flow and resource utilization.
- VLAN Pruning: Prevents broadcast, unknown unicast, and multicast traffic for a specific VLAN from being sent across trunk links where that VLAN has no active members.
- Cisco (VTP Pruning):
vtp mode transparent,vtp pruning. Or manuallyswitchport trunk allowed vlan. - Juniper: Configure
no-pruningor specify allowed VLANs on trunks. - Arista:
switchport trunk allowed vlan - Benefit: Reduces unnecessary traffic, saves bandwidth on trunk links, and improves overall network efficiency.
- Cisco (VTP Pruning):
- Spanning Tree Protocol (STP) Optimization:
- Rapid PVST+ (RPVST+) / MSTP: Ensure fast convergence and stability across VLANs. RPVST+ runs a separate STP instance for each VLAN, while MSTP groups VLANs into instances to reduce overhead.
- PortFast/Edge Port: Configure access ports connected to end devices with PortFast (Cisco) or edge port (Juniper/Arista) to bypass the listening/learning states, allowing immediate connectivity and preventing unnecessary TCNs.
- BPDU Guard/Root Guard: Protect the STP topology by preventing unauthorized devices from becoming root bridges or injecting BPDUs.
- Jumbo Frames: If applications require large packets (e.g., storage networks, data center interconnects), configure jumbo frames (MTU > 1500 bytes) consistently across all devices and VLANs in the path.
- Broadcast Domain Sizing: Keep VLANs appropriately sized to minimize broadcast traffic. Too large VLANs can impact performance and security.
- Quality of Service (QoS): Implement QoS policies to prioritize critical VLAN traffic (e.g., voice, video) over less sensitive traffic. VLAN tags (PCP bits in 802.1Q) can be used for marking.
Network Diagram with VLAN Pruning
This diagram illustrates how VLAN pruning can be used to optimize trunk links by preventing specific VLAN traffic from propagating where it’s not needed.
@startuml
!theme mars
' Define elements
cloud "Internet/MPLS" as CLOUD
node "Core Router" as R1
node "Core Switch 1" as CS1
node "Core Switch 2" as CS2
node "Access Switch A" as ASA
node "Access Switch B" as ASB
rectangle "VLAN 10 (Sales)" as VLAN10_A
rectangle "VLAN 20 (Marketing)" as VLAN20_A
rectangle "VLAN 30 (Engineering)" as VLAN30_A
rectangle "VLAN 10 (Sales)" as VLAN10_B
rectangle "VLAN 20 (Marketing)" as VLAN20_B
rectangle "VLAN 40 (Guest)" as VLAN40_B
' Connections
CLOUD -- R1
R1 -- CS1 : "Trunk to Core"
CS1 -- CS2 : "Core Interconnect (Trunk - ALL)"
CS1 -- ASA : "Trunk (VLAN 10,20,30 - Pruned)"
CS2 -- ASB : "Trunk (VLAN 10,20,40 - Pruned)"
ASA -- VLAN10_A : "Access"
ASA -- VLAN20_A : "Access"
ASA -- VLAN30_A : "Access"
ASB -- VLAN10_B : "Access"
ASB -- VLAN20_B : "Access"
ASB -- VLAN40_B : "Access"
' Emphasize pruning
note right of CS1
Trunk to ASA:
VLANs 10,20,30
(VLAN 40 pruned)
end note
note right of CS2
Trunk to ASB:
VLANs 10,20,40
(VLAN 30 pruned)
end note
@enduml
Hands-On Lab: Implementing GitOps for a New VLAN
This lab simulates adding a new VLAN using a GitOps workflow.
Lab Topology
nwdiag {
network management_network {
address = "192.168.1.0/24"
lab_server [address = "192.168.1.100", shape = server, description = "Git/Ansible Host"];
core_switch_1 [address = "192.168.1.11", shape = cisco];
access_switch_1 [address = "192.168.1.13", shape = arista];
}
network vlan_10_prod {
address = "10.10.10.0/24"
prod_server_a [address = "10.10.10.5"];
}
network vlan_20_dev {
address = "10.20.20.0/24"
dev_pc_a [address = "10.20.20.5"];
}
group {
label = "VLAN 10 Devices";
color = "#CCFFCC";
prod_server_a -- access_switch_1 [label = "E1/1 (Access VLAN 10)"];
}
group {
label = "VLAN 20 Devices";
color = "#FFCCCC";
dev_pc_a -- access_switch_1 [label = "E1/5 (Access VLAN 20)"];
}
core_switch_1 -- access_switch_1 [label = "Gi1/0/24 - E1/24 (Trunk ALL)"];
}
Objectives
- Add a new VLAN (VLAN 30,
GUEST_WIFI) to the declarative configuration in Git. - Modify the Ansible playbook to include this new VLAN.
- Simulate a Git Pull Request process.
- Execute the Ansible playbook to apply the changes to
core_switch_1(Cisco) andaccess_switch_1(Arista). - Verify the new VLAN’s presence on both switches.
Step-by-Step Configuration
Prerequisites:
- A Linux host (
lab_server) with Git and Ansible installed. - Access to
core_switch_1(Cisco IOS-XE) andaccess_switch_1(Arista EOS) with SSH credentials. - Initial
inventory.iniandgroup_vars/all/vlans.yml(as per Automation Examples section). - Clone the Git repository containing your Ansible files on
lab_server.
Step 1: Create a new Git Branch
On lab_server, navigate to your Git repository and create a new branch for the change:
cd /path/to/your/repo
git checkout -b feature/add-guest-wifi-vlan
Step 2: Update the group_vars/all/vlans.yml file
Edit group_vars/all/vlans.yml to add the new VLAN 30:
# ... (existing content) ...
- id: 30
name: GUEST_WIFI
description: VLAN for Guest Wireless Access
ip_address: 10.30.30.1/24
devices:
cisco_core_switch_1:
access_ports: [] # No direct access ports on core for this VLAN
trunk_ports: ['GigabitEthernet1/0/24']
trunk_allowed_vlan: '10,20,30' # Update allowed VLANs
trunk_native_vlan: 999
arista_access_switch_1:
access_ports: ['Ethernet1/10'] # Assuming E1/10 is for guest AP
trunk_ports: ['Ethernet1/24']
trunk_allowed_vlan: '10,20,30' # Update allowed VLANs
trunk_native_vlan: 999
Step 3: Commit and Push the Changes
git add group_vars/all/vlans.yml
git commit -m "feat: Add VLAN 30 for Guest WiFi"
git push origin feature/add-guest-wifi-vlan
Step 4: Simulate CI/CD (Manual Trigger for Lab)
Normally, a Git webhook would trigger your CI/CD pipeline. For this lab, we’ll manually run the Ansible playbook from the lab_server.
# Before running, ensure your playbook (configure_vlans.yml) is up-to-date
# You would typically merge this feature branch into main after review,
# and the CI/CD pipeline would trigger.
# For this lab, we assume the feature branch is merged to main.
git checkout main
git pull origin main # Ensure you have the latest (merged) changes
ansible-playbook -i inventory.ini configure_vlans.yml --limit core_switch_1,access_switch_1
Step 5: Verification
After the playbook completes, log into the devices and verify the new VLAN.
On Cisco core_switch_1:
ssh admin@192.168.1.11
show vlan brief
show interface GigabitEthernet1/0/24 switchport
show ip interface brief
Expected Output for show vlan brief: VLAN 30 should be listed.
Expected Output for show interface GigabitEthernet1/0/24 switchport: VLAN 30 should be in the “Trunking VLANs Enabled” list.
Expected Output for show ip interface brief: Vlan30 should have 10.30.30.1 assigned.
On Arista access_switch_1:
ssh admin@192.168.1.13
show vlan
show interface Ethernet1/24 switchport
show interface Ethernet1/10 switchport
show ip interface brief
Expected Output for show vlan: VLAN 30 should be listed.
Expected Output for show interface Ethernet1/24 switchport: VLAN 30 should be in the “Allowed VLans on Trunk” list.
Expected Output for show interface Ethernet1/10 switchport: Access Mode VLAN: 30.
Expected Output for show ip interface brief: Vlan30 should have 10.30.30.1 assigned.
Challenge Exercises
- Remove a VLAN: Create a new branch, remove
VLAN 20fromvlans.yml, commit, push, and run the playbook to observe its removal. - Add Port Security: Update
vlans.ymland the Ansible playbook to applyswitchport port-security maximum 1to a specific access port for VLAN 10 on the Cisco switch. - Implement VLAN Pruning: Modify the trunk configuration in
vlans.ymlto only allow VLAN 10 and 30 on the trunk betweencore_switch_1andaccess_switch_1, ensuring VLAN 20 traffic is explicitly pruned.
Best Practices Checklist
Implementing a GitOps workflow for VLAN configuration requires adherence to best practices to maximize its benefits.
- Declarative Configuration: All VLAN configurations are defined declaratively in version control.
- Single Source of Truth: Git is the sole source of all desired VLAN states.
- Granular Data Separation: Separate VLAN definitions from device-specific assignments where possible, using Jinja2 templating or similar.
- Peer Review for Changes: All changes to VLAN configurations go through a Pull Request review process.
- Automated Validation (CI): Linting, syntax checks, and logical validation of configuration files are automated.
- Automated Deployment (CD): Approved changes are automatically deployed to network devices.
- Rollback Capability: The system supports quick and reliable rollbacks to previous configurations.
- Secure Access to Git: Protect your Git repository with strong authentication and authorization.
- Secure Automation Credentials: Store sensitive credentials (API keys, passwords) in a secure vault (e.g., Ansible Vault, HashiCorp Vault).
- Idempotent Automation: Automation scripts should be idempotent, meaning running them multiple times produces the same result without unintended side effects.
- Comprehensive Logging: Log all automation activities, including changes applied, timestamp, and user.
- Monitoring for Drift: Implement monitoring to detect configuration drift between the Git repository and the actual device state.
- Dedicated Native VLAN: Use an unused, dedicated VLAN ID for native VLAN on all trunks.
- Disable DTP/Negotiation: Manually configure trunk and access modes, disabling DTP.
- VLAN Pruning: Restrict VLANs on trunks to only those required.
- Port Security: Implement port security measures on access ports.
- Regular Audits: Periodically audit the Git repository and live configurations for compliance.
- Documentation: Keep clear documentation of the GitOps workflow, repository structure, and configuration standards.
- Environment Segregation: Use separate Git branches or repositories for different environments (dev, staging, production) with stricter approval processes for production.
Reference Links
- IEEE 802.1Q (VLAN Tagging): https://standards.ieee.org/ieee/802.1Q/10323/ (Latest 802.1Q-2022)
- IEEE 802.1ad (QinQ / Provider Bridging): https://en.wikipedia.org/wiki/IEEE_802.1ad
- GitOps Principles: https://www.gitops.tech/
- Ansible Documentation: https://docs.ansible.com/
- Terraform Documentation: https://developer.hashicorp.com/terraform/docs
- Netmiko Documentation: https://pynet.twb-tech.com/netmiko/
- Cisco VLAN Best Practices: https://www.cisco.com/c/en/us/support/docs/smb/routers/cisco-rv-series-small-business-routers/1778-tz-VLAN-Best-Practices-and-Security-Tips-for-Cisco-Business-Routers.html
- Juniper VLAN Configuration: Refer to Juniper’s official documentation for Junos OS on specific platforms.
- Arista VLAN Configuration: Refer to Arista’s official documentation for EOS on specific platforms.
- VLAN Hopping Attacks & Mitigation: https://www.imperva.com/learn/availability/vlan-hopping/
- Network Automation with Jinja2 & Ansible: https://kd9cpb.com/ansible
What’s Next
This chapter has provided a comprehensive dive into establishing a robust GitOps workflow for managing VLAN configurations. We’ve explored the theoretical underpinnings, practical multi-vendor automation examples, and critical security and performance considerations.
You’ve learned to:
- Frame VLAN management within a GitOps paradigm.
- Translate VLAN requirements into declarative configuration code.
- Leverage Ansible and Terraform for automated, multi-vendor VLAN provisioning and configuration.
- Identify and mitigate common VLAN security vulnerabilities.
- Optimize VLAN performance for enterprise-scale networks.
In the next chapter, we will expand on network segmentation by exploring VXLAN (Virtual Extensible LAN). While VLANs are limited to 4094 IDs and operate primarily within a single Layer 2 domain, VXLAN offers significant scalability (16 million segments) and the ability to extend Layer 2 networks over a Layer 3 underlay, which is crucial for modern data centers and cloud-native architectures. You’ll discover how GitOps principles can also be applied to VXLAN deployments, further enhancing network agility and automation.