Getting requester's IP address in AWS lambda function - amazon-web-services

I understand, from the answer to this question, that I can get the IP address of the caller of a node.js lambda function by doing this:
events['headers']['X-Forwarded-For']
It seems like, for Go, this information should be inside the context.Context for lambda signatures that take it. However, looking at the documentation, I don't see any mention of request headers. Is there a way to get the same information in a Go lambda function?

After doing some research on my own, I figured out that I needed to put my lambda function behind an API Gateway instance to get the information I was interested in. After doing that, I could modify the handler like this:
package main
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
func LambdaHandler(ctx context.Context, request events.APIGatewayV2HTTPRequest) (int, error) {
// some code
addr := request.RequestContext.HTTP.SourceIP
// some other code
return 0
}
func main() {
lambda.Start(LambdaHandler)
}
By doing this, the API gateway will populate all the request metadata I need and pass that on to my lambda function. The request body is now contained in request.Body so I can extract that data using JSON deserialization or whatever other method my data is encoded as.

Related

Go AWS Lambda: Where is event?

Most Lambda runtimes have the following handler signature, which allows accessing both the event and context objects passed into the Lambda:
lambdaHandler(event, context){}
However the documentation for Go Lambda handlers does not follow this convention as shown here: https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html
Question: How does one access the event object when using the Go Lambda runtime, i.e., when trying to determine the repository URL in an AWS CodeCommit Lambda Trigger (https://docs.aws.amazon.com/codecommit/latest/userguide/how-to-notify-lambda.html)?
Your expected event is of type events.CodeCommitEvent
func handler(ctx context.Context, codeEvent events.CodeCommitEvent) {
for _, record := range codeEvent.Records {
// do you magic here.
}
}

How to send multi part key value via http get

I am trying to learn to use AWS Lambda, DynamoDB, and API Gateway.
I am able to set up a run a successful test of the Lambda that would take querystring parameters and returns the data from the database and I know I have the ability to access the Lambda from my API as I have created one to just return hello world. However, I am stuck as to how to send the parameters I need to the Lambda. These work fine from the Lambda test feature within Lambda.
{
"queryStringParameters": {
"TableName": "Exams",
"Key": {
"ExamName": "MyFirstExam"
}
}
}
However I can not seem to create a html query http://apiurl/route?parameters
as I have no idea how to send the Key.
I have tried everything I can think of here is just a sample of what I have tried
http://apiurl/route?TableName=Exams&Key=ExamName&ExamName=MyFirstExam
http://apiurl/route?TableName=Exams&key=ExamName&Keyvalue=MyFirstExam
http://apiurl/route?TableName=Exams&key=ExamName&value=MyFirstExam
http://apiurl/route?TableName=Exams&key=ExamName:MyFirstExam
but nothing has worked so I am wondering how do you send key = ExamName : MyfirstExam so that it is a part of the querystring parameters?
UPDATE:
I found that the rest api gave me more feed back and it looks like I need to use
````http://apiURL/Route?TableName= Exams&Key= { ExamName: MyFirstExam } ```
as this populates the querystring parameters.
"queryStringParameters":{"TableName":"Exams","Key":"{ ExamName: MyFirstExam }"}
However, I am still getting an error. That leads me to believe that I still may not have the format right. The problem is that when I used the same data in the test from within the lambda console it works and returns the data. When I use the querystring above which appears to be the same data as used in the lambda test it throws an error.
Lambda execution failed with status 200 due to customer function error: The provided key element does not match the schema. So my Key is getting there as I get a status 200 and if I leave the key off of the querystring it throws and error that the key is missing but it appears to not be formatted correctly apparently or is it something else that I am missing. It appears to be the same as the format that is working in the Lambda test.
This works from the lambda test
{ "queryStringParameters": { "TableName": "Exams", "Key": { "ExamName": "MyFirstExam" } } }
this does not work from the API test
"queryStringParameters":{"TableName":"Exams","Key":"{ ExamName: MyFirstExam }"}
This is how I am formatting the querystring
http://apiURL/Route?TableName= Exams&Key= { ExamName: MyFirstExam }
How do I format a querystring so that it works from the API gateway?
The easiest and most commonly used method of joining API Gateway and lambda is through AWS_PROXY integration between your API and your function.
With this type of integration:
API Gateway applies a default mapping template to send the entire request to the Lambda function and transforms the output from the Lambda function to HTTP responses.
This means that every query parameter you provide through your API endpoint is going to be delivered directly into lambda function:
This request data includes the request headers, query string parameters, URL path variables, payload, and API configuration data.
The event format your lambda function gets is described here.
I resolved this by having the key part already in the code and just now only need to pass a normal variable ExamName=Exam.
var queryParameters ={
TableName: request.TableName,
Key:{
"ExamName": request.ExamName
},
};```

Is there any ways to set values to system fields of CloudWatch Logs?

I want to set values to the fields #requestId in lambda executions because logs outputted explicitly inside the source code does not contain the #requestId field value.
I've read an article and tried to output logs like the below but the #requestId did not to be filled.
fmt.Print(`{"requestId":"come on!!"}`)
Neither the below code.
fmt.Print(`{"#requestId":"come on!!"}`)
Are system fields protected?
When an AWS Lambda function is triggered, a context element is passed to the function.
From AWS Lambda Function Handler in Go - AWS Lambda:
func HandleRequest(ctx context.Context, name MyEvent) (string, error) {
return fmt.Sprintf("Hello %s!", name.Name ), nil
}
From AWS Lambda Context Object in Go - AWS Lambda:
Context Properties
AwsRequestID – The identifier of the invocation request.
Therefore, you can retrieve the Request ID from the context. Then, anything that your function prints will be captured in CloudWatch Logs. So, include the Request ID in your print statement.

Is it possible to access the original request body in a response body mapping template in AWS API Gateway?

Using API Gateway, I am trying to define a POST end point that accepts application/json to do the following:
Trigger a Lambda asynchronously
Respond with a JSON payload composed of elements from the request body
I have #1 working. I think it's by the book.
It's #2 I'm getting tripped up on. It looks like I don't have access to the request body in the context of the response mapping template. I have access to the original query params with $input.params but I cannot find any property that will give me the original request body, and I need it to get the data that I want to respond with. It's either that or I need to figure out how to get the asynchronous launch of a Lambda to somehow provide the original request body.
Does anyone know if this is possible?
My goal is to ensure that my API responds as fast as possible without incurring a cold start of a Lambda to respond AND simultaneously triggering an asynchronous workflow by starting a Lambda. I'd also be willing to integrate with SNS instead of Lambda directly and have Lambda subscribe to the topic but I don't know if that will get me access to the data I need in the response mapping template.
From https://stackoverflow.com/a/61482410/3221253:
Save the original request body in the integration mapping template:
#set($context.requestOverride.path.body = $input.body)
Retrieve it in the integration mapping response:
#set($body = $context.requestOverride.path.body)
{
"statusCode": 200,
"body": $body,
}
You can also access specific attributes:
#set($object = $util.parseJson($body))
{
"id": "$object.id"
}
To access the original request directly, you should use a Proxy Integration for Lambda rather than mapping things via a normal integration. You'll be able to access the entire request context, such as headers, path params, etc.
I have determined that it is not possible to do what I want to do.

What is the template body for an API Gateway to pass the complete $context.authorizer.claims to the Lambda function

I'm trying to make the Cognito work with a Lambda function associated with an API Gateway. And I would like to get the claims complete object passed to the Lambda function.
This is what I've got as the template body to pass the email address from claims to the Lambda function:
{
"email": "$context.authorizer.claims.email"
}
But I'm looking for something like this:
{
"claims": "$context.authorizer.claims"
}
Obviously, this does not work (it will return an empty string as for claims). And I think it's because claims is an object. So how can I pass an object (here it would be claims) to the Lambda function?
Calling $context.authorizer.claims returns null in the mapping template (source). You'll have to use Lambda Proxy integration for that.