If you’ve ever SSH’d into a router at 2 a.m. because an interface went down, you already understand why Cisco’s Embedded Event Manager (EEM) exists. EEM lets your IOS-XE device watch for specific conditions — syslog messages, interface state changes, SNMP thresholds, timers — and automatically execute CLI actions when those conditions are met. No Python server required, no external orchestrator, just logic baked directly into the box.
This guide focuses on practical EEM applets you can drop into production IOS-XE today, including real show output and gotchas I’ve run into on Cat9K and ISR4K platforms.
What Is EEM and Why Should You Care?
EEM has been in IOS since 12.3(14)T and matured significantly in IOS-XE. At its core, EEM is an event-driven scripting framework built into the device. You define an applet — a named policy — that pairs an event detector (the trigger) with one or more actions (CLI commands, syslog messages, email notifications, SNMP traps).
The killer use case is self-healing: an applet that detects an interface going down and automatically tries to bring it back up, logs the event, and pages your on-call engineer. No human in the loop for tier-1 recovery.
Other high-value use cases:
- Scheduled config backups to a TFTP/SCP server
- Auto-disable a switchport when a specific MAC appears
- Alert via syslog when CPU/memory crosses a threshold
- Reload standby RP if primary failover hasn’t cleared in X seconds
- Run a
show techcapture automatically on crash
EEM Architecture: Detectors, Applets, and Policies
EEM supports three types of policies:
- Applets — inline CLI policies, defined directly in the running config. Best for simple, well-understood triggers.
- Tcl scripts — full Tcl programs registered with EEM. More powerful, requires Tcl knowledge and a file system to store scripts.
- Stub policies — used with EEM Manager in DNA Center environments.
This post sticks to applets — they cover 90% of real-world needs without the Tcl overhead.
Common Event Detectors
| Detector | Trigger Source | Typical Use |
|---|---|---|
event syslog |
Syslog message pattern | Interface state changes, adjacency drops, auth failures |
event timer |
Watchdog / cron-style timer | Scheduled backups, periodic checks |
event cli |
CLI command execution | Block/audit specific commands |
event snmp |
SNMP OID threshold | CPU/memory alerts, interface utilization |
event track |
IP SLA / Object Tracking state | Route failover, SLA violation response |
event none |
Manual trigger only | On-demand scripts, testing |
Your First EEM Applet: Interface Flap Logger
Let’s start simple. This applet fires whenever any interface goes down and logs a timestamped entry to syslog with extra detail:
event manager applet INTF-DOWN-LOGGER
event syslog pattern ".*LINEPROTO-5-UPDOWN.*line protocol.*down"
action 1.0 syslog priority warnings msg "EEM: Interface down detected - capturing state"
action 2.0 cli command "enable"
action 3.0 cli command "show interfaces | include line protocol|errors|resets"
action 4.0 syslog priority warnings msg "EEM: Interface snapshot complete"
The event syslog pattern uses a regular expression matched against the full syslog message. The .* wildcards are critical — the message format is %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet0/0/1, changed state to down and you need the pattern to match that full string.
Verify it’s loaded:
Router# show event manager policy registered
No. Class Type Event Type Trap Time Registered Name
1 applet system syslog Off Mon Apr 20 09:14:22 2026 INTF-DOWN-LOGGER
Applet 2: Auto-Bounce a Flapping Interface
This is the one network engineers either love or fear. If an interface flaps more than once in 30 seconds, shut it and alert. Useful for stopping spanning tree storms caused by a bad fiber or a misbehaving access switch:
event manager applet INTF-FLAP-SHUTDOWN
event syslog pattern ".*LINEPROTO-5-UPDOWN.*GigabitEthernet1/0/24.*down" occurs 2 period 30
action 1.0 syslog priority critical msg "EEM: GigabitEthernet1/0/24 flapping - shutting down"
action 2.0 cli command "enable"
action 3.0 cli command "configure terminal"
action 4.0 cli command "interface GigabitEthernet1/0/24"
action 5.0 cli command "shutdown"
action 6.0 cli command "end"
action 7.0 syslog priority critical msg "EEM: GigabitEthernet1/0/24 administratively shut"
The key options on the event syslog line are occurs 2 period 30 — fire only after the pattern matches twice within 30 seconds. Without these, the applet would trigger on the very first flap, which is usually too aggressive.
Important caveat: Be careful with auto-shutdown on uplinks. Always scope the pattern to specific access ports. You do NOT want this applet silently shutting a trunk port to your core.
Applet 3: Scheduled Config Backup via SCP
This is my most-deployed EEM applet. It runs nightly at 02:00 local time and SCPs the running config to a backup server:
event manager applet NIGHTLY-CONFIG-BACKUP
event timer cron name nightly-backup cron-entry "0 2 * * *"
action 1.0 syslog priority notifications msg "EEM: Starting nightly config backup"
action 2.0 cli command "enable"
action 3.0 cli command "copy running-config scp://netbackup@10.10.10.50/configs/router1-running.cfg"
action 4.0 wait 30
action 5.0 syslog priority notifications msg "EEM: Nightly config backup complete"
The cron-entry follows standard cron syntax: minute, hour, day-of-month, month, day-of-week. For SCP to work without interactive prompts, you need pre-configured SSH keys or a ip scp server enable host entry. Alternatively, use TFTP for simpler setups:
action 3.0 cli command "copy running-config tftp://10.10.10.50/router1-running.cfg"
Check when it last ran:
Router# show event manager history events
No. Time Event Type Name
1 Mon Apr 20 02:00:00 2026 timer cron NIGHTLY-CONFIG-BACKUP
Applet 4: BGP Neighbor Down Alert
BGP drops are serious. This applet fires immediately when any BGP peer goes down and sends a high-priority syslog — which your SIEM or Grafana/Loki stack can alert on:
event manager applet BGP-PEER-DOWN
event syslog pattern "%BGP-5-ADJCHANGE.*Down"
action 1.0 syslog priority alerts msg "EEM ALERT: BGP adjacency lost - check routing table immediately"
action 2.0 cli command "enable"
action 3.0 cli command "show bgp summary | begin Neighbor"
action 4.0 cli command "show ip route bgp | head 20"
The pattern %BGP-5-ADJCHANGE.*Down matches lines like:
%BGP-5-ADJCHANGE: neighbor 192.0.2.1 Down BGP Notification sent
You can get fancier and capture the neighbor IP using EEM variables. Here’s how to use regexp action to extract the peer address and include it in the log:
event manager applet BGP-PEER-DOWN-DETAIL
event syslog pattern "%BGP-5-ADJCHANGE.*Down"
action 1.0 regexp "neighbor ([0-9.]+) Down" "$_syslog_msg" full_match peer_ip
action 2.0 syslog priority alerts msg "EEM ALERT: BGP peer $peer_ip went down"
action 3.0 cli command "enable"
action 4.0 cli command "show bgp neighbors $peer_ip | include BGP state|Last reset"
The $_syslog_msg built-in variable contains the full syslog message text. The regexp action stores the full match in $full_match and the first capture group in the user-named variable $peer_ip — you can name these anything meaningful.
Applet 5: CPU Spike Capture
When CPU spikes, you want a snapshot before it clears. This SNMP-triggered applet fires when 5-minute CPU utilization exceeds 80% and auto-collects diagnostics:
event manager applet HIGH-CPU-CAPTURE
event snmp oid 1.3.6.1.4.1.9.2.1.57.0 get-type next entry-op gt entry-val 80 poll-interval 60
action 1.0 syslog priority critical msg "EEM: CPU > 80% detected - capturing diagnostics"
action 2.0 cli command "enable"
action 3.0 cli command "show processes cpu sorted | head 20"
action 4.0 cli command "show processes memory sorted | head 20"
action 5.0 cli command "show platform software process slot switch active R0 monitor | head 30"
action 6.0 syslog priority critical msg "EEM: CPU diagnostic capture complete - check logs"
The OID 1.3.6.1.4.1.9.2.1.57.0 is Cisco’s cpmCPUTotal5minRev — the 5-minute CPU average. The poll-interval 60 means EEM polls this OID every 60 seconds. If it exceeds 80, the applet fires. It won’t re-fire until the value drops below 80 and comes back up (default hysteresis behavior).
This pairs beautifully with a Python/NAPALM automation script that scrapes those captured syslog entries and creates incidents in your ticketing system.
Applet 6: Unauthorized CLI Command Audit
Want to know every time someone runs no shutdown on a critical interface? The event cli detector can intercept commands:
event manager applet AUDIT-NO-SHUTDOWN
event cli pattern "no shutdown" sync yes bypass none
action 1.0 syslog priority warnings msg "EEM AUDIT: 'no shutdown' executed by $_cli_username on $_cli_tty"
action 2.0 set retval 0
With sync yes, the applet runs synchronously — the command is held until the applet completes. The built-in variables $_cli_username and $_cli_tty capture who ran it and from which VTY line. Combine this with your AAA accounting logs and you have a solid audit trail without a full TACACS+ deployment.
For the full security hardening picture, check the network security hardening guide — EEM audit applets are a strong complement to CoPP and AAA.
Debugging EEM Applets
When your applet doesn’t fire, start here:
Router# debug event manager action cli
Router# debug event manager detector syslog
Router# show event manager policy registered
Router# show event manager history events detail
The most common reasons an applet doesn’t fire:
- Pattern mismatch — the syslog message format doesn’t match your regex. Always test with
event manager runon anevent nonetest applet first. - Privilege level — EEM applets run at privilege 0 by default. You need to add
event manager session cli username <priv15-user>globally, or theenableaction will fail. - IOS-XE version quirks — Some detectors behave differently pre/post 17.x. The
event snmpdetector was significantly reworked in 16.12. Check your release notes.
Critical global config you almost always need:
event manager session cli username EEM_PRIV15
!
username EEM_PRIV15 privilege 15 secret <strong-password>
Without this, any applet that runs CLI commands will fail silently on privilege-required operations.
EEM Variables Quick Reference
These built-in variables are available in any applet:
$_event_type - Event type (syslog, timer, cli, etc.)
$_event_type_string - Human-readable event description
$_event_id - Unique event ID
$_event_pub_time - Unix timestamp of event
$_syslog_msg - Full syslog message text (syslog events)
$_cli_msg - CLI command that triggered event (cli events)
$_cli_username - Username who ran the command
$_cli_tty - VTY/console line
$_eem_str_1 ... _9 - Regex capture groups from regexp action
$_job_id - EEM job ID (useful for logging)
Cat9K-Specific Notes
On Catalyst 9000 series switches running IOS-XE 17.x, EEM applets that interact with show platform commands may need to use action cli command with the noecho option to avoid flooding the console:
event manager applet CAT9K-STACK-CHANGE
event syslog pattern "STACKMGR-6-SWITCH_ADDED|STACKMGR-6-SWITCH_REMOVED"
action 1.0 cli command "enable"
action 2.0 cli command "show switch detail" noecho
action 3.0 syslog priority warnings msg "EEM: Stack membership changed - review output in logs"
Also, on Cat9K with StackWise, be aware that syslog messages from member switches include a switch number prefix. Your patterns need to account for this:
! Matches on both active and member switches
event syslog pattern ".*LINEPROTO-5-UPDOWN.*GigabitEthernet[0-9]/0/24.*down"
The leading .* handles the stack member prefix (*Apr 20 09:14:22.543: %LINEPROTO... vs *Apr 20 09:14:22.543: *2: %LINEPROTO...).
Putting It All Together: A Production EEM Baseline
Here’s a minimal EEM baseline I deploy on most enterprise IOS-XE routers and distribution switches. Add this to your config standard:
! Required for CLI applets to run privileged commands
event manager session cli username EEM_LOCAL_USER
! BGP peer monitoring
event manager applet BGP-PEER-DOWN
event syslog pattern "%BGP-5-ADJCHANGE.*Down"
action 1.0 syslog priority alerts msg "EEM ALERT: BGP adjacency lost"
action 2.0 cli command "enable"
action 3.0 cli command "show bgp summary | begin Neighbor"
! Nightly config backup
event manager applet NIGHTLY-BACKUP
event timer cron name nightly-backup cron-entry "0 2 * * *"
action 1.0 cli command "enable"
action 2.0 cli command "copy running-config tftp://10.10.10.50/$(hostname)-running.cfg"
action 3.0 syslog priority notifications msg "EEM: Nightly backup complete"
! CPU spike capture
event manager applet HIGH-CPU-CAPTURE
event snmp oid 1.3.6.1.4.1.9.2.1.57.0 get-type next entry-op gt entry-val 85 poll-interval 60
action 1.0 syslog priority critical msg "EEM: CPU spike detected - capturing state"
action 2.0 cli command "enable"
action 3.0 cli command "show processes cpu sorted | head 15"
This baseline gives you audit-ready config backups, real-time BGP alerting, and CPU diagnostics — all with zero external dependencies. For environments where you want to push these to 50+ devices at once, Ansible is the cleanest way to deploy EEM applets at scale.
When EEM Is Not the Right Tool
EEM is powerful, but it has limits. If your automation logic requires:
- Cross-device correlation (e.g., “if router A AND router B both lose BGP, do X”)
- Database lookups or REST API calls
- Complex conditional branching across many devices
- Integration with ticketing systems or chat platforms
…then you’re better served by a proper network automation framework. The Python/Netmiko/NAPALM/Nornir guide covers that layer. EEM and Python automation aren’t mutually exclusive — EEM handles on-device reactions, Python handles cross-device orchestration.
Conclusion
Cisco EEM applets are one of the most underutilized features in IOS-XE. They require no external infrastructure, survive device reloads, and can handle a significant chunk of tier-1 network events automatically. Start with the nightly config backup and BGP monitoring applets — they’re low risk and immediately useful. Once you’re comfortable with the syslog event detector and CLI actions, the CPU capture and interface flap applets are natural next steps.
The best part: every applet you deploy is one fewer 2 a.m. page. And that’s worth the 10 minutes it takes to write the config.
Leave a Reply