Skip to content

Instantly share code, notes, and snippets.

@ensean
Last active March 21, 2025 02:15
Show Gist options
  • Save ensean/f3d7940d55f3256b9ec5dbb964388006 to your computer and use it in GitHub Desktop.
Save ensean/f3d7940d55f3256b9ec5dbb964388006 to your computer and use it in GitHub Desktop.
send email via ses smtp endpoint
#!/bin/bash
#
# Script to send emails via AWS SES SMTP endpoint using curl
# Usage: ./send-ses-email.sh -f [email protected] -t [email protected] -s "Subject" -b "Body text" [-a attachment.pdf]
# Default AWS SES SMTP settings
AWS_REGION="us-east-1"
SES_SMTP_HOST="email-smtp.us-east-1.amazonaws.com"
SES_SMTP_PORT="587"
SES_SMTP_USERNAME=""
SES_SMTP_PASSWORD=""
# Function to display usage
usage() {
echo "Usage: $0 -f [email protected] -t [email protected] -s \"Subject\" -b \"Body text\" [-a attachment.pdf]"
echo " -f FROM_EMAIL : Sender email address"
echo " -t TO_EMAIL : Recipient email address (can be used multiple times)"
echo " -c CC_EMAIL : CC email address (can be used multiple times)"
echo " -s SUBJECT : Email subject"
echo " -b BODY : Email body text"
echo " -a ATTACHMENT : File to attach (can be used multiple times)"
echo " -r AWS_REGION : AWS region (default: us-east-1)"
echo " -u USERNAME : AWS SES SMTP username"
echo " -p PASSWORD : AWS SES SMTP password"
echo " -h : Display this help message"
exit 1
}
# Check if curl command is available
if ! command -v curl &> /dev/null; then
echo "Error: 'curl' command not found. Please install curl."
echo " For macOS: brew install curl"
echo " For Ubuntu/Debian: sudo apt-get install curl"
echo " For CentOS/RHEL: sudo yum install curl"
exit 1
fi
# Function to generate a random boundary string for MIME parts
generate_boundary() {
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1
}
# Function to generate a random message ID
generate_message_id() {
local random=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)
echo "${random}@email.amazonses.com"
}
# Parse command line arguments
TO_EMAILS=()
CC_EMAILS=()
ATTACHMENTS=()
while getopts "f:t:c:s:b:a:r:u:p:h" opt; do
case $opt in
f) FROM_EMAIL="$OPTARG" ;;
t) TO_EMAILS+=("$OPTARG") ;;
c) CC_EMAILS+=("$OPTARG") ;;
s) SUBJECT="$OPTARG" ;;
b) BODY="$OPTARG" ;;
a) ATTACHMENTS+=("$OPTARG") ;;
r) AWS_REGION="$OPTARG"
SES_SMTP_HOST="email-smtp.$AWS_REGION.amazonaws.com" ;;
u) SES_SMTP_USERNAME="$OPTARG" ;;
p) SES_SMTP_PASSWORD="$OPTARG" ;;
h) usage ;;
\?) echo "Invalid option: -$OPTARG" >&2; usage ;;
esac
done
# Validate required parameters
if [ -z "$FROM_EMAIL" ] || [ ${#TO_EMAILS[@]} -eq 0 ] || [ -z "$SUBJECT" ] || [ -z "$BODY" ]; then
echo "Error: Missing required parameters."
usage
fi
if [ -z "$SES_SMTP_USERNAME" ] || [ -z "$SES_SMTP_PASSWORD" ]; then
echo "Error: AWS SES SMTP credentials are required."
usage
fi
# Create a temporary file for the email content
create_email_content() {
local boundary="$(generate_boundary)"
local mime_boundary="--${boundary}"
local mime_end="${mime_boundary}--"
local date_str=$(date -R)
local message_id=$(generate_message_id)
local temp_file=$(mktemp)
# Email headers
{
echo "From: ${FROM_EMAIL}"
echo "To: $(IFS=,; echo "${TO_EMAILS[*]}")"
# Add CC if present
if [ ${#CC_EMAILS[@]} -gt 0 ]; then
echo "Cc: $(IFS=,; echo "${CC_EMAILS[*]}")"
fi
echo "Subject: ${SUBJECT}"
echo "Date: ${date_str}"
echo "Message-ID: <${message_id}>"
echo "MIME-Version: 1.0"
# If we have attachments, use multipart
if [ ${#ATTACHMENTS[@]} -gt 0 ]; then
echo "Content-Type: multipart/mixed; boundary=\"${boundary}\""
echo ""
echo "${mime_boundary}"
echo "Content-Type: text/plain; charset=utf-8"
echo "Content-Transfer-Encoding: 7bit"
echo ""
echo "${BODY}"
# Add attachments
for attachment in "${ATTACHMENTS[@]}"; do
if [ -f "$attachment" ]; then
local filename=$(basename "$attachment")
local mime_type=$(file --mime-type -b "$attachment" 2>/dev/null || echo "application/octet-stream")
echo ""
echo "${mime_boundary}"
echo "Content-Type: ${mime_type}; name=\"${filename}\""
echo "Content-Transfer-Encoding: base64"
echo "Content-Disposition: attachment; filename=\"${filename}\""
echo ""
# Base64 encode with proper line breaks (76 chars per line)
if ! base64 < "$attachment" 2>/dev/null | fold -w 76; then
echo "Warning: Failed to encode attachment: $attachment" >&2
continue
fi
else
echo "Warning: Attachment file not found: $attachment" >&2
fi
done
# End of multipart message
echo ""
echo "${mime_end}"
else
# Simple text email
echo "Content-Type: text/plain; charset=utf-8"
echo ""
echo "${BODY}"
fi
} > "$temp_file"
echo "$temp_file"
}
# Prepare curl recipients arguments
prepare_recipients() {
local rcpt_args=""
# Add all To recipients
for email in "${TO_EMAILS[@]}"; do
# Strip any surrounding quotes and whitespace
email=$(echo "$email" | sed -e 's/^[[:space:]"'\'']*//g' -e 's/[[:space:]"'\'']*$//g')
# Ensure the email is properly formatted
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
rcpt_args="${rcpt_args} --mail-rcpt ${email}"
else
echo "Warning: Skipping invalid email format: $email" >&2
fi
done
# Add all CC recipients
for email in "${CC_EMAILS[@]}"; do
# Strip any surrounding quotes and whitespace
email=$(echo "$email" | sed -e 's/^[[:space:]"'\'']*//g' -e 's/[[:space:]"'\'']*$//g')
# Ensure the email is properly formatted
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
rcpt_args="${rcpt_args} --mail-rcpt ${email}"
else
echo "Warning: Skipping invalid email format: $email" >&2
fi
done
echo "$rcpt_args"
}
# Send the email
echo "Sending email via AWS SES SMTP ($SES_SMTP_HOST:$SES_SMTP_PORT)..."
# Create email content file
EMAIL_FILE=$(create_email_content)
# Prepare recipient arguments
RCPT_ARGS=$(prepare_recipients)
# Check if we have valid recipients
if [ -z "$RCPT_ARGS" ]; then
echo "Error: No valid recipient email addresses provided."
rm -f "$EMAIL_FILE"
exit 1
fi
# Strip any surrounding quotes from the sender email
FROM_EMAIL=$(echo "$FROM_EMAIL" | sed -e 's/^[[:space:]"'\'']*//g' -e 's/[[:space:]"'\'']*$//g')
# Debug info
echo "From: $FROM_EMAIL"
echo "To: ${TO_EMAILS[*]}"
if [ ${#CC_EMAILS[@]} -gt 0 ]; then
echo "CC: ${CC_EMAILS[*]}"
fi
echo "Subject: $SUBJECT"
echo "Attachments: ${#ATTACHMENTS[@]}"
# Use eval to properly handle the recipient arguments
echo "Sending email..."
eval "curl --ssl-reqd \
--url smtp://${SES_SMTP_HOST}:${SES_SMTP_PORT} \
--user ${SES_SMTP_USERNAME}:${SES_SMTP_PASSWORD} \
--mail-from ${FROM_EMAIL} \
${RCPT_ARGS} \
--upload-file ${EMAIL_FILE}"
RESULT=$?
# Clean up
rm -f "$EMAIL_FILE"
# Check result
if [ $RESULT -eq 0 ]; then
echo "Email sent successfully!"
else
echo "Failed to send email. Exit code: $RESULT"
echo "If you're having issues:"
echo " - Verify your AWS SES SMTP credentials are correct"
echo " - Ensure your sender email is verified in AWS SES"
echo " - Check if you're in sandbox mode and need to verify recipient emails"
echo " - Make sure port $SES_SMTP_PORT is not blocked by your firewall"
fi
exit $RESULT
@ensean
Copy link
Author

ensean commented Mar 18, 2025

./send-ses-email.sh \
  -f "[email protected]" \
  -t "[email protected]" \
  -s "Test Email from AWS SES" \
  -b "This is a test email sent via AWS SES SMTP endpoint." \
  -r "us-west-2" \
  -u "YOUR_SES_SMTP_USERNAME" \
  -p "YOUR_SES_SMTP_PASSWORD"

image

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment