Skip to main content

Cisco Router Automation with Ansible

This repository contains Ansible playbooks and roles for automating VPN tunnel and direct link configurations on Cisco IOS routers across multiple datacenters (EQX, KKB) and AWS connections.

πŸ“‹ Table of Contents

πŸ—οΈ Architecture Overview

Datacenter Infrastructure

  • EQX Datacenter: 2 routers (master/slave) running BGP AS 65401/65402
  • KKB Datacenter: 2 routers (master/slave) running BGP AS 65501/65502
  • AWS Integration: Direct Connect and IPsec VPN connections
  • Fortigate Firewalls: Local firewalls at each datacenter for traffic filtering
  1. IPsec VPN Tunnels: Encrypted tunnels between datacenters and to AWS
  2. DWDM Direct Links: High-speed fiber optic connections between EQX and KKB
  3. AWS Direct Connect: Dedicated connections to AWS via IST and FR POPs
  4. Fortigate Links: BGP peering with local firewalls

πŸ“¦ Prerequisites

# Python environment (recommended: pyenv + virtualenv)
pyenv virtualenv 3.x routers
pyenv activate routers

# Install Ansible
pip install ansible

# Install required collections (if any)
ansible-galaxy collection install ansible.netcommon

πŸš€ Quick Start

1. Clone and Setup

cd /path/to/cisco-automation-ansible
pyenv activate routers  # or your Python environment

2. Verify Inventory

# List all routers
ansible -i hosts.ini all_routers --list-hosts

# Test connectivity (dry-run)
ansible -i hosts.ini all_routers -m debug -a 'var=links' --connection=local
# Deploy specific link to a router
ansible-playbook -i hosts.ini deploy_tunnel.yml \
  --limit eqx-master \
  --extra-vars "link_name=TO-KKB-IPSEC-1"

πŸ“Š Deployment Commands by Router

πŸ”Ή eqx-master (EQX Master - AS 65401)

# Inter-Datacenter Links
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-master --extra-vars "link_name=TO-KKB-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-master --extra-vars "link_name=TO-KKB-DWDM-1"

# Local Firewall
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-master --extra-vars "link_name=TO-FW-FORTI-1"

# AWS Links
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-master --extra-vars "link_name=TO-AWS-PROD-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-master --extra-vars "link_name=TO-AWS-PROD-IPSEC-2"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-master --extra-vars "link_name=TO-AWS-PROD-IST-DCON-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-master --extra-vars "link_name=TO-AWS-PROD-FR-DCON-2"

πŸ”Ή eqx-slave (EQX Slave - AS 65402)

# Inter-Datacenter Links
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-slave --extra-vars "link_name=TO-KKB-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-slave --extra-vars "link_name=TO-KKB-DWDM-1"

# Local Firewall
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-slave --extra-vars "link_name=TO-FW-FORTI-1"

# AWS Links
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-slave --extra-vars "link_name=TO-AWS-PROD-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-slave --extra-vars "link_name=TO-AWS-PROD-IPSEC-2"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-slave --extra-vars "link_name=TO-AWS-PROD-IST-DCON-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit eqx-slave --extra-vars "link_name=TO-AWS-PROD-FR-DCON-2"

πŸ”Ή kkb-master (KKB Master - AS 65501)

# Inter-Datacenter Links
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-master --extra-vars "link_name=TO-EQX-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-master --extra-vars "link_name=TO-EQX-DWDM-1"

# Local Firewall
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-master --extra-vars "link_name=TO-FW-FORTI-1"

# AWS Links
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-master --extra-vars "link_name=TO-AWS-PROD-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-master --extra-vars "link_name=TO-AWS-PROD-IPSEC-2"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-master --extra-vars "link_name=TO-AWS-PROD-IST-DCON-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-master --extra-vars "link_name=TO-AWS-PROD-FR-DCON-2"

πŸ”Ή kkb-slave (KKB Slave - AS 65502)

# Inter-Datacenter Links
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-slave --extra-vars "link_name=TO-EQX-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-slave --extra-vars "link_name=TO-EQX-DWDM-1"

