Using Claude to Automate Cisco Router Configuration and Diagnostics
Router Automation with Claude
Cisco routers — whether running IOS-XE on ASR 1000s or IOS-XR on ASR 9000s — are where complex routing policy lives. BGP policy, route-maps, prefix-lists, MPLS VPN configuration, QoS policy — all of this is configuration-intensive and easy to get wrong. A single incorrect route-map can black-hole traffic silently.
Claude excels here because routing problems require reasoning, not just scripting. This post covers patterns for using Claude to generate router configuration, analyze routing state, and build intelligent WAN management tooling.
Setup
import anthropic
import json
from netmiko import ConnectHandler
client = anthropic.Anthropic()
def ask_claude(prompt: str, system: str = None, max_tokens: int = 3000) -> str:
kwargs = {
"model": "claude-opus-4-5",
"max_tokens": max_tokens,
"messages": [{"role": "user", "content": prompt}]
}
if system:
kwargs["system"] = system
return client.messages.create(**kwargs).content[0].text
def connect(host: str, os_type: str = "cisco_ios") -> ConnectHandler:
return ConnectHandler(
device_type=os_type, # "cisco_ios" or "cisco_xr"
host=host,
username="admin",
password="password",
)
Pattern 1: BGP Policy Generator
BGP policy (route-maps, prefix-lists, community matching) is notoriously complex to write correctly. Claude generates complete, consistent policy from a plain description:
def generate_bgp_policy(policy_description: str, platform: str = "IOS-XE") -> str:
prompt = f"""
Generate a complete Cisco {platform} BGP routing policy configuration for:
{policy_description}
Requirements:
- Create all referenced prefix-lists, community-lists, and AS-path access-lists
- Use numbered route-map sequences with meaningful gaps (10, 20, 30...)
- Add descriptions to all policy elements
- Use explicit permit/deny — no implicit dependencies
- Include the neighbor statement applying the route-map
- Follow Cisco best practices for BGP policy
Output ONLY {platform} CLI configuration, no explanations.
"""
return ask_claude(
prompt,
system=f"You are a senior Cisco BGP engineer. Output only {platform} CLI configuration."
)
# IOS-XE example
policy = generate_bgp_policy("""
Provider: AS64512, neighbor 203.0.113.1
Our ASN: 65001
Inbound policy:
- Accept default route (0.0.0.0/0) and set local-preference 150
- Accept any /24 or shorter from 10.0.0.0/8 and set local-preference 100
- Reject all /25 or longer prefixes
- Tag all accepted routes with community 65001:100
Outbound policy:
- Advertise only our prefixes: 192.0.2.0/24 and 198.51.100.0/24
- Prepend our ASN twice on the backup link (this is the primary)
- Set MED to 100 on all advertised routes
""", platform="IOS-XE")
print(policy)
Pattern 2: IOS-XR Route Policy Language Generator
IOS-XR uses its own policy language (RPL) which is very different from IOS-XE route-maps:
def generate_xr_rpl(policy_description: str) -> str:
prompt = f"""
Generate Cisco IOS-XR Route Policy Language (RPL) configuration for:
{policy_description}
IOS-XR RPL requirements:
- Use 'route-policy POLICY-NAME' blocks
- Use proper RPL syntax: if/elseif/else/endif
- Reference prefix-sets, community-sets, as-path-sets properly
- Define all sets BEFORE the policies that reference them
- Use 'pass', 'drop', 'done' correctly
- Apply policy under 'router bgp' with 'route-policy' statement
Output ONLY IOS-XR CLI configuration. Do NOT use IOS-XE route-map syntax.
"""
return ask_claude(
prompt,
system="You are a Cisco IOS-XR expert. Output only valid IOS-XR RPL and CLI configuration."
)
xr_policy = generate_xr_rpl("""
On ASR 9000 edge router:
- Inbound from PEER-AS 64512: accept prefixes in community 64512:100,
set local-pref 200. Accept 0.0.0.0/0, set local-pref 50. Reject all else.
- Outbound to 64512: advertise only prefixes tagged with community 65001:EXTERNAL.
Strip all internal communities before advertising.
""")
Pattern 3: WAN Link Analyzer
def analyze_wan_links(router_host: str) -> dict:
commands_iosxe = [
"show interfaces | include line protocol|input rate|output rate|error|reset",
"show ip bgp summary",
"show ip route summary",
"show processes cpu sorted | head 15",
"show processes memory sorted | head 10",
]
with connect(router_host) as conn:
outputs = {cmd.split()[2]: conn.send_command(cmd) for cmd in commands_iosxe}
prompt = f"""
Analyze this WAN router's operational state and provide a capacity and health assessment.
Router: {router_host}
Data: {json.dumps(outputs, indent=2)}
Return JSON with:
- health_status: "healthy" | "warning" | "critical"
- interface_issues: list of interfaces with errors, resets, or high utilization
- bgp_status: summary of BGP peer states
- cpu_concern: true/false with explanation
- memory_concern: true/false with explanation
- capacity_warnings: list of links at >70% utilization
- top_recommendations: list of 3 most important actions to take
Output ONLY valid JSON.
"""
return json.loads(ask_claude(prompt))
Pattern 4: MPLS VPN Config Generator
MPLS L3VPN configuration requires precise coordination of VRF definitions, RD/RT values, and BGP VPNv4 peering:
def generate_mpls_vpn_config(vpn_params: dict) -> dict:
prompt = f"""
Generate Cisco IOS-XE MPLS L3VPN configuration for a PE router.
VPN Parameters:
- VPN Customer: {vpn_params['customer']}
- VRF Name: {vpn_params['vrf_name']}
- Route Distinguisher: {vpn_params['rd']}
- Route Target Import: {vpn_params['rt_import']}
- Route Target Export: {vpn_params['rt_export']}
- CE Router IP: {vpn_params['ce_ip']}
- PE Interface to CE: {vpn_params['pe_interface']}
- PE Interface IP: {vpn_params['pe_ip']}
- CE Routing Protocol: {vpn_params['ce_protocol']}
- CE ASN (if BGP): {vpn_params.get('ce_asn', 'N/A')}
- PE BGP ASN: {vpn_params['pe_bgp_asn']}
- Route Reflector: {vpn_params['route_reflector']}
Return JSON with:
- vrf_config: VRF definition commands
- interface_config: PE-CE interface configuration
- bgp_config: BGP VPNv4 and CE peering configuration
- verification_commands: commands to verify the VPN is working
Output ONLY valid JSON.
"""
return json.loads(ask_claude(
prompt,
system="You are a Cisco MPLS expert. Generate valid IOS-XE configuration."
))
vpn = generate_mpls_vpn_config({
"customer": "ACME-CORP",
"vrf_name": "ACME",
"rd": "65000:100",
"rt_import": "65000:100",
"rt_export": "65000:100",
"ce_ip": "10.0.0.2",
"pe_interface": "GigabitEthernet0/0/1",
"pe_ip": "10.0.0.1/30",
"ce_protocol": "BGP",
"ce_asn": "65100",
"pe_bgp_asn": "65000",
"route_reflector": "10.255.255.1"
})
Pattern 5: Router Incident Responder
When something breaks, speed matters. This multi-turn assistant guides real-time incident response:
class RouterIncidentResponder:
def __init__(self, router_host: str, platform: str = "IOS-XE"):
self.host = router_host
self.platform = platform
self.history = []
self.evidence = {}
self.conn = connect(router_host, "cisco_ios" if platform == "IOS-XE" else "cisco_xr")
self.system = f"""You are a senior network engineer responding to a {platform} router incident.
Your job is to quickly identify root cause and provide fix commands.
At each step: state what you learned from the last output, then ask for ONE specific command.
When root cause is found, immediately provide exact fix commands.
Be concise and urgent — every minute of downtime matters."""
def investigate(self, symptom: str) -> str:
"""Start or continue investigation"""
self.history.append({"role": "user", "content": symptom})
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=512,
system=self.system,
messages=self.history
).content[0].text
self.history.append({"role": "assistant", "content": response})
return response
def run_command(self, command: str) -> str:
"""Run command on router and feed output back"""
output = self.conn.send_command(command)
self.evidence[command] = output
return self.investigate(f"Command: {command}\n\nOutput:\n{output}")
def __del__(self):
if hasattr(self, 'conn'):
self.conn.disconnect()
# Usage during an incident
responder = RouterIncidentResponder("10.0.0.1")
# Start the investigation
print(responder.investigate(
"URGENT: BGP session to 203.0.113.1 (AS64512, our primary upstream) went down 10 minutes ago. "
"We're losing about 40% of our routes. Secondary link is up but saturated."
))
# Claude asks for a specific command — run it
print(responder.run_command("show ip bgp neighbors 203.0.113.1 | include BGP state|notification|reset"))
# Claude analyzes and asks for next command
print(responder.run_command("show logging | include 203.0.113.1 | tail 20"))
# Continue until root cause found and fix commands provided
Key Considerations for Router Automation
Test in a lab first. BGP policy mistakes can cause widespread routing outages. Always validate generated configs in a lab or against a mock topology before production deployment.
Use commit confirmed on IOS-XR. IOS-XR’s commit confirmed 5 (auto-rollback after 5 minutes unless confirmed) is invaluable when pushing Claude-generated configs to production:
RP/0/RSP0/CPU0:router# commit confirmed 5
Validate prefix-lists before applying. Use show ip prefix-list detail and test with specific prefixes before attaching to a BGP neighbor.
For IOS-XR, always validate policy before committing:
RP/0/RSP0/CPU0:router# show rpl route-policy MY-POLICY detail