Automating Email Notifications in Scripts: Complete Guide

Learn to implement automated email notifications across Python, Bash, and PowerShell with SMTP protocols, security best practices, and troubleshooting tips.

Automating Email Notifications in Scripts

Table of Contents

1. [Introduction](#introduction) 2. [Email Protocols Overview](#email-protocols-overview) 3. [Setting Up Email Configuration](#setting-up-email-configuration) 4. [Python Implementation](#python-implementation) 5. [Bash Shell Implementation](#bash-shell-implementation) 6. [PowerShell Implementation](#powershell-implementation) 7. [Advanced Features](#advanced-features) 8. [Security Considerations](#security-considerations) 9. [Troubleshooting](#troubleshooting) 10. [Best Practices](#best-practices)

Introduction

Email automation in scripts is a crucial component of modern system administration and application monitoring. It enables automatic notification of system events, error alerts, backup completion status, and various other operational updates without manual intervention. This comprehensive guide covers multiple approaches to implementing email notifications across different scripting environments.

Email automation serves several purposes: - System monitoring and alerting - Automated reporting - Error notification and debugging - Backup and maintenance status updates - User notification systems - Integration with CI/CD pipelines

Email Protocols Overview

Understanding email protocols is fundamental to implementing automated email notifications effectively.

SMTP (Simple Mail Transfer Protocol)

SMTP is the standard protocol for sending emails. It operates on specific ports and requires proper authentication.

| Protocol | Port | Security | Description | |----------|------|----------|-------------| | SMTP | 25 | None | Standard unencrypted SMTP | | SMTP | 587 | STARTTLS | Submission port with encryption | | SMTPS | 465 | SSL/TLS | SMTP over SSL | | SMTP | 2587 | STARTTLS | Alternative submission port |

Common Email Providers Configuration

| Provider | SMTP Server | Port | Authentication | |----------|-------------|------|----------------| | Gmail | smtp.gmail.com | 587 | App Password Required | | Outlook | smtp-mail.outlook.com | 587 | OAuth2 or App Password | | Yahoo | smtp.mail.yahoo.com | 587 | App Password Required | | Exchange | mail.company.com | 587 | Domain Authentication |

Setting Up Email Configuration

Gmail Configuration

For Gmail, you need to enable 2-factor authentication and create an app-specific password:

1. Enable 2-Factor Authentication 2. Generate App Password 3. Use app password instead of regular password

Environment Variables Setup

Create a configuration file or use environment variables to store sensitive information:

`bash

Email configuration

export SMTP_SERVER="smtp.gmail.com" export SMTP_PORT="587" export EMAIL_USER="your-email@gmail.com" export EMAIL_PASS="your-app-password" export FROM_EMAIL="notifications@company.com" export TO_EMAIL="admin@company.com" `

Python Implementation

Python provides robust libraries for email automation through the built-in smtplib and email modules.

Basic Email Function

`python import smtplib import ssl from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.base import MIMEBase from email import encoders import os from datetime import datetime import logging

class EmailNotifier: def __init__(self, smtp_server, smtp_port, username, password): """ Initialize email notifier with SMTP configuration Args: smtp_server (str): SMTP server address smtp_port (int): SMTP server port username (str): Email username password (str): Email password or app password """ self.smtp_server = smtp_server self.smtp_port = smtp_port self.username = username self.password = password self.logger = self._setup_logging() def _setup_logging(self): """Setup logging for email operations""" logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) return logging.getLogger(__name__) def send_email(self, to_email, subject, body, attachments=None, is_html=False): """ Send email with optional attachments Args: to_email (str or list): Recipient email address(es) subject (str): Email subject body (str): Email body content attachments (list): List of file paths to attach is_html (bool): Whether body is HTML formatted Returns: bool: True if email sent successfully, False otherwise """ try: # Create message container msg = MIMEMultipart() msg['From'] = self.username msg['Subject'] = subject # Handle multiple recipients if isinstance(to_email, list): msg['To'] = ', '.join(to_email) recipients = to_email else: msg['To'] = to_email recipients = [to_email] # Add timestamp to body timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") body_with_timestamp = f"{body}\n\nSent at: {timestamp}" # Attach body if is_html: msg.attach(MIMEText(body_with_timestamp, 'html')) else: msg.attach(MIMEText(body_with_timestamp, 'plain')) # Add attachments if provided if attachments: for file_path in attachments: if os.path.isfile(file_path): self._attach_file(msg, file_path) else: self.logger.warning(f"Attachment not found: {file_path}") # Create secure connection and send email context = ssl.create_default_context() with smtplib.SMTP(self.smtp_server, self.smtp_port) as server: server.starttls(context=context) server.login(self.username, self.password) server.sendmail(self.username, recipients, msg.as_string()) self.logger.info(f"Email sent successfully to {recipients}") return True except Exception as e: self.logger.error(f"Failed to send email: {str(e)}") return False def _attach_file(self, msg, file_path): """ Attach file to email message Args: msg: Email message object file_path (str): Path to file to attach """ with open(file_path, "rb") as attachment: part = MIMEBase('application', 'octet-stream') part.set_payload(attachment.read()) encoders.encode_base64(part) part.add_header( 'Content-Disposition', f'attachment; filename= {os.path.basename(file_path)}' ) msg.attach(part) def send_system_alert(self, alert_type, message, severity="INFO"): """ Send system alert with predefined formatting Args: alert_type (str): Type of alert (ERROR, WARNING, INFO) message (str): Alert message severity (str): Severity level """ subject = f"[{severity}] System Alert: {alert_type}" body = f""" System Alert Notification

Alert Type: {alert_type} Severity: {severity} Message: {message} Hostname: {os.uname().nodename if hasattr(os, 'uname') else 'Unknown'} Timestamp: {datetime.now().isoformat()}

This is an automated message from your monitoring system. """ # Determine recipients based on severity recipients = self._get_recipients_by_severity(severity) return self.send_email(recipients, subject, body.strip()) def _get_recipients_by_severity(self, severity): """Get email recipients based on alert severity""" recipients = { "CRITICAL": ["admin@company.com", "oncall@company.com"], "ERROR": ["admin@company.com"], "WARNING": ["admin@company.com"], "INFO": ["notifications@company.com"] } return recipients.get(severity, ["admin@company.com"])

Configuration and usage example

def main(): # Load configuration from environment variables smtp_server = os.getenv('SMTP_SERVER', 'smtp.gmail.com') smtp_port = int(os.getenv('SMTP_PORT', '587')) email_user = os.getenv('EMAIL_USER') email_pass = os.getenv('EMAIL_PASS') if not all([email_user, email_pass]): print("Error: EMAIL_USER and EMAIL_PASS environment variables required") return # Initialize email notifier notifier = EmailNotifier(smtp_server, smtp_port, email_user, email_pass) # Example usage success = notifier.send_email( to_email="recipient@example.com", subject="Test Notification", body="This is a test email from the automated notification system." ) if success: print("Email sent successfully") else: print("Failed to send email")

if __name__ == "__main__": main() `

Advanced Python Email Templates

`python class EmailTemplates: """Predefined email templates for common notifications""" @staticmethod def backup_completion_template(backup_name, status, size, duration): """Template for backup completion notifications""" if status.lower() == 'success': subject = f"Backup Completed Successfully: {backup_name}" body = f""" Backup Operation Completed

Backup Name: {backup_name} Status: SUCCESS Size: {size} Duration: {duration} Completion Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

All files have been successfully backed up. """ else: subject = f"Backup Failed: {backup_name}" body = f""" Backup Operation Failed

Backup Name: {backup_name} Status: FAILED Error Details: {size} # Reusing parameter for error message Duration: {duration} Failure Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

Please check the backup system immediately. """ return subject, body.strip() @staticmethod def disk_space_alert_template(filesystem, usage_percent, available_space): """Template for disk space alerts""" subject = f"Disk Space Alert: {filesystem} at {usage_percent}%" if usage_percent >= 95: severity = "CRITICAL" elif usage_percent >= 85: severity = "WARNING" else: severity = "INFO" body = f""" Disk Space Alert

Filesystem: {filesystem} Usage: {usage_percent}% Available Space: {available_space} Severity: {severity} Check Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

{'IMMEDIATE ACTION REQUIRED!' if usage_percent >= 95 else 'Please monitor closely.'} """ return subject, body.strip() `

Bash Shell Implementation

Bash scripts can send emails using various methods including mail, sendmail, and curl.

Using mail command

`bash #!/bin/bash

Email configuration

SMTP_SERVER="${SMTP_SERVER:-smtp.gmail.com}" SMTP_PORT="${SMTP_PORT:-587}" FROM_EMAIL="${FROM_EMAIL:-notifications@company.com}" TO_EMAIL="${TO_EMAIL:-admin@company.com}"

Function to send simple email using mail command

send_simple_email() { local to_email="$1" local subject="$2" local body="$3" local attachment="$4" # Check if mail command is available if ! command -v mail &> /dev/null; then echo "Error: mail command not found. Install mailutils package." return 1 fi # Send email with or without attachment if [ -n "$attachment" ] && [ -f "$attachment" ]; then echo "$body" | mail -s "$subject" -A "$attachment" "$to_email" else echo "$body" | mail -s "$subject" "$to_email" fi local exit_code=$? if [ $exit_code -eq 0 ]; then echo "Email sent successfully to $to_email" log_email_event "SUCCESS" "$to_email" "$subject" else echo "Failed to send email to $to_email" log_email_event "FAILED" "$to_email" "$subject" fi return $exit_code }

Function to send email using curl (more reliable for external SMTP)

send_email_curl() { local to_email="$1" local subject="$2" local body="$3" local attachment="$4" # Temporary file for email content local email_file="/tmp/email_$.txt" local boundary="boundary-$(date +%s)" # Create email headers and body cat > "$email_file" << EOF From: $FROM_EMAIL To: $to_email Subject: $subject Date: $(date -R) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="$boundary"

--$boundary Content-Type: text/plain; charset=UTF-8

$body

Sent at: $(date '+%Y-%m-%d %H:%M:%S') EOF

# Add attachment if provided if [ -n "$attachment" ] && [ -f "$attachment" ]; then cat >> "$email_file" << EOF

--$boundary Content-Type: application/octet-stream Content-Disposition: attachment; filename="$(basename "$attachment")" Content-Transfer-Encoding: base64

$(base64 "$attachment") EOF fi echo "--$boundary--" >> "$email_file" # Send email using curl curl --url "smtp://$SMTP_SERVER:$SMTP_PORT" \ --ssl-reqd \ --mail-from "$FROM_EMAIL" \ --mail-rcpt "$to_email" \ --upload-file "$email_file" \ --user "$EMAIL_USER:$EMAIL_PASS" \ --silent local exit_code=$? # Clean up temporary file rm -f "$email_file" if [ $exit_code -eq 0 ]; then echo "Email sent successfully to $to_email using curl" log_email_event "SUCCESS" "$to_email" "$subject" else echo "Failed to send email to $to_email using curl" log_email_event "FAILED" "$to_email" "$subject" fi return $exit_code }

Function to log email events

log_email_event() { local status="$1" local recipient="$2" local subject="$3" local log_file="/var/log/email_notifications.log" # Create log directory if it doesn't exist mkdir -p "$(dirname "$log_file")" # Log the event echo "$(date '+%Y-%m-%d %H:%M:%S') - $status - To: $recipient - Subject: $subject" >> "$log_file" }

Function to send system alert

send_system_alert() { local alert_type="$1" local message="$2" local severity="${3:-INFO}" local subject="[$severity] System Alert: $alert_type" local body="System Alert Notification

Alert Type: $alert_type Severity: $severity Message: $message Hostname: $(hostname) Timestamp: $(date '+%Y-%m-%d %H:%M:%S')

This is an automated message from your monitoring system." # Determine recipients based on severity local recipients case "$severity" in "CRITICAL") recipients="admin@company.com,oncall@company.com" ;; "ERROR"|"WARNING") recipients="admin@company.com" ;; *) recipients="notifications@company.com" ;; esac # Send to multiple recipients IFS=',' read -ra ADDR <<< "$recipients" for recipient in "${ADDR[@]}"; do send_email_curl "$recipient" "$subject" "$body" done }

Function to monitor disk space and send alerts

monitor_disk_space() { local threshold="${1:-85}" # Get disk usage information df -h | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{ print $5 " " $1 " " $4 }' | while read -r output; do usage=$(echo "$output" | awk '{ print $1}' | cut -d'%' -f1) partition=$(echo "$output" | awk '{ print $2 }') available=$(echo "$output" | awk '{ print $3 }') if [ "$usage" -ge "$threshold" ]; then local severity if [ "$usage" -ge 95 ]; then severity="CRITICAL" elif [ "$usage" -ge 85 ]; then severity="WARNING" else severity="INFO" fi local alert_message="Disk usage on $partition is at $usage%. Available space: $available" send_system_alert "Disk Space Alert" "$alert_message" "$severity" fi done }

Function to check service status and send alerts

check_service_status() { local service_name="$1" if systemctl is-active --quiet "$service_name"; then echo "Service $service_name is running" else local alert_message="Service $service_name is not running on $(hostname)" send_system_alert "Service Down" "$alert_message" "ERROR" fi }

Main execution example

main() { echo "Starting automated email notification system..." # Check required environment variables if [ -z "$EMAIL_USER" ] || [ -z "$EMAIL_PASS" ]; then echo "Error: EMAIL_USER and EMAIL_PASS environment variables are required" exit 1 fi # Example usage case "${1:-test}" in "test") send_email_curl "$TO_EMAIL" "Test Notification" "This is a test email from the bash notification system." ;; "disk") monitor_disk_space 80 ;; "service") check_service_status "${2:-apache2}" ;; "alert") send_system_alert "${2:-Test Alert}" "${3:-This is a test alert}" "${4:-INFO}" ;; *) echo "Usage: $0 {test|disk|service |alert [severity]}" exit 1 ;; esac }

Execute main function with all arguments

main "$@" `

PowerShell Implementation

PowerShell provides the Send-MailMessage cmdlet and .NET classes for email functionality.

PowerShell Email Module

`powershell

Email notification module for PowerShell

Email configuration class

class EmailConfig { [string]$SmtpServer [int]$SmtpPort [string]$Username [string]$Password [string]$FromEmail [bool]$UseSSL EmailConfig([string]$server, [int]$port, [string]$user, [string]$pass, [string]$from) { $this.SmtpServer = $server $this.SmtpPort = $port $this.Username = $user $this.Password = $pass $this.FromEmail = $from $this.UseSSL = $true } }

Email notifier class

class EmailNotifier { [EmailConfig]$Config [string]$LogPath EmailNotifier([EmailConfig]$config) { $this.Config = $config $this.LogPath = "C:\Logs\EmailNotifications.log" $this.EnsureLogDirectory() } [void]EnsureLogDirectory() { $logDir = Split-Path -Parent $this.LogPath if (-not (Test-Path -Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } } [bool]SendEmail([string[]]$ToEmail, [string]$Subject, [string]$Body, [string[]]$Attachments, [bool]$IsHtml) { try { # Create secure password $securePassword = ConvertTo-SecureString -String $this.Config.Password -AsPlainText -Force $credential = New-Object System.Management.Automation.PSCredential($this.Config.Username, $securePassword) # Add timestamp to body $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $bodyWithTimestamp = "$BodynnSent at: $timestamp" # Prepare email parameters $emailParams = @{ SmtpServer = $this.Config.SmtpServer Port = $this.Config.SmtpPort From = $this.Config.FromEmail To = $ToEmail Subject = $Subject Body = $bodyWithTimestamp Credential = $credential UseSsl = $this.Config.UseSSL } # Add HTML formatting if specified if ($IsHtml) { $emailParams.BodyAsHtml = $true } # Add attachments if provided if ($Attachments -and $Attachments.Count -gt 0) { $validAttachments = @() foreach ($attachment in $Attachments) { if (Test-Path -Path $attachment) { $validAttachments += $attachment } else { Write-Warning "Attachment not found: $attachment" } } if ($validAttachments.Count -gt 0) { $emailParams.Attachments = $validAttachments } } # Send email Send-MailMessage @emailParams $this.LogEmailEvent("SUCCESS", ($ToEmail -join ", "), $Subject) Write-Host "Email sent successfully to: $($ToEmail -join ', ')" -ForegroundColor Green return $true } catch { $this.LogEmailEvent("FAILED", ($ToEmail -join ", "), $Subject, $_.Exception.Message) Write-Error "Failed to send email: $($_.Exception.Message)" return $false } } [bool]SendEmail([string[]]$ToEmail, [string]$Subject, [string]$Body) { return $this.SendEmail($ToEmail, $Subject, $Body, @(), $false) } [void]LogEmailEvent([string]$Status, [string]$Recipients, [string]$Subject, [string]$Error = "") { $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $logEntry = "$timestamp - $Status - To: $Recipients - Subject: $Subject" if ($Error) { $logEntry += " - Error: $Error" } Add-Content -Path $this.LogPath -Value $logEntry } [bool]SendSystemAlert([string]$AlertType, [string]$Message, [string]$Severity = "INFO") { $subject = "[$Severity] System Alert: $AlertType" $body = @" System Alert Notification

Alert Type: $AlertType Severity: $Severity Message: $Message Hostname: $env:COMPUTERNAME Timestamp: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")

This is an automated message from your monitoring system. "@ $recipients = $this.GetRecipientsBySeverity($Severity) return $this.SendEmail($recipients, $subject, $body) } [string[]]GetRecipientsBySeverity([string]$Severity) { switch ($Severity) { "CRITICAL" { return @("admin@company.com", "oncall@company.com") } "ERROR" { return @("admin@company.com") } "WARNING" { return @("admin@company.com") } default { return @("notifications@company.com") } } } }

Email templates class

class EmailTemplates { static [hashtable]BackupCompletionTemplate([string]$BackupName, [string]$Status, [string]$Size, [string]$Duration) { if ($Status.ToLower() -eq 'success') { $subject = "Backup Completed Successfully: $BackupName" $body = @" Backup Operation Completed

Backup Name: $BackupName Status: SUCCESS Size: $Size Duration: $Duration Completion Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')

All files have been successfully backed up. "@ } else { $subject = "Backup Failed: $BackupName" $body = @" Backup Operation Failed

Backup Name: $BackupName Status: FAILED Error Details: $Size Duration: $Duration Failure Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')

Please check the backup system immediately. "@ } return @{ Subject = $subject Body = $body } } static [hashtable]DiskSpaceAlertTemplate([string]$DriveLetter, [int]$UsagePercent, [string]$AvailableSpace) { $subject = "Disk Space Alert: Drive $DriveLetter at $UsagePercent%" $severity = switch ($UsagePercent) { {$_ -ge 95} { "CRITICAL" } {$_ -ge 85} { "WARNING" } default { "INFO" } } $actionRequired = if ($UsagePercent -ge 95) { "IMMEDIATE ACTION REQUIRED!" } else { "Please monitor closely." } $body = @" Disk Space Alert

Drive: $DriveLetter Usage: $UsagePercent% Available Space: $AvailableSpace Severity: $severity Check Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')

$actionRequired "@ return @{ Subject = $subject Body = $body } } }

Utility functions for system monitoring

function Monitor-DiskSpace { param( [EmailNotifier]$Notifier, [int]$Threshold = 85 ) Get-WmiObject -Class Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 } | ForEach-Object { $usagePercent = [math]::Round((($_.Size - $_.FreeSpace) / $_.Size) * 100, 2) $availableGB = [math]::Round($_.FreeSpace / 1GB, 2) if ($usagePercent -ge $Threshold) { $severity = switch ($usagePercent) { {$_ -ge 95} { "CRITICAL" } {$_ -ge 85} { "WARNING" } default { "INFO" } } $template = [EmailTemplates]::DiskSpaceAlertTemplate($_.DeviceID, $usagePercent, "$availableGB GB") $Notifier.SendEmail($Notifier.GetRecipientsBySeverity($severity), $template.Subject, $template.Body) } } }

function Test-ServiceStatus { param( [EmailNotifier]$Notifier, [string]$ServiceName ) $service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue if (-not $service) { $message = "Service '$ServiceName' not found on $env:COMPUTERNAME" $Notifier.SendSystemAlert("Service Not Found", $message, "ERROR") } elseif ($service.Status -ne 'Running') { $message = "Service '$ServiceName' is $($service.Status) on $env:COMPUTERNAME" $Notifier.SendSystemAlert("Service Down", $message, "ERROR") } else { Write-Host "Service $ServiceName is running normally" -ForegroundColor Green } }

Main execution function

function Start-EmailNotificationSystem { param( [string]$Action = "test", [string]$Parameter1 = "", [string]$Parameter2 = "", [string]$Parameter3 = "" ) # Load configuration from environment variables $smtpServer = $env:SMTP_SERVER ?? "smtp.gmail.com" $smtpPort = [int]($env:SMTP_PORT ?? "587") $emailUser = $env:EMAIL_USER $emailPass = $env:EMAIL_PASS $fromEmail = $env:FROM_EMAIL ?? $emailUser $toEmail = $env:TO_EMAIL ?? "admin@company.com" if (-not $emailUser -or -not $emailPass) { Write-Error "EMAIL_USER and EMAIL_PASS environment variables are required" return } # Initialize email configuration and notifier $config = [EmailConfig]::new($smtpServer, $smtpPort, $emailUser, $emailPass, $fromEmail) $notifier = [EmailNotifier]::new($config) # Execute based on action parameter switch ($Action.ToLower()) { "test" { $success = $notifier.SendEmail(@($toEmail), "Test Notification", "This is a test email from the PowerShell notification system.") if ($success) { Write-Host "Test email sent successfully" -ForegroundColor Green } } "disk" { $threshold = if ($Parameter1) { [int]$Parameter1 } else { 80 } Monitor-DiskSpace -Notifier $notifier -Threshold $threshold } "service" { if (-not $Parameter1) { Write-Error "Service name parameter required" return } Test-ServiceStatus -Notifier $notifier -ServiceName $Parameter1 } "alert" { if (-not $Parameter1 -or -not $Parameter2) { Write-Error "Alert type and message parameters required" return } $severity = if ($Parameter3) { $Parameter3 } else { "INFO" } $notifier.SendSystemAlert($Parameter1, $Parameter2, $severity) } default { Write-Host "Usage: Start-EmailNotificationSystem -Action {test|disk|service|alert} [parameters]" Write-Host "Examples:" Write-Host " Start-EmailNotificationSystem -Action test" Write-Host " Start-EmailNotificationSystem -Action disk -Parameter1 80" Write-Host " Start-EmailNotificationSystem -Action service -Parameter1 'Spooler'" Write-Host " Start-EmailNotificationSystem -Action alert -Parameter1 'Test Alert' -Parameter2 'This is a test' -Parameter3 'INFO'" } } }

Export functions for module use

Export-ModuleMember -Function Start-EmailNotificationSystem Export-ModuleMember -Class EmailNotifier, EmailConfig, EmailTemplates `

Advanced Features

Email Rate Limiting

Implement rate limiting to prevent email spam:

`python import time from collections import defaultdict

class RateLimitedEmailNotifier(EmailNotifier): def __init__(self, args, *kwargs): super().__init__(args, *kwargs) self.email_counts = defaultdict(list) self.rate_limits = { 'per_minute': 10, 'per_hour': 100, 'per_day': 500 } def _check_rate_limit(self, recipient): now = time.time() times = self.email_counts[recipient] # Remove old entries times[:] = [t for t in times if now - t < 86400] # Keep last 24 hours # Check limits minute_count = len([t for t in times if now - t < 60]) hour_count = len([t for t in times if now - t < 3600]) day_count = len(times) if (minute_count >= self.rate_limits['per_minute'] or hour_count >= self.rate_limits['per_hour'] or day_count >= self.rate_limits['per_day']): return False return True def send_email(self, to_email, subject, body, kwargs): recipients = [to_email] if isinstance(to_email, str) else to_email for recipient in recipients: if not self._check_rate_limit(recipient): self.logger.warning(f"Rate limit exceeded for {recipient}") return False success = super().send_email(to_email, subject, body, kwargs) if success: now = time.time() for recipient in recipients: self.email_counts[recipient].append(now) return success `

Email Queue Management

`python import queue import threading import json from datetime import datetime

class EmailQueue: def __init__(self, notifier, max_workers=3): self.notifier = notifier self.email_queue = queue.Queue() self.max_workers = max_workers self.workers = [] self.running = False self.failed_emails_file = "failed_emails.json" def start(self): self.running = True for i in range(self.max_workers): worker = threading.Thread(target=self._worker, name=f"EmailWorker-{i}") worker.daemon = True worker.start() self.workers.append(worker) def stop(self): self.running = False # Add sentinel values to wake up workers for _ in range(self.max_workers): self.email_queue.put(None) def _worker(self): while self.running: try: email_data = self.email_queue.get(timeout=1) if email_data is None: # Sentinel value break success = self.notifier.send_email(email_data['params']) if not success: self._save_failed_email(email_data) self.email_queue.task_done() except queue.Empty: continue except Exception as e: print(f"Worker error: {e}") def queue_email(self, priority=1, email_params): email_data = { 'timestamp': datetime.now().isoformat(), 'priority': priority, 'params': email_params } self.email_queue.put(email_data) def _save_failed_email(self, email_data): try: failed_emails = [] try: with open(self.failed_emails_file, 'r') as f: failed_emails = json.load(f) except FileNotFoundError: pass failed_emails.append(email_data) with open(self.failed_emails_file, 'w') as f: json.dump(failed_emails, f, indent=2) except Exception as e: print(f"Failed to save failed email: {e}") `

Security Considerations

Secure Credential Storage

| Method | Security Level | Use Case | |--------|----------------|----------| | Environment Variables | Medium | Development/Testing | | Encrypted Configuration Files | High | Production Systems | | Key Management Services | Very High | Enterprise Applications | | OAuth2 Tokens | High | Cloud Services |

Best Security Practices

1. Use App-Specific Passwords: Never use main account passwords 2. Implement TLS/SSL: Always encrypt email transmission 3. Rotate Credentials: Regularly update passwords and tokens 4. Limit Permissions: Use accounts with minimal required privileges 5. Monitor Usage: Log and monitor email sending patterns

Troubleshooting

Common Issues and Solutions

| Issue | Possible Cause | Solution | |-------|----------------|----------| | Authentication Failed | Wrong credentials | Verify username/password, use app password | | Connection Timeout | Firewall/Network | Check network connectivity, firewall rules | | SSL Certificate Error | Certificate issues | Update certificates, verify server settings | | Rate Limiting | Too many emails | Implement rate limiting, use multiple accounts | | Large Attachment Failure | Size limits | Compress files, use file sharing links |

Debugging Commands

`bash

Test SMTP connectivity

telnet smtp.gmail.com 587

Check DNS resolution

nslookup smtp.gmail.com

Test with openssl

openssl s_client -connect smtp.gmail.com:587 -starttls smtp

Verify certificate

openssl s_client -showcerts -connect smtp.gmail.com:587 `

Best Practices

Email Content Guidelines

1. Clear Subject Lines: Include severity and system information 2. Structured Body: Use consistent formatting for alerts 3. Include Context: Add timestamp, hostname, and relevant details 4. Action Items: Clearly state required actions 5. Contact Information: Include escalation contacts

Performance Optimization

1. Connection Pooling: Reuse SMTP connections when possible 2. Batch Processing: Group emails to same recipients 3. Asynchronous Sending: Use queues for non-critical emails 4. Template Caching: Pre-compile email templates 5. Attachment Optimization: Compress large files

Monitoring and Maintenance

1. Log All Activities: Track sent, failed, and queued emails 2. Monitor Delivery Rates: Track bounce rates and failures 3. Regular Testing: Verify email functionality periodically 4. Update Dependencies: Keep email libraries current 5. Backup Configurations: Maintain configuration backups

This comprehensive guide provides the foundation for implementing robust email notification systems across multiple scripting environments, ensuring reliable automated communications for system monitoring and alerting needs.

Tags

  • SMTP
  • email automation
  • notifications
  • python-scripting
  • system-administration

Related Articles

Popular Technical Articles & Tutorials

Explore our comprehensive collection of technical articles, programming tutorials, and IT guides written by industry experts:

Browse all 8+ technical articles | Read our IT blog

Automating Email Notifications in Scripts: Complete Guide