Code Monkey home page Code Monkey logo

awslambda-es-hipchat's Introduction

Introduction

Sometimes AWS cognito can't deliver the message to registered user for confirmation. We have a demand to track these messages and notify the bounced/complaint messages via a Hipchat channel. Here is the workflow

AWS cognito ----> AWS SES ---> AWS SNS ---> AWS Lambda ---> AWS Elasticsearch/Kibana and Hipchat

AWS SES has ability to handle such bounces and complaint emails.

Walkthrough steps

AWS cognito

In Message Customizations section, select SES Region drop-down box; for example, US East if your SES is set up there. Select the email address you want to handle bouncing and complaining from Email drop-down box.

AWS Elasticsearch

Granting access to AWS ES cluster's endpoint. Amazon ES adds support for an authorization layer by integrating with IAM. The IAM polocy allow or deny Actions (HTTP methods) against Resources (the domain endpoint).

An IAM policy can be

Resource-based policies โ€“ This type of policy is attached to an AWS resource, such as an Amazon S3 bucket, as described in Writing IAM Policies: How to Grant Access to an Amazon S3 Bucket. Identity-based policies โ€“ This type of policy is attached to an identity, such as an IAM user, group, or role.

We can apply both type of policies above by using two strategies to authenticate Amazon ES requests

  • Omit the Principal from your policy and specify an IP Condition
  • based on the originating Principal

However, given that AWS Lambda function is not in VPC and it doesn't have a specific IPaddress so that we has to use the second strategy. An example is

{
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::XXXXXXX:role/lambda_verifyEmailBounceComplaints"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:us-east-1:XXXXXXX:domain/email-tracking/*"
}

We can attach the policies that we build in IAM or in the Amazon ES console to specific IAM entities (in other words, the Amazon ES domain, users, groups, and roles):

To grant access to AWS Elasticsearch endpoint, either specify it in the AWS Elasticsearch Modify Access policy or in AWS IAM role attached to AWS Lambda function

Modify Access policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:us-east-1:XXXXXXX:domain/email-tracking/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "trusted IP address"
          ]
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::XXXXXXX:role/lambda_verifyEmailBounceComplaints"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:us-east-1:XXXXXXX:domain/email-tracking/*"
    }
  ]
}

Create an IAM role which is attached to AWS Lambda function

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Sid": "ES",
            "Effect": "Allow",
            "Action": [
                "es:*"
            ],
            "Resource": [
                "arn:aws:es:us-east-1:XXXXXXX:domain/email-tracking",
                "arn:aws:es:us-east-1:XXXXXXX:domain/email-tracking/*"
            ]
        }
    ]
}

Create AWS Lambda Function using Environment Variables:

Use aws lambda cli or AWS Lambda console to create a lambda function. https://docs.aws.amazon.com/lambda/latest/dg/get-started-create-function.html

Select the Execution Role is the IAM role created above

We should define the following Environment Variables in advance

  • ELASTICSEARCH_URL
  • ELASTICSEARCH_INDEX
  • HIPCHAT_V2_TOKEN
  • HIPCHAT_ROOMID

which will be used in hipchat.py

es_host = os.getenv('ELASTICSEARCH_URL')
es_index = os.getenv('ELASTICSEARCH_INDEX')
hipchat_v2_token = os.getenv('HIPCHAT_V2_TOKEN')
hipchat_room_id = os.getenv('HIPCHAT_ROOMID')

We no need to set the following reserved variables because They are generated via the IAM execution role specified for this Lambda function (assume role concept)

AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN

AWS_REGION is The AWS region where the Lambda function is executed.

Note:

the Lambda handle name must be the name of the main file (omit .py) and the lambda_handler function name (eg: hipchat.lambda_handler)

Invoking AWS Lambda function using AWS SNS notification

  • Create a SNS topic
  • Subscribe to topic: choose protocol which is the AWS Lambda function and the Endpoint is the AWS Lambda ARN above.

https://docs.aws.amazon.com/sns/latest/dg/sns-lambda.html

Configure AWS SES to notify SNS

In AWS SES console, Email Address > Click on the email address configured to deliver message from cognito.

Under Notification section, choose the Amazon SNS topic we created above for both Bounces and Complaints

https://docs.aws.amazon.com/ses/latest/DeveloperGuide/dashboardconfigureSESnotifications.html

Tools and Code Explanation

The project uses pipenv which is a official recommended packaging tool to add packages/dependencies and generate very important Pipfile.lock which produces deterministic builds. Some python packages are not available in AWS Lambda for example elasticsearch. We need to pack them along with our code.

pipenv run pip install -r <(pipenv lock -r) --target ./

which will download and install all dependencies with specific versions written in the Pipfile.lock file

I wrote a small bash file to repeat zip and upload the AWS lambda zip ball file to AWS lambda for every change. However, you can change it to make it works with a CI/CD such as Travis or Jenkins

Besides, the code is self-explanation. The main lambda_handler function writes to AWS ES and sends notification to hipchat channel. It is using aws-requests-auth lib which supports AWS Signature Version 4

ref : https://github.com/DavidMuller/aws-requests-auth/blob/master/README.md

#Troubleshooting

If you encounter the following error in the cloudwatch, it is likely that you misconfigured the IAM policy mentioned above

TransportError(403, u'
{
    "Message": "User: arn:aws:sts::XXXXXX:assumed-role/lambda_verifyEmailBounceComplaints/ES_test is not authorized to perform: es:ESHttpPost on resource: email-tracking"
}
'): AuthorizationException
Traceback (most recent call last):
File "/var/task/hipchat.py", line 111, in lambda_handler
body=message)
File "/var/task/elasticsearch/client/utils.py", line 76, in _wrapped
return func(*args, params=params, **kwargs)
File "/var/task/elasticsearch/client/__init__.py", line 319, in index
_make_path(index, doc_type, id), params=params, body=body)
File "/var/task/elasticsearch/transport.py", line 314, in perform_request
status, headers_response, data = connection.perform_request(method, url, params, body, headers=headers, ignore=ignore, timeout=timeout)
File "/var/task/elasticsearch/connection/http_requests.py", line 90, in perform_request
self._raise_error(response.status_code, raw_data)
File "/var/task/elasticsearch/connection/base.py", line 125, in _raise_error
raise HTTP_EXCEPTIONS.get(status_code, TransportError)(status_code, error_message, additional_info)
AuthorizationException: TransportError(403, u'
{
    "Message": "User: arn:aws:sts::XXXXXX:assumed-role/lambda_verifyEmailBounceComplaints/ES_test is not authorized to perform: es:ESHttpPost on resource: email-tracking"
}
')

ref: https://aws.amazon.com/blogs/security/how-to-control-access-to-your-amazon-elasticsearch-service-domain/

###Test run the test

cd test
aws --region us-east-1 ses send-email --from [email protected]  --destination file://destination.json --message file://message.json

ToAddresses can be set to [email protected] [email protected]

the notificationType in the message written to ES will be alternatively Delivery,Bounce, and Complaints

MAINTAINER Inspectorio DevOps <[email protected]>

awslambda-es-hipchat's People

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.