Skip to content

Free IT Guide

Part 4: Trigger Ansible Playbooks from Cisco Syslog Events

August 18, 2026 · Anthony Ventura

Series overview | Previous: Automate Cisco Switchports with Ansible and Jinja2 | Next: Test and Deploy Network Automation Safely

Part 4 of 5: In Part 4, we connect the event queue to Ansible. A Bash worker extracts the switch address, MAC address, and interface, selects the appropriate playbook, records the result, and runs continuously as a systemd service.

Safety note: Use an isolated lab, replace every example value, protect all credentials, maintain console access, and back up configurations before allowing automated changes.

Create the Log-Processing Service

The next component polls the MySQL queue every five seconds and processes messages sequentially. Despite the original section name, this implementation uses polling rather than inotify. Unknown messages are deleted by the sample script; change that behavior if your retention or audit requirements require them to be preserved.

sudo nano /usr/local/bin/monitor_logs.sh

Create the script below and replace the example database password. For production, move the credential to a root-readable MySQL option file or secrets manager instead of leaving it in the script.

#!/bin/bash

# MySQL connection details
MYSQL_USER="rsyslog_user"
MYSQL_PASS="REPLACE_WITH_A_LONG_RANDOM_PASSWORD"
MYSQL_DB="rsyslog"
MYSQL_HOST="127.0.0.1" # or 'localhost'
LOG_FILE="/var/log/inotify.log"
Device_Log_File_Path="/var/log/rsyslog/"

# MySQL query to get rows where status is blank (NULL or empty)
QUERY="SELECT CONCAT(id,',',FromHost,',',Message) AS formatted_row FROM SystemEvents WHERE status IS NULL OR status = '';"

# Loop to continuously check the database
while true; do
    # Query MySQL for rows where status is blank
    rows=$(mysql -u "$MYSQL_USER" -p"$MYSQL_PASS" -D "$MYSQL_DB" -N -e "$QUERY")

    if [ ! -z "$rows" ]; then
        echo "Found rows with blank status. Executing Ansible playbook for each row..." >>"$LOG_FILE"
        # Mac Address Lists
        macOuiList="/etc/ansible/OUIs.txt"
        macDeviceList="/etc/ansible/DeviceMacs.txt"
        macUnknownList="/var/log/rsyslog/unknowndevices.log"
        wirelessList="/etc/ansible/Wireless.txt"
        # Loop through each row and execute the playbook
        while read -r row; do
            echo "$row" >>"$LOG_FILE"
            # Extract the row details (id and msg)
            #id=$(echo "$row" | awk '{print $1}')
            #msg=$(echo "$row" | awk '{print $2}')
            IFS=',' read -r id ip_address msg <<<"$row"
            # Print the current row being processed
            echo "Processing row ID: $id with message: $msg from $ip_address" >>"$LOG_FILE"
            # Extract IP address, MAC address, and interface using regex
            macaddress=$(echo "$msg" | grep -oE '[[:xdigit:]]{4}\.[[:xdigit:]]{4}\.[[:xdigit:]]{4}' | head -n 1)
            interface=$(echo "$msg" | grep -oE '(FiveGigabitEthernet|TenGigabitEthernet|GigabitEthernet|Gi|Fi|Te)[0-9]+/[0-9]+/[0-9]+' | head -n 1)
            dt=$(date '+%m/%d/%Y %H:%M:%S')
            echo "current data is: $ip_address,$macaddress,$interface" >>"$LOG_FILE"
            # Verify device is known and port should be configured. If not remove sql entry.
            if [[ "$msg" == *"psecure-violation"* ]] || [[ "$msg" == *"Security violation occurred"* ]]; then
                echo "$dt: Port: $interface disabled on $ip_address running dot1xreset script" >>"$LOG_FILE"
                # Trigger the Ansible playbook (modify as needed)
                ansible-playbook /etc/ansible/playbooks/resetdot1x.yml -e "ip_address=$ip_address macaddress=$macaddress interface=$interface sql_id=$id" >>"$Device_Log_File_Path/portsecure.log" 2>&1
                # After execution, update the row's status to 'processed'
                mysql -u "$MYSQL_USER" -p"$MYSQL_PASS" -h "$MYSQL_HOST" -D "$MYSQL_DB" -e "UPDATE SystemEvents SET status='started-reset-dot1x-playbook' WHERE id=$id;"
                echo "Row ID: $id processed successfully!" >>"$LOG_FILE"
            elif [[ "$msg" == *"BLOCK_BPDUGUARD"* ]]; then
                echo "$dt: Port: $interface will be disabled" >>"$LOG_FILE"
                #Trigger the Ansible playbook
                ansible-playbook /etc/ansible/playbooks/bpduguardDisable.yml -e "ip_address=$ip_address interface=$interface sql_id=$id" >>"$Device_Log_File_Path/bpduportdisable.log" 2>&1
                mysql -u "$MYSQL_USER" -p"$MYSQL_PASS" -h "$MYSQL_HOST" -D "$MYSQL_DB" -e "UPDATE SystemEvents SET status='started-port-disable-script-bpdu' WHERE id=$id;"
                echo "Row ID: $id processed successfully!" >>"$LOG_FILE"
            elif grep -qi "${macaddress:0:7}" "$macOuiList" || grep -qi "$macaddress" "$macDeviceList"; then
                # Execute the Ansible playbook for the current row (you can pass extra vars if needed)
                if [[ "$msg" == *"DOT1X-5-FAIL"* ]]; then
                    echo "$dt: Port: $interface in guest vlan on $ip_address running port_config script" >>"$LOG_FILE"
                    # Trigger the Ansible playbook (modify as needed)
                    ansible-playbook /etc/ansible/playbooks/switch_port_config.yml -vvvv -e "ip_address=$ip_address macaddress=$macaddress interface=$interface sql_id=$id" >>"$Device_Log_File_Path/$macaddress.log" 2>&1
                    # After execution, update the row's status to 'processed'
                    mysql -u "$MYSQL_USER" -p"$MYSQL_PASS" -h "$MYSQL_HOST" -D "$MYSQL_DB" -e "UPDATE SystemEvents SET status='started-switchport-config-playbook' WHERE id=$id;"
                    echo "Row ID: $id processed successfully!" >>"$LOG_FILE"
                else
                    echo "dt: Port: $interface on $ip_address did not match a rule with message: $msg" >>"$LOG_FILE"
                    mysql -u "$MYSQL_USER" -p"$MYSQL_PASS" -h "$MYSQL_HOST" -D "$MYSQL_DB" -e "UPDATE SystemEvents SET status='Did not meet any playbook requirements.' WHERE id=$id;"
                fi

            elif grep -qi "${macaddress:0:7}" "$wirelessList"; then
                # Execute the Ansible playbook for the current row (you can pass extra vars if needed)
                if [[ "$msg" == *"DOT1X-5-FAIL"* ]]; then
                    echo "$dt: Port: $interface in guest vlan on $ip_address running port_config script" >>"$LOG_FILE"
                    # Trigger the Ansible playbook (modify as needed)
                    ansible-playbook /etc/ansible/playbooks/switch_port_config.yml -vvvv -e "ip_address=$ip_address macaddress=$macaddress interface=$interface sql_id=$id" >>"$Device_Log_File_Path/$macaddress.log" 2>&1
                    # After execution, update the row's status to 'processed'
                    mysql -u "$MYSQL_USER" -p"$MYSQL_PASS" -h "$MYSQL_HOST" -D "$MYSQL_DB" -e "UPDATE SystemEvents SET status='started-switchport-config-playbook' WHERE id=$id;"
                    echo "Row ID: $id processed successfully!" >>"$LOG_FILE"

                else
                    echo "dt: Port: $interface on $ip_address did not match a rule with message: $msg" >>"$LOG_FILE"
                    mysql -u "$MYSQL_USER" -p"$MYSQL_PASS" -h "$MYSQL_HOST" -D "$MYSQL_DB" -e "UPDATE SystemEvents SET status='Did not meet any playbook requirements.' WHERE id=$id;"
                fi
            else
                if grep -qi "$macaddress" "$macUnknownList"; then
                        echo "done"
                else
                        echo "$macaddress does not exist in OUIs.txt or DeviceMacs.txt" >> "$macUnknownList"
                fi
                mysql -u "$MYSQL_USER" -p"$MYSQL_PASS" -h "$MYSQL_HOST" -D "$MYSQL_DB" -e "Delete FROM SystemEvents WHERE id=$id;"
            fi
        done <<<"$rows"
    else
        echo "No rows with blank status found. Waiting for updates..."
    fi

    # Wait 5 seconds before checking again (can be adjusted)
    sleep 5
done

We need to update the file permissions to make it executable.

sudo chmod +x /usr/local/bin/monitor_logs.sh

After the script is finalized, we will transform it into a service that automatically restarts each time the server reboots.

sudo nano /etc/systemd/system/monitor_logs.service
[Unit]
Description=Monitor logs for Ansible trigger
After=network-online.target mysql.service rsyslog.service
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/monitor_logs.sh
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Reload systemd, enable and start the service, and then check its status and recent logs.

sudo systemctl daemon-reload
sudo systemctl enable --now monitor_logs.service
sudo systemctl status monitor_logs.service
sudo journalctl -u monitor_logs.service -n 50 --no-pager

If you modify the unit later, reload systemd and restart the service:

sudo systemctl daemon-reload
sudo systemctl restart monitor_logs.service

Expected Result

At the end of this part, the worker starts automatically, processes queued lab events, and exposes useful status information through systemd and its log files.

Troubleshooting

  • Run the script interactively before enabling the service so parsing and permissions are visible.
  • Use sudo journalctl -u monitor_logs.service -n 100 --no-pager when the service restarts or fails.
  • If variables are empty, compare the regular expressions against an unmodified syslog message from the affected switch model.

Series overview | Previous: Automate Cisco Switchports with Ansible and Jinja2 | Next: Test and Deploy Network Automation Safely

Leave a Reply

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