funwithlinux guide

Bash Scripting for Network Automation: Real-World Examples

In today’s fast-paced IT landscape, network automation has become a cornerstone of efficient operations. Repetitive tasks like device monitoring, configuration backups, and VLAN provisioning can drain time and introduce human error—*unless automated*. While tools like Ansible, Python, or Terraform dominate enterprise automation, **Bash scripting** remains a powerful, accessible option for network engineers. Why Bash? It’s pre-installed on nearly all Unix-like systems (Linux, macOS, network devices with embedded Linux), requires no additional dependencies, and integrates seamlessly with command-line tools (e.g., `ssh`, `ping`, `grep`, `awk`). Whether you’re a seasoned engineer or just starting, Bash lets you automate network tasks with minimal setup. This blog dives into **real-world Bash scripting examples** for network automation, from basic reachability checks to complex configuration deployments. Each example includes a clear goal, script code, detailed explanations, and best practices.

Table of Contents

  1. Why Bash for Network Automation?
  2. Prerequisites
  3. Real-World Examples
  4. Conclusion
  5. References

Why Bash for Network Automation?

Bash scripting shines in network automation for several reasons:

  • Ubiquity: Pre-installed on Linux, macOS, and even network devices (e.g., Cisco IOS XE, Arista EOS).
  • Simplicity: Easy to learn for beginners; leverages familiar command-line tools (grep, awk, ssh).
  • Speed: No need for complex dependencies or runtime environments.
  • Integration: Works with legacy CLI tools, SNMP, REST APIs, and even modern tools like Docker.

Limitations? Bash isn’t ideal for large-scale orchestration (e.g., managing 1000+ devices) or complex data structures. For those, tools like Ansible or Python are better. But for small-to-medium tasks, Bash is unbeatable.

Prerequisites

To follow along, you’ll need:

  • A Linux/macOS system (Bash 4.0+ recommended).
  • Basic Bash knowledge (variables, loops, conditionals).
  • Network devices to test with (physical or virtual, e.g., GNS3, EVE-NG, or Cisco CML).
  • Tools: ssh, ping, grep, awk, expect (for interactive sessions), snmpwalk (for SNMP), and sshpass (for password-based SSH, use cautiously).
  • SSH access to devices (enable mode for Cisco, privilege escalation for others).

Real-World Examples

Example 1: Device Reachability Check

Goal: Verify if a list of network devices is online (ICMP reachable).

Use Case: Daily monitoring to identify offline devices before users report issues.

Script Overview

  • Read IP addresses from a text file.
  • Ping each IP with a 2-second timeout and 2 packets.
  • Log results (success/failure) with timestamps.

Script Code: check_reachability.sh

#!/bin/bash
# Device reachability checker

# Configuration
INPUT_FILE="devices.txt"  # List of IPs, one per line
LOG_FILE="reachability_$(date +%Y%m%d).log"
TIMEOUT=2  # Seconds per ping
PACKETS=2  # Number of packets to send

# Check if input file exists
if [ ! -f "$INPUT_FILE" ]; then
    echo "Error: Input file $INPUT_FILE not found!"
    exit 1
fi

# Log header
echo "=== Reachability Check: $(date) ===" >> "$LOG_FILE"

# Loop through each device
while IFS= read -r ip; do
    # Skip empty lines
    if [ -z "$ip" ]; then continue; fi

    echo "Checking $ip..."
    # Ping with timeout and count
    ping -c "$PACKETS" -W "$TIMEOUT" "$ip" > /dev/null 2>&1

    if [ $? -eq 0 ]; then
        result="UP"
    else
        result="DOWN"
    fi

    # Log result
    echo "[$(date +%H:%M:%S)] $ip: $result" >> "$LOG_FILE"
    echo "$ip is $result"
done < "$INPUT_FILE"

echo "Check complete. Results logged to $LOG_FILE"

Key Explanations

  • ping -c $PACKETS -W $TIMEOUT $ip: Sends 2 packets with a 2-second timeout. > /dev/null 2>&1 suppresses output.
  • $? -eq 0: Checks if the last command (ping) succeeded (exit code 0).
  • Logs are timestamped and saved to a daily file (e.g., reachability_20240520.log).

