Setup Email Notifications for AWS Backups

  1. If does not exist - create an IAM Role like this one:

 2. Select target region

3. Create SNS
There should be at least 2 SNS Topics – 1 for a BackupRadar email, another should be an  internal consumer for error messages from the Lambda function.
It is for types of errors that may happen during the lambda function – for example, can not parse a volume name.
If there is a problem with a snapshot generation and there is no lambda function errors – the info will be sent to BackupRadar

BackupRadar topic should be include a subscription with a BackupRadar email and “email” protocol


4. Create Lamdba function

4a.Click AWS Lambda >> Functions >> “Create function”
Existing role – select the role from the 1st step of this document

4b Copy and paste the code of (find it below) into the inline editor

4c. Add “SUCCESS_SNS_ARNs” and “FAILURE_SNS_ARNs” Environment variables
Each value is an array of ARNs, so they must be in the format: [‘…’,’…’] even if there is only 1 ARN: [‘…’]

4d. Set 6 seconds time out

4e. Click “Save” button

5. Create CloudWatch event rule

5a. Click CloudWatch >> Events >> Rules >> “Create rule” button 

5b. Click “Configure details” button, (optionally) set “Description” and “Enabled” checkbox


To test it works:

  • Go to EC2 Dashboard > Volumes
  • Select a volume and choose “Create Snapshot” in “Actions” button.


==== Copy from the next line until the end of the file ====

from __future__ import print_function

#from datetime import time


import json

import boto3

import os

import ast

import time


#print('Loading function (' + + ')')


def lambda_handler(event, context):

    #print("Received event: " + json.dumps(event, indent=2))

    prevTime = time.time()



        snapshotId = get_snaphot_id(event)

        prevTime   = my_print('Got snapshotId', prevTime)

        volumeId   = get_volume_id(event)

        prevTime   = my_print('Got volumeId', prevTime)

        volumeName = get_volume_name(volumeId)

        prevTime   = my_print('Got volumeName', prevTime)

        #volumeName = 'SOM-CRMWED:C:MEDHHS:'

        parts      = parse_volume_name(volumeName)

        prevTime   = my_print('Got parts', prevTime)

        data       = prepare_data(parts, event)

        prevTime   = my_print('Got data', prevTime)

        send_by_sns_topics(snapshotId, data)

        my_print('Finish', prevTime)

        return 0

    except Exception as error:

        prevTime = my_print('Exception happened', prevTime)

        error_by_sns_topics(snapshotId, error)

        my_print('Finish', prevTime)



def get_snaphot_id(event):

    if 'detail' in event:

        if 'snapshot_id' in event['detail']:

            return event['detail']['snapshot_id']

    raise ValueError("Error! Can't get snapshotId", event)


def get_volume_id(event):

    if 'detail' in event:

        if 'source' in event['detail']:

            volumeId = event['detail']['source']

            return volumeId.rpartition('/')[2]

    raise ValueError("Error! Can't get volumeId", event)


def get_volume_name(volumeId):

    ec2 = boto3.resource('ec2')

    volume = ec2.Volume(volumeId)

    for i, item in enumerate(volume.tags):

        if 'Key' in item and item['Key'] == 'Name' and 'Value' in item:

            return item['Value']

    raise ValueError("Error! Can't get volume name", volumeId, volume.tags)


def parse_volume_name(volumeName):

    parts = volumeName.split(':')

    if(3 == len(parts)):

        return {

            'server_name': parts[0],

            'volume':      parts[1],

            'client_name': parts[2]


    raise ValueError("Error! Can't parse volume name", volumeName, "Expected format: server_name:volume:client_name")


def prepare_data(parts, event):

    if 'detail' in event:

        if 'result' in event['detail']:

            parts['result'] = event['detail']['result']

            parts['original-data'] = event

            return parts

    raise ValueError("Error! Can't get result", event, parts)


def send_by_sns_topics(snapshotId, data):

    client = boto3.client('sns')

    prevTime = time.time()

    for sns_arn in ast.literal_eval(os.environ["SUCCESS_SNS_ARNs"]):

        prevTime   = my_print('Send to ' + sns_arn, prevTime)

        response = client.publish(


            Subject='Snapshot ' + snapshotId + ' was processed successfully',

            Message=json.dumps(data, indent=4)


        prevTime   = my_print('Sent', prevTime)


def error_by_sns_topics(snapshotId, error):

    client = boto3.client('sns')

    prevTime = time.time()

    for sns_arn in ast.literal_eval(os.environ["FAILURE_SNS_ARNs"]):

        prevTime   = my_print('Send to ' + sns_arn, prevTime)

        response = client.publish(


            Subject='Snapshot ' + snapshotId + ' processing failure',



        prevTime   = my_print('Sent', prevTime)


def json_serial(obj):

    """JSON serializer for objects not serializable by default json code"""


    if isinstance(obj, (datetime, date)):

        return obj.isoformat()

    raise TypeError ("Type %s not serializable" % type(obj))


def my_print(msg, prevTime):

    now = time.time()

    print(msg + ' (' + str(now - prevTime) + ')')

    return now


Article is closed for comments.