Use AWS Lambda and SES to send an email - python

I ran the following script which works on my computer but doesnt work in AWS Lambda. All I did was added "def lambda_handler(event, context): return event" function as its required by the lambda. I ran the Lambda test a few times.
I am not getting the SES email and there's no errors when I execute it in Lambda. Any idea why?
import boto3
from botocore.exceptions import ClientError
SENDER = "Sender Name <testu#test.com>"
RECIPIENT = "test#test.com"
CONFIGURATION_SET = "ConfigSet"
AWS_REGION = "ap-southeast-2"
SUBJECT = "Amazon SES Test (SDK for Python)"
BODY_TEXT = ("Amazon SES Test (Python)\r\n"
"This email was sent with Amazon SES using the "
"AWS SDK for Python (Boto)."
)
BODY_HTML = """<html>
</html>
"""
def lambda_handler(event, context):
return event
# The character encoding for the email.
CHARSET = "UTF-8"
# Create a new SES resource and specify a region.
client = boto3.client('ses',region_name=AWS_REGION)
try:
response = client.send_email(
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER,
ConfigurationSetName=CONFIGURATION_SET,
)
except ClientError as e:
print(e.response['Error']['Message'])
else:
print(response['MessageId'])

Just like what Anon Coward said, you have to perform the SES sending inside the handler function and put the return statement at the bottom of that function.
It should look something like this:
def lambda_handler(event, context):
response = client.send_email(PAYLOAD_HERE_)
return {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": json.dumps({
"Region ": json_region
})
}

Related

Writing a lambda function to upload files to my S3 bucket

I'm trying to write a lambda function to upload files to my S3 bucket.
I'm new to coding so I don't understand why this doesn't work.
I get a "KeyError" for Body. Can anyone explain what this means and how do I fix my code?
Thanks in advance.
import base64
import boto3
import json
import uuid
s3 = boto3.client('s3')
def lambda_handler(event, context):
print(event)
response = s3.put_object(
Bucket='s3-bucket',
Key=str(uuid.uuid4()) + '.jpg',
Body=event.body,
)
image = response['Body'].read()
return {
'headers': {
"Content-Type": "image/jpg",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
"Access-Control-Allow-Methods": "*",
"Access-Control-Allow-Credentials": True,
},
'statusCode': 200,
'body': base64.b64encode(image).decode('utf-8'),
'isBase64Encoded': True
}
I tried replacing Body = event.body with Body=event['body'] or Body = event('body') but it still doesn't work.
I expect my lambda function to be able to upload a file to my S3 bucket.
Your call to put_object will raise an exception if it fails. You can catch this and respond accordingly, for example:
import boto3
from botocore.exceptions import ClientError
def lambda_handler(event, context):
print(event)
try:
response = s3.put_object(
Bucket='s3-bucket',
Key=str(uuid.uuid4()) + '.jpg',
Body=event.body)
return {
'headers': { ... },
'statusCode': 200
}
except ClientError as e:
print("Error from put_object:", event)
return {
'headers': { ... },
'statusCode': 500
}

Amazon Lex V2 Lambda python code to get intent name or any response back from lambda is not working

'''
Trying to get response back as name of the intent from lambda for Amazon Lex v2. It can be string or any response back in simple program.
I have referred the V2 Lex documentation but I can come-up with below code which shows error after several attempts. https://docs.aws.amazon.com/lexv2/latest/dg/lambda.html
error : "Invalid Lambda Response: Received error response from Lambda: Unhandled"
'''
def lambda_handler(event, context):
entity = event["currentIntent"]["slots"]["Nm"].title()
intent = event["currentIntent"]["name"]
response = {
'sessionState': {
'dialogAction': {
'type': 'Close'
},
'state': 'Fulfilled'
},
'messages': [
'contentType': 'PlainText',
'content': "The intent you are in now is "+intent+"!"
],
}
return response
The 'messages' field is an array of objects, not an array of strings. It should be declared as follows:
'messages': [
{
'contentType': 'PlainText',
'content': "The intent you are in now is "+intent+"!"
}
]
Reference:
Amazon Lex - Lambda Response format
I faced the same issue. The solution that worked for me is like below
var response = {};
response.messages = [
message
];
response.sessionState = {
sessionAttributes:sessionAttributes,
intent : {
name : intentRequest.interpretations[0].intent.name,
state : 'Fulfilled'
},
dialogAction: {
type: "Close",
fulfillmentState: "Fulfilled"
}
};
Refer to lex v2 developer guide page 69 Response format
https://docs.aws.amazon.com/lexv2/latest/dg/lex2.0.pdf

How do I get an API response containing body, but no headers with Python & AWS Lambda?

I have created an API with AWS Lambda in Python. Unfortunately, the response contains the headers, and I would like it to only contain the body. The Lambda API looks like this:
import json
import boto3
def lambda_handler(event, context):
#Step1: Scan table
client = boto3.resource('dynamodb') #Access DynamoDB
table = client.Table("register-to-event") #Access table
response = table.scan()
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json",
},
"body": response["Items"]
}
The problem is that the API response contains headers and body when I call it. This is the response:
{
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": [
{
"your-email": "hannes.hannesen#googlemail.com",
"your-number": "004785454548",
"your-last-name": "Hannesen",
"your-first-name": "Hannes",
"ID": 3
},
{
"your-email": "stig.stigesen#googlemail.com",
"your-number": "+4754875456",
"your-last-name": "Stigesen",
"your-first-name": "Stig",
"ID": 0
}
]
}
The goal is to call the API and return only the body which is json like this:
[
{
"your-email": "hannes.hannesen#googlemail.com",
"your-number": "004785454548",
"your-last-name": "Hannesen",
"your-first-name": "Hannes",
"ID": 3
},
{
"your-email": "stig.stigesen#googlemail.com",
"your-number": "+4754875456",
"your-last-name": "Stigesen",
"your-first-name": "Stig",
"ID": 0
}
]
The Solution was to configure the API in AWS correctly by ticking the box next to "Use Lambda Proxy integration":
Use Lambda Proxy integration
My next problem ended up being "serializing decimals" as a result of DynamoDB making the keys of the table a type (Decimal) which does not exist in Python.
The solution to this can be found here: https://learn-to-code.workshop.aws/persisting_data/dynamodb/step-3.html

Format body mapping templates in API Gateway

Need your help! I have the below Lambda function that will take inputs from the API Gateway (using RestAPI Post method) and pass the same as Payload to a second Lambda function.
def lambda_handler(event, context):
customerName = event['Name']
customerEmail = event['EmailAddress']
input = {"Name": customerName, "EmailAddress": customerEmail}
response = lambda_client.invoke(
FunctionName='arn_of_the_lambda_to_be_invoked',
InvocationType='Event',
Payload=json.dumps(input))
Below will be my input to the API Gateway in JSON format -
{
"Name": "TestUser",
"EmailAddress": "test#abc.com"
}
Have tried Lambda proxy integration and Generic Body Mapping templates (from here). In both occasions, API Gateway returns the below error -
Response Body:
{
"errorMessage": "'Name'",
"errorType": "KeyError",
"stackTrace": [
" File \"/var/task/lambda_function.py\", line 7, in lambda_handler\n customerName = event['Name']\n"
]
}
With the same JSON input when I directly invoke the Lambda from the Lambda console, it works. I know that API Gateway, along with the JSON body, pushes many other things. However, I'm unable to figure out.
How do I get this working?
In the lambda proxy integration, the event will be in the following format.
{
"resource": "Resource path",
"path": "Path parameter",
"httpMethod": "Incoming request's method name"
"headers": {String containing incoming request headers}
"multiValueHeaders": {List of strings containing incoming request headers}
"queryStringParameters": {query string parameters }
"multiValueQueryStringParameters": {List of query string parameters}
"pathParameters": {path parameters}
"stageVariables": {Applicable stage variables}
"requestContext": {Request context, including authorizer-returned key-value pairs}
"body": "A JSON string of the request payload."
"isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode"
}
the values you posted will be inside body as a json string. you need to parse the json.
import json
def lambda_handler(event, context):
fullBody = json.loads(event['body'])
print('fullBody: ', fullBody)
body = fullBody['body']
customerName = body['Name']
customerEmail = body['EmailAddress']
input = {"Name": customerName, "EmailAddress": customerEmail}
response = lambda_client.invoke(
FunctionName='arn_of_the_lambda_to_be_invoked',
InvocationType='Event',
Payload=json.dumps(input))
Input format of a Lambda function for proxy integration
Please remember that the lambda is expected to return the output in the following format for Lambda Proxy integration.
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"multiValueHeaders": { "headerName": ["headerValue", "headerValue2", ...], ... },
"body": "..."
}

In the lambda function, how to display python variable in HTML

In the below lambda function, I want to pass "Message2" variable in the HTML "BODY_HTML". Is there a way to pass my event Message2 in the HTML.
import boto3
from botocore.exceptions import ClientError
def lambda_handler(event, context):
Message1 = event ["Message"]
print("Message_Received : ")
print(Message1)
Message2 = event ["Event"]
print("Event_Received : ")
print(Message2)
Message3 = event ["Sender"]
print("Sender : ")
print(Message3)
SENDER = "Redshift Pause Resuem Service <redshift#abc.com>"
RECIPIENT = Message3
#CONFIGURATION_SET = "ConfigSet"
AWS_REGION = "us-east-1"
SUBJECT = subject1
BODY_TEXT = ("Amazon SES Test (Python)\r\n"
"This email was sent with Amazon SES using the "
"AWS SDK for Python (Boto)."
)
BODY_HTML = **"""<html>
<head></head>
<body>
<p>I want to print here the variable "Message2"</p>
</body>
</html>
"""**
The character encoding for the email.
CHARSET = "UTF-8"
Create a new SES resource and specify a region.
client = boto3.client('ses',region_name=AWS_REGION)
Try to send the email.
try:
#Provide the contents of the email.
response = client.send_email(
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER,
)
# Display an error if something goes wrong.
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
Try using string formatting:
BODY_HTML = f"""<html>
<head></head>
<body>
<p>{Message2}</p>
</body>
</html>
"""
Or string concatenation:
BODY_HTML = f"<html><head></head><body><p>" + Message2 + "</p></body></html>"

Categories

Resources