Usage

  1. Create devices.txt with IPs:
    192.168.1.1  # Router
    192.168.1.2  # Switch
    192.168.1.3  # Firewall
  2. Run: chmod +x check_reachability.sh && ./check_reachability.sh

Notes

  • Some devices block ICMP (ping). Use TCP checks (e.g., nc -zv $ip 22 for SSH port) as a fallback.
  • Add --icmp-echo to nc for TCP-based reachability: nc -zv $ip 22 > /dev/null 2>&1.

Example 2: Automated Device Inventory Collection

Goal: Collect hostname, model, and OS version from multiple devices via SSH.

Use Case: Build an up-to-date inventory for asset management or audit preparation.

Script Overview

  • Read IPs/credentials from a file.
  • SSH into each device, run show version (Cisco) or equivalent.
  • Parse output with grep/awk to extract hostname, model, OS version.
  • Save results to a CSV.

Script Code: collect_inventory.sh

#!/bin/bash
# Device inventory collector (Cisco IOS example)

# Configuration
INPUT_FILE="inventory_input.txt"  # Format: ip,username,password,enable_pass
OUTPUT_CSV="inventory_$(date +%Y%m%d).csv"
COMMANDS="show version"  # Command to run on devices

# Check input file
if [ ! -f "$INPUT_FILE" ]; then
    echo "Error: Input file $INPUT_FILE not found!"
    exit 1
fi

# CSV header
echo "IP,Hostname,Model,OS Version" > "$OUTPUT_CSV"

# Read input file and process each device
while IFS=, read -r ip user pass enable_pass; do
    echo "Processing $ip..."

    # Use expect to handle interactive SSH (enable mode)
    inventory_data=$(expect -c "
        spawn ssh $user@$ip
        expect \"Password:\"
        send \"$pass\r\"
        expect \"$user>\"
        send \"enable\r\"
        expect \"Password:\"
        send \"$enable_pass\r\"
        expect \"#\"
        send \"$COMMANDS\r\"
        expect \"#\"
        send \"exit\r\"
        expect eof
    ")

    # Parse output with grep/awk
    hostname=$(echo "$inventory_data" | grep "hostname" | awk '{print $2}')
    model=$(echo "$inventory_data" | grep "Model number" | awk '{print $4}')  # Adjust based on device output
    os_version=$(echo "$inventory_data" | grep "Version" | head -1 | awk '{print $3}')

    # Handle missing data
    hostname=${hostname:-"N/A"}
    model=${model:-"N/A"}
    os_version=${os_version:-"N/A"}

    # Save to CSV
    echo "$ip,$hostname,$model,$os_version" >> "$OUTPUT_CSV"
done < "$INPUT_FILE"

echo "Inventory saved to $OUTPUT_CSV"

inventory_input.txt Format

192.168.1.1,admin,cisco123,enablepass
192.168.1.2,admin,cisco456,enablepass

Key Explanations

  • expect: Automates interactive SSH sessions (enters passwords, enables mode).
  • grep/awk: Parses show version output. Adjust patterns for non-Cisco devices (e.g., Arista uses show version | include Software).
  • CSV output: Easy to import into Excel/Google Sheets for reporting.

Notes

  • Security Warning: Storing passwords in plaintext is risky! Use SSH keys instead of expect/sshpass for production.
  • For Arista EOS: Replace enable with enable secret <pass> and adjust show version parsing.

Example 3: Automated VLAN Configuration

Goal: Deploy VLANs to multiple switches using a standard template.

Use Case: Provisioning VLANs for a new department (e.g., VLAN 100, name “ENGINEERING”).

Script Overview

  • Define VLAN ID/name as variables.
  • Generate config snippets (e.g., vlan 100; name ENGINEERING).
  • Push config to switches via SSH.

Script Code: deploy_vlan.sh

#!/bin/bash
# VLAN deployment script (Cisco IOS example)

# Configuration
VLAN_ID=100
VLAN_NAME="ENGINEERING"
SWITCHES=("192.168.1.10" "192.168.1.11")  # List of switch IPs
USER="admin"
PASS="cisco123"
ENABLE_PASS="enablepass"

# VLAN config template (Cisco IOS syntax)
VLAN_CONFIG="
vlan $VLAN_ID
 name $VLAN_NAME
exit
"

# Deploy to each switch
for switch_ip in "${SWITCHES[@]}"; do
    echo "Deploying VLAN $VLAN_ID to $switch_ip..."

    # Use expect to send config
    expect -c "
        spawn ssh $USER@$switch_ip
        expect \"Password:\"
        send \"$PASS\r\"
        expect \"$USER>\"
        send \"enable\r\"
        expect \"Password:\"
        send \"$ENABLE_PASS\r\"
        expect \"#\"
        send \"conf t\r\"
        expect \"(config)#\"
        send \"$VLAN_CONFIG\"
        expect \"(config)#\"
        send \"end\r\"
        expect \"#\"
        send \"write memory\r\"  # Save config
        expect \"#\"
        send \"exit\r\"
        expect eof
    "

    echo "VLAN $VLAN_ID deployed to $switch_ip"
done

Key Explanations

  • Config Template: Uses a here-doc (VLAN_CONFIG) to define VLAN commands.
  • conf t: Enters global config mode; write memory saves changes.
  • Loop over SWITCHES array to deploy to multiple devices.

Notes

  • Test First: Run show run | include vlan $VLAN_ID after deployment to verify.
  • Rollback Plan: Have a script to delete VLANs if needed (e.g., no vlan $VLAN_ID).

Example 4: Configuration Backup

Goal: Backup running configurations from devices and store them securely.

Use Case: Daily backups to recover from accidental config changes or outages.

Script Overview

  • Pull show running-config from devices.
  • Save to timestamped files (e.g., backup_192.168.1.1_20240520.cfg).
  • Compress backups weekly.

Script Code: backup_configs.sh

#!/bin/bash
# Configuration backup script

# Configuration
BACKUP_DIR="/backups"
DEVICES=("192.168.1.1" "192.168.1.2")
USER="admin"
PASS="cisco123"
ENABLE_PASS="enablepass"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# Create backup dir if missing
mkdir -p "$BACKUP_DIR"

# Backup each device
for device_ip in "${DEVICES[@]}"; do
    backup_file="$BACKUP_DIR/backup_${device_ip}_${TIMESTAMP}.cfg"
    echo "Backing up $device_ip to $backup_file..."

    # Fetch running config
    expect -c "
        spawn ssh $USER@$device_ip
        expect \"Password:\"
        send \"$PASS\r\"
        expect \"$USER>\"
        send \"enable\r\"
        expect \"Password:\"
        send \"$ENABLE_PASS\r\"
        expect \"#\"
        send \"terminal length 0\r\"  # Disable pagination
        send \"show running-config\r\"
        expect \"#\"
        send \"exit\r\"
        expect eof
    " | grep -v "spawn ssh" | grep -v "$USER@$device_ip" > "$backup_file"  # Filter out noise

    # Verify backup size
    if [ $(wc -l < "$backup_file") -lt 10 ]; then
        echo "Warning: Backup for $device_ip may be incomplete!"
    fi
done

# Compress old backups (keep 7 days)
find "$BACKUP_DIR" -name "backup_*.cfg" -mtime +7 -exec gzip {} \;
echo "Backup completed. Old backups compressed."

Key Explanations

  • terminal length 0: Disables pagination so show running-config outputs the full config.
  • grep -v: Filters out SSH session noise (e.g., “spawn ssh” messages).
  • find -mtime +7: Compresses backups older than 7 days to save space.

Example 5: Bandwidth Monitoring with SNMP

Goal: Monitor interface bandwidth (bits per second) on a router using SNMP.

Use Case: Identify traffic spikes on critical interfaces (e.g., WAN links).

Script Overview

  • Use SNMP to fetch interface bytes (OID: 1.3.6.1.2.1.2.2.1.10 for in, 1.3.6.1.2.1.2.2.1.16 for out).
  • Poll twice (5-second interval), calculate delta, and convert to Mbps.

Script Code: monitor_bandwidth.sh

#!/bin/bash
# Bandwidth monitor via SNMP

# Configuration
DEVICE_IP="192.168.1.1"
SNMP_COMMUNITY="public"  # Read-only community string
INTERFACE_INDEX=2  # Interface index (find with snmpwalk .1.3.6.1.2.1.2.2.1.2)
POLL_INTERVAL=5  # Seconds between polls

# Fetch initial bytes
in_bytes1=$(snmpwalk -v2c -c "$SNMP_COMMUNITY" "$DEVICE_IP" 1.3.6.1.2.1.2.2.1.10."$INTERFACE_INDEX" | awk '{print $4}')
out_bytes1=$(snmpwalk -v2c -c "$SNMP_COMMUNITY" "$DEVICE_IP" 1.3.6.1.2.1.2.2.1.16."$INTERFACE_INDEX" | awk '{print $4}')

# Wait interval
sleep "$POLL_INTERVAL"

# Fetch second bytes
in_bytes2=$(snmpwalk -v2c -c "$SNMP_COMMUNITY" "$DEVICE_IP" 1.3.6.1.2.1.2.2.1.10."$INTERFACE_INDEX" | awk '{print $4}')
out_bytes2=$(snmpwalk -v2c -c "$SNMP_COMMUNITY" "$DEVICE_IP" 1.3.6.1.2.1.2.2.1.16."$INTERFACE_INDEX" | awk '{print $4}')

# Calculate delta (bytes per interval)
delta_in=$((in_bytes2 - in_bytes1))
delta_out=$((out_bytes2 - out_bytes1))

# Convert to Mbps (1 byte = 8 bits; 1 Mbps = 1e6 bits/sec)
mbps_in=$(echo "scale=2; ($delta_in * 8) / ($POLL_INTERVAL * 1000000)" | bc)
mbps_out=$(echo "scale=2; ($delta_out * 8) / ($POLL_INTERVAL * 1000000)" | bc)

echo "Bandwidth on Interface $INTERFACE_INDEX (Device: $DEVICE_IP):"
echo "In: $mbps_in Mbps | Out: $mbps_out Mbps"

Key Explanations

  • SNMP OIDs: 1.3.6.1.2.1.2.2.1.10 (ifInOctets) and 1.3.6.1.2.1.2.2.1.16 (ifOutOctets) track bytes.
  • Interface Index: Find with snmpwalk -v2c -c public 192.168.1.1 1.3.6.1.2.1.2.2.1.2 (maps index to interface name).
  • Mbps Calculation: (bytes * 8 bits/byte) / (interval sec * 1e6 bits/Mbps).

Example 6: Error Handling and Logging

Goal: Make scripts robust with error handling, retries, and logging.

Use Case: Ensuring scripts fail gracefully and provide actionable logs for debugging.

Script Code: robust_script.sh

#!/bin/bash
# Example with error handling and logging

# Enable strict mode (exit on error, undefined variable, pipe failure)
set -euo pipefail

# Configuration
LOG_FILE="script_$(date +%Y%m%d).log"
MAX_RETRIES=3

# Log function (timestamped)
log() {
    echo "[$(date +%Y-%m-%dT%H:%M:%S)] $1" >> "$LOG_FILE"
}

# Retry function
retry() {
    local retries=$1
    shift
    local cmd=$*
    local attempt=1

    while [ $attempt -le $retries ]; do
        if $cmd; then
            return 0
        else
            log "Attempt $attempt failed. Retrying..."
            attempt=$((attempt + 1))
            sleep 2
        fi
    done
    log "Command failed after $retries attempts: $cmd"
    exit 1
}

# Main logic
log "Script started"

# Example: Reachability check with retries
log "Checking reachability to 192.168.1.1..."
retry $MAX_RETRIES ping -c 1 -W 2 192.168.1.1 > /dev/null 2>&1

log "Script completed successfully"

Key Explanations

  • set -euo pipefail: Exits on errors, undefined variables, or failed pipeline commands.
  • log(): Adds timestamps to logs for audit/debugging.
  • retry(): Retries failed commands (e.g., flaky network calls) up to MAX_RETRIES.

Conclusion

Bash scripting is a versatile tool for network automation, ideal for small-to-medium tasks like monitoring, backups, and configuration deployment. Its simplicity and ubiquity make it a go-to for engineers who want to automate without learning complex tools.

For larger-scale needs, pair Bash with Ansible (e.g., use Bash scripts as Ansible modules) or transition to Python. But for quick wins, Bash is hard to beat.

Start small: automate one repetitive task this week (e.g., daily backups), then build from there.

References