# Local Firewall
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-slave --extra-vars "link_name=TO-FW-FORTI-1"

# AWS Links
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-slave --extra-vars "link_name=TO-AWS-PROD-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-slave --extra-vars "link_name=TO-AWS-PROD-IPSEC-2"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-slave --extra-vars "link_name=TO-AWS-PROD-IST-DCON-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit kkb-slave --extra-vars "link_name=TO-AWS-PROD-FR-DCON-2"

πŸ”Έ Development Routers

dev-eqx-master

ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-eqx-master --extra-vars "link_name=TO-KKB-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-eqx-master --extra-vars "link_name=TO-KKB-DWDM-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-eqx-master --extra-vars "link_name=TO-FW-FORTI-1"

dev-eqx-slave

ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-eqx-slave --extra-vars "link_name=TO-KKB-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-eqx-slave --extra-vars "link_name=TO-KKB-DWDM-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-eqx-slave --extra-vars "link_name=TO-FW-FORTI-1"

dev-kkb-master

ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-kkb-master --extra-vars "link_name=TO-EQX-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-kkb-master --extra-vars "link_name=TO-EQX-DWDM-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-kkb-master --extra-vars "link_name=TO-FW-FORTI-1"

dev-kkb-slave

ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-kkb-slave --extra-vars "link_name=TO-EQX-IPSEC-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-kkb-slave --extra-vars "link_name=TO-EQX-DWDM-1"
ansible-playbook -i hosts.ini deploy_tunnel.yml --limit dev-kkb-slave --extra-vars "link_name=TO-FW-FORTI-1"

🌐 Network Topology

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        EQX DATACENTER                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                β”‚
β”‚  β”‚ eqx-master   │◄────DWDM────►│ eqx-slave    β”‚                β”‚
β”‚  β”‚ AS 65401     β”‚              β”‚ AS 65402     β”‚                β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚
β”‚         β”‚                              β”‚                        β”‚
β”‚         β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚                        β”‚
β”‚         └────────►│ Fortigateβ”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β”‚
β”‚                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚ IPsec VPN
                      β”‚ DWDM
                      β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        KKB DATACENTER                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                β”‚
β”‚  β”‚ kkb-master   │◄────DWDM────►│ kkb-slave    β”‚                β”‚
β”‚  β”‚ AS 65501     β”‚              β”‚ AS 65502     β”‚                β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚
β”‚         β”‚                              β”‚                        β”‚
β”‚         β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚                        β”‚
β”‚         └────────►│ Fortigateβ”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β”‚
β”‚                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β”‚ Direct Connect (IST/FR)
                      β”‚ IPsec VPN
                      β–Ό
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚   AWS Cloud   β”‚
              β”‚  VPC / VGW    β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“ Configuration Structure

cisco-automation-ansible/
β”œβ”€β”€ ansible.cfg                 # Ansible configuration
β”œβ”€β”€ hosts.ini                   # Inventory file with all routers
β”œβ”€β”€ deploy_tunnel.yml           # Main playbook
β”œβ”€β”€ group_vars/
β”‚   β”œβ”€β”€ all.yml                # Shared network lists (centralized)
β”‚   β”œβ”€β”€ eqx_masters.yml        # EQX master router link configs
β”‚   β”œβ”€β”€ eqx_slaves.yml         # EQX slave router link configs
β”‚   β”œβ”€β”€ kkb_masters.yml        # KKB master router link configs
β”‚   └── kkb_slaves.yml         # KKB slave router link configs
β”œβ”€β”€ host_vars/
β”‚   β”œβ”€β”€ eqx-master.yml         # Per-host variables (prod)
β”‚   β”œβ”€β”€ eqx-slave.yml
β”‚   β”œβ”€β”€ kkb-master.yml
β”‚   β”œβ”€β”€ kkb-slave.yml
β”‚   β”œβ”€β”€ dev-eqx-master.yml     # Per-host variables (dev)
β”‚   β”œβ”€β”€ dev-eqx-slave.yml
β”‚   β”œβ”€β”€ dev-kkb-master.yml
β”‚   └── dev-kkb-slave.yml
└── roles/
    └── vpn_tunnel/
        └── tasks/
            β”œβ”€β”€ main.yml       # Entry point with link selection
            β”œβ”€β”€ ipsec.yml      # IPsec tunnel configuration
            β”œβ”€β”€ bgp.yml        # BGP neighbor & routing config
            └── interface.yml  # Interface configuration

Centralized Network Configuration

All network prefixes are defined once in group_vars/all.yml:

  • eqx_receive_networks / eqx_distribute_networks: 18 EQX datacenter networks
  • kkb_distribute_networks: 13 KKB datacenter networks
  • aws_receive_networks: AWS VPC networks (172.16.110.0/24)
  • aws_distribute_networks: Networks announced to AWS
  • forti_receive_in_eqx / forti_distribute_in_eqx: Combined networks for EQX Fortigate
  • forti_receive_in_kkb / forti_distribute_in_kkb: Combined networks for KKB Fortigate

This DRY (Don't Repeat Yourself) approach ensures consistency and easier maintenance.

πŸ”§ Advanced Usage

Deploy to Multiple Routers

Dry-Run / Check Mode

# Test without making changes
ansible-playbook -i hosts.ini deploy_tunnel.yml \
  --limit eqx-master \
  --extra-vars "link_name=TO-KKB-IPSEC-1" \
  --check

Selective Configuration

configure:
  bgp: true        # Deploy BGP configuration
  ipsec: true      # Deploy IPsec configuration
  interface: true  # Deploy interface configuration

To skip specific components, set them to false in the link definition.

πŸ› Troubleshooting

Common Issues

# Solution: Always specify link_name
ansible-playbook -i hosts.ini deploy_tunnel.yml \
  --limit eqx-master \
  --extra-vars "link_name=TO-KKB-IPSEC-1"

Issue: "Unable to connect to router"

# Check SSH connectivity
ansible -i hosts.ini eqx-master -m ping

# Verify credentials in group_vars/all.yml
ansible_user: admin
ansible_password: admin

Issue: "Jinja2 template error"

# Validate configuration syntax
ansible -i hosts.ini eqx-master -m debug -a 'var=links' --connection=local

Debug Commands

# List all configured links for a router
ansible -i hosts.ini eqx-master -m debug -a 'var=links' --connection=local

# Check specific link configuration
ansible -i hosts.ini eqx-master -m debug \
  -a 'var=links[0]' \
  --connection=local

# Verify network lists
ansible -i hosts.ini eqx-master -m debug \
  -a 'var=eqx_receive_networks' \
  --connection=local

Validation

# Validate all routers can render their configurations
ansible -i hosts.ini all_routers -m debug \
  -a 'var=links[0].name' \
  --connection=local | grep SUCCESS

πŸ“ Best Practices

  1. Always test in dev environment first: Use dev routers before deploying to production
  2. Deploy one link at a time: Easier to troubleshoot and rollback
  3. Use version control: Commit changes before deploying to production
  4. Document changes: Update this README when adding new links or routers
  5. Backup configurations: Save router configs before making changes
  6. Monitor BGP sessions: Verify BGP neighbors come up after deployment

πŸ” Security Notes

  • Credentials are stored in group_vars/all.yml - ensure proper file permissions (600)
  • Consider using Ansible Vault for sensitive data:
    ansible-vault encrypt group_vars/all.yml
    
  • IPsec pre-shared keys should be rotated regularly
  • Limit SSH access to Ansible control node only

πŸ“Š BGP AS Numbers

Router AS Number Role
eqx-master 65401 EQX Primary
eqx-slave 65402 EQX Secondary
kkb-master 65501 KKB Primary
kkb-slave 65502 KKB Secondary
Fortigate (EQX) 65001 EQX Firewall
Fortigate (KKB) 65000 KKB Firewall

πŸ“š Additional Resources

πŸ“„ License

Internal use only - Proprietary

πŸ‘₯ Support

For issues or questions, contact the Network Operations team.


Last Updated: October 2025