Skip to content

Instantly share code, notes, and snippets.

@rambabusaravanan
Last active November 7, 2023 03:35
Show Gist options
  • Save rambabusaravanan/dfa2b80369c89ce7517855f4094367e6 to your computer and use it in GitHub Desktop.
Save rambabusaravanan/dfa2b80369c89ce7517855f4094367e6 to your computer and use it in GitHub Desktop.
AWS Lambda Function to send SMTP Email
import smtplib
import os
def send_email(host, port, username, password, subject, body, mail_to, mail_from = None, reply_to = None):
if mail_from is None: mail_from = username
if reply_to is None: reply_to = mail_to
message = """From: %s\nTo: %s\nReply-To: %s\nSubject: %s\n\n%s""" % (mail_from, mail_to, reply_to, subject, body)
print (message)
try:
server = smtplib.SMTP(host, port)
server.ehlo()
server.starttls()
server.login(username, password)
server.sendmail(mail_from, mail_to, message)
server.close()
return True
except Exception as ex:
print (ex)
return False
def lambda_handler(event, context):
# initialize variables
username = os.environ['USERNAME']
password = os.environ['PASSWORD']
host = os.environ['SMTPHOST']
port = os.environ['SMTPPORT']
mail_from = os.environ.get('MAIL_FROM')
mail_to = os.environ['MAIL_TO'] # separate multiple recipient by comma. eg: "abc@gmail.com, xyz@gmail.com"
origin = os.environ.get('ORIGIN')
origin_req = event['headers'].get('Host')
reply_to = event['queryStringParameters'].get('reply')
subject = event['queryStringParameters']['subject']
body = event['body']
# vaildate cors access
cors = ''
if not origin:
cors = '*'
elif origin_req in [o.strip() for o in origin.split(',')]:
cors = origin_req
# send mail
success = False
if cors:
success = send_email(host, port, username, password, subject, body, mail_to, mail_from, reply_to)
# prepare response
response = {
"isBase64Encoded": False,
"headers": { "Access-Control-Allow-Origin": cors }
}
if success:
response["statusCode"] = 200
response["body"] = '{"status":true}'
elif not cors:
response["statusCode"] = 403
response["body"] = '{"status":false}'
else:
response["statusCode"] = 400
response["body"] = '{"status":false}'
return response
@rambabusaravanan
Copy link
Author

rambabusaravanan commented Aug 5, 2017

Environment Variables

SMTPHOST: smtp.gmail.com
SMTPPORT: 587
USERNAME: yourname@domain.com
PASSWORD: { app-specific-password }
MAIL_FROM: Alias Name <alias@domain.com> (Optional)
MAIL_TO: Recipient1 <recipient1@domain.com>, Recipient2 <recipient2@domain.com>
ORIGIN: https://domain.com, https://www.domain.com (Optional)

Query Params

subject="System Notice"
reply="reply@domain.com" (Optional)

Request Body

[ Email Text Message ]

@rambabusaravanan
Copy link
Author

Replace the string 'Host' with 'origin' at line number 32

@ashishhavells
Copy link

Hi you may use below codes for creating multiple backups in a day with instance name:

import boto3
import collections
import datetime
import sys
import pprint

ec = boto3.client('ec2')
#image = ec.Image('id')

def lambda_handler(event, context):

reservations = ec.describe_instances(
    Filters=[
        {'Name': 'tag-key', 'Values': ['odd', 'Odd']},
    ]
).get(
    'Reservations', []
)
print "Hello",
for h in reservations:
    print h
instances = sum(
    [
        [i for i in r['Instances']]
        for r in reservations
    ], [])

print "Found %d instances that need backing up" % len(instances)

to_tag = collections.defaultdict(list)

for instance in instances:
    try:
        retention_days = [
            int(t.get('Value')) for t in instance['Tags']
            if t['Key'] == 'Retention'][0]
    except IndexError:
        retention_days = 7
        
    finally:

    #for dev in instance['BlockDeviceMappings']:
    #    if dev.get('Ebs', None) is None:
    #        continue
    #    vol_id = dev['Ebs']['VolumeId']
    #    print "Found EBS volume %s on instance %s" % (
    #        vol_id, instance['InstanceId'])

        #snap = ec.create_snapshot(
        #    VolumeId=vol_id,
        #)
        
        #create_image(instance_id, name, description=None, no_reboot=False, block_device_mapping=None, dry_run=False)
        # DryRun, InstanceId, Name, Description, NoReboot, BlockDeviceMappings
        create_time = datetime.datetime.now()
        create_fmt = create_time.strftime('%H-%M-%S--%Y-%m-%d')
    
        #Variables for storing ec2 Instance Name and ec2 Instance ID
        ec2_Instance_Name = ''
        ec2_Instance_ID = ''
        ec2_Instance_ID = instance['InstanceId']
        ec2_Instance_Name = get_instance_name(ec2_Instance_ID)
        
        AMIid = ec.create_image(InstanceId=instance['InstanceId'], Name="Lambda - " + instance['InstanceId'] + " " + ec2_Instance_Name + " (Odd) from " + create_fmt, Description="Lambda created AMI of instance " + instance['InstanceId'] + " from " + create_fmt, NoReboot=True, DryRun=False)

    
        pprint.pprint(instance)
        #sys.exit()
        #break
    
        #to_tag[retention_days].append(AMIid)
        
        to_tag[retention_days].append(AMIid['ImageId'])
        
        print "Retaining AMI %s of instance %s for %d days" % (
            AMIid['ImageId'],
            instance['InstanceId'],
            retention_days,
        )

print to_tag.keys()

for retention_days in to_tag.keys():
    delete_date = datetime.date.today() + datetime.timedelta(days=retention_days)
    delete_fmt = delete_date.strftime('%m-%d-%Y')
    print "Will delete %d AMIs on %s" % (len(to_tag[retention_days]), delete_fmt)
    
    #break

    ec.create_tags(
        Resources=to_tag[retention_days],
        Tags=[
            {'Key': 'DeleteOn', 'Value': delete_fmt},
        ]
    )

Function to get Instance Name

def get_instance_name(fid):
# When given an instance ID as str e.g. 'i-1234567', return the instance 'Name' from the name tag.
ec2 = boto3.resource('ec2')
ec2instance = ec2.Instance(fid)
instancename = ''
for tags in ec2instance.tags:
if tags["Key"] == 'Name':
instancename = tags["Value"]
return instancename

@ashishhavells
Copy link

can anyone help me to generate summary in mail for daily backup detail.
Like:
AMI Name and Backup Status.

@rambabusaravanan
Copy link
Author

Good one to note 🎉 👍

@tcotton1
Copy link

tcotton1 commented Aug 21, 2019

I believe the line
server.sendmail(mail_from, mail_to, message)
needs changing to
server.sendmail(mail_from, mail_to.split(","), message)
Otherwise only the first recipient in the comma separated list will get the email.

@hazue
Copy link

hazue commented Apr 29, 2020

Would be great if it has function to send attachment

@LoharG
Copy link

LoharG commented Mar 25, 2021

{
"errorMessage": "'headers'",
"errorType": "KeyError",
"stackTrace": [
" File "/var/task/lambda_function.py", line 32, in lambda_handler\n origin_req = event['headers'].get('origin')\n"
]
}

Function Logs
START RequestId: 57014a4c-2757-447b-930a-7231aba9bd57 Version: $LATEST
[ERROR] KeyError: 'headers'
Traceback (most recent call last):
  File "/var/task/lambda_function.py", line 32, in lambda_handler
    origin_req = event['headers'].get('origin')
END RequestId: 57014a4c-2757-447b-930a-7231aba9bd57
REPORT RequestId: 57014a4c-2757-447b-930a-7231aba9bd57 Duration: 14.10 ms Billed Duration: 15 ms Memory Size: 128 MB Max Memory Used: 49 MB Init Duration: 137.49 ms

Request ID
57014a4c-2757-447b-930a-7231aba9bd57

@LoharG
Copy link

LoharG commented Mar 25, 2021

Getting error after Replace the string 'Host' with 'origin' at line number 32

@ShaktiAhad
Copy link

I wasn’t able to send email using Gmail username/password. I made following changes to make it work.

Create password for external App(In our case Lambda function)

Got to Google Account Security and Scroll down to "Signing in to Google" option. If you don't have 2 step verification on; enable it. Then you will see "App passwords" option. Generate a password for your lambda function.

  • Make sure to copy the Password else you will not be able to copy it later.

Sample lambda function in python to check SMTP server

I have used a this following code to verify my SMTP server is working.

import smtplib, os

def lambda_handler(event=None, context=None):
    sender = os.getenv("SENDER")
    receiver = os.getenv("RECEIVER")
    password = os.getenv("PASSWORD")
    host = os.getenv("SMTPHOST")
    port = os.getenv("SMTPPORT")

    try:
        server = smtplib.SMTP(host, port)
        server.ehlo()
        server.starttls()
        server.login(sender, password)
        server.sendmail(sender, receiver, msg="Subject: Test\n\n This is a test from lambda")
        server.close()
        return True
    except Exception as ex:
        print (ex)
        return False

lambda_handler()

Send attachment using SMTP server

As I have used xlsWriter module to create Excel file, I had to create a lambda layer for that. First I will explain how I created the lambda layer.

xlsWriterlambda layer creation:

Create a file xlsWriter.py on your local ~/Desktop path and copy/paste the following command. It will create xlswriter.zip file on your Desktop.

"""
......This scrpit is for creating xlsWriter lambda layer......
"""
import os

os.system(
    '''
    echo ".............All good! Ready to go!............."
    export ZIP_FILE_NAME="xlswriter.zip" 
    cd ~/Desktop
    mkdir -p python/lib/python3.9/site-packages
    pip3 install XlsxWriter --target ~/Desktop/python/lib/python3.9/site-packages
    zip -r xlswriter.zip ./python 
    echo ".............Check the file:${ZIP_FILE_NAME} on your Desktop............."
    '''
)

After creating the zip file use following steps to upload file and create the layer.

  1. Login to your AWS account
  2. Got to Lambda service
  3. Click Layers under Additional resources from left plane
  4. Click Create layer
  5. Put a name for lambda layer
  6. Upload the xlswriter.zip file.
  7. Copy the ARN from the top right corner. We need the ARN to use the lambda layer in our lambda function.

There are some other fields such as Description, Compatible architectures, Compatible runtimes, etc. These are optionals.

Sample lambda function in python to send an attachment:

  • Lambda function creation and add the lambda layer:
  1. Create a lambda function SMTP_attachment_lambda.
  2. Under Function Overview, Click Layers option just below the function name.
  3. Click Add Layer.
  4. Choose Specify An ARN option paste the layer ARN in the blank field.
  5. Click verify then Add.
  • Make sure you create the excel file in /tmp folder. Else you will get following error "errorMessage": "[Errno 30] Read-only file system: '/hello.xlsx'"
  • Also you need to create __init.py__ file. Else you will get lots of following error "File \"<frozen importlib._bootstrap>\"
  • Lambda Folder tree:
SMTP_attachment_lambda
├── __init.py__
└── lambda_function.py
  • Code:
import smtplib, os, xlsxwriter
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def lambda_handler(event=None, context=None):
    
    # Create an Excel File
    workbook = xlsxwriter.Workbook('/tmp/hello.xlsx')
    worksheet = workbook.add_worksheet()
    worksheet.write('A1', 'Hello world')
    workbook.close()
    
    # Attach Excel file and send
    sender = os.getenv("SENDER")
    receiver = os.getenv("RECEIVER")
    password = os.getenv("PASSWORD")
    host = os.getenv("SMTPHOST")
    port = os.getenv("SMTPPORT")

    msg = MIMEMultipart()
    msg['From'] = sender
    msg['To'] = receiver
    msg['Subject'] = "Test_email"
    text = """
        This is a test email with an excel file attached. 

        Regards,
        Lambda
    """
    msg.attach(MIMEText(text))
    with open('/tmp/hello.xlsx', 'rb') as file:
            part = MIMEApplication(file.read())
            part.add_header('Content-Disposition',f'attachment; filename={os.path.basename("hello.xlsx")}')
            msg.attach(part)


    try:
        smtp = smtplib.SMTP(host=host, port=port)
        smtp.starttls()
        smtp.login(sender, password)
        smtp.sendmail(sender, receiver, msg.as_string())
        print("Success: Email has been sent")
    except Exception:
        print("Error: Unable to send email")
        raise Exception


lambda_handler()

@ubuntu821
Copy link

python3.6 error
image

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