Table of Contents
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), andsshpass(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>&1suppresses 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
- Create
devices.txtwith IPs:192.168.1.1 # Router 192.168.1.2 # Switch 192.168.1.3 # Firewall - Run:
chmod +x check_reachability.sh && ./check_reachability.sh
Notes
- Some devices block ICMP (ping). Use TCP checks (e.g.,
nc -zv $ip 22for SSH port) as a fallback. - Add
--icmp-echotoncfor 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/awkto 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: Parsesshow versionoutput. Adjust patterns for non-Cisco devices (e.g., Arista usesshow 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/sshpassfor production. - For Arista EOS: Replace
enablewithenable secret <pass>and adjustshow versionparsing.
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 memorysaves changes.- Loop over
SWITCHESarray to deploy to multiple devices.
Notes
- Test First: Run
show run | include vlan $VLAN_IDafter 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-configfrom 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 soshow running-configoutputs 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.10for in,1.3.6.1.2.1.2.2.1.16for 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) and1.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 toMAX_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.