AWS HTTP API Gateway Getting Double Messages from Browser - amazon-web-services

I have an AWS API Gateway setup to pass values from HTTP calls to a lambda.
When I use postman, everything works great. When I use the browser, however, I get 2 calls. One of which is the same as the postman call, the other is missing all of the arguments and causes an error.
resource "aws_apigatewayv2_api" "retry_api" {
name = "${var.environment}_${var.cdp_domain}_retry_api"
protocol_type = "HTTP"
description = "To pass commands into the retry lambda."
target = module.retry-support.etl_lambda_arn
}
resource "aws_lambda_permission" "allow_retry_api" {
statement_id = "AllowAPIgatewayInvokation"
action = "lambda:InvokeFunction"
function_name = module.retry-support.etl_lambda_arn
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.retry_api.execution_arn}/*/*"
}
I'm giving the parameter as such:
?type_of_retry=retry_event
And on the first event it comes in correctly:
'rawQueryString': 'type_of_retry=retry_event'
'queryStringParameters': {'type_of_retry': 'retry_event'}
On the second event the rawQueryString is an empty list and there is no queryStringParameters key at all.
If anyone knows how to eliminate that 2nd event from being generated that would be helpful. Thank you.

Turns out that 2nd message was a favicon request.

Related

How to subscibe to SNS topic with lambda ARN?

I created a lambda function using the aws_cdk.aws_lambda.Function constructor. Following that I aim to subscribe to a SNS topic I created with boto3. However, one of the arguments needed is the lambda function ARN which I try to get with dynamodb_lambda.function_arn but the problem is that the the attributes returns a unresolved token more specifically ${Token[TOKEN.240]}
Here is a portion of the code to further clarify what I'm doing.
dynamodb_lambda = lambda_.Function(
self, #scope
"foobar", #id
runtime = lambda_.Runtime.PYTHON_3_7,
handler = "lambda_handlers.dynamodb_lambda_handler", #filename.methodname at path
code = lambda_.Code.from_asset(path),
role = iam_.Role( #need this for cloudwatch access
self, #scope
"foobar", #id
assumed_by = iam_.ServicePrincipal('lambda.amazonaws.com'),
managed_policies = [
iam_.ManagedPolicy.from_aws_managed_policy_name('DynamoDBFullAccess')
]
)
)
client_sns = boto3.client("sns")
response = client_sns.create_topic(
Name = c.SNS_TOPIC_NAME,
Tags = [ # for easier filtering and searching
{
'Key': 'CohortStudent',
'Value': 'anon'
}
])
client_sns.subscribe(
TopicArn = response['TopicArn'],
Protocol = 'lambda', #usually "email" or "sms", see link above for possible values
Endpoint = dynamodb_lambda.function_arn
)
The last parameter is where I'm facing trouble. The lambda function isn't created yet so the ARN is a token, but the subscribe function doesn't accept that.
botocore.errorfactory.InvalidParameterException: An error occurred (InvalidParameter) when calling the Subscribe operation: Invalid parameter: Lambda endpoint ARN
Please help me understand how to figure this out. Any help is appreciated.
As luk2302 has commented, using boto3 calls to subscribe to a SNS Topic created using aws_cdk is wrong. A simple fix would be to use subscribe to the lambda using aws_cdk.

How to make a PubSub triggered Cloud Function with message ordering using terraform?

I am trying to create a Cloud Function that is triggered from a PubSub subscription, but I need to have the message ordering enabled. I know to use the event_trigger block in the google_cloudfunctions_function block, when creating a function linked to a subscription. However this does not like the enable_message_ordering as described under PubSub. When using the subscription push config, I don't know how I can get link the endpoint to the function.
So is there a way I can link the function to a subscription with message ordering enabled?
Can I just use the internal URL to the function as the push config URL?
You can't use background functions triggered by PubSub and message ordering (or filtering).
You have do deploy a HTTP functions (take care, the signature of the fonction change, and the the format of the PubSub message also change slightly).
Then create a PubSub PUSH subscriptions, use the Cloud Functions URL. The best is also to add a Service Account on PubSub to allow only it to call your Functions.
For completeness I wanted to add the terraform that I used to do this. In case others are looking.
# This is the HTTP function that processes the events from PubSub, note it is set as a HTTP trigger
resource "google_cloudfunctions_function" "processEvent" {
name = "processEvent"
runtime = var.RUNTIME
environment_variables = {
GCP_PROJECT_ID = var.GCP_PROJECT_ID
LOG_LEVEL = var.LOG_LEVEL
}
available_memory_mb = var.AVAILABLE_MEMORY
timeout = var.TIMEOUT
source_archive_bucket = var.SOURCE_ARCHIVE_BUCKET
source_archive_object = google_storage_bucket_object.processor-archive.name
trigger_http = true
entry_point = "processEvent"
}
# define the topic
resource "google_pubsub_topic" "event-topic" {
name = "event-topic"
}
# We need to create the subscription specifically as we need to enable message ordering
resource "google_pubsub_subscription" "processEvent_subscription" {
name = "processEvent_subscription"
topic = google_pubsub_topic.event-topic.name
ack_deadline_seconds = 20
push_config {
push_endpoint = "https://${var.REGION}-${var.GCP_PROJECT_ID}.cloudfunctions.net/${google_cloudfunctions_function.processEvent.name}"
oidc_token {
# a new IAM service account is need to allow the subscription to trigger the function
service_account_email = "cloudfunctioninvoker#${var.GCP_PROJECT_ID}.iam.gserviceaccount.com"
}
}
enable_message_ordering = true
}

Enable Provisioned Concurrency for a Lambda Authorizer

Using Terraform, I have a confiuration for an AWS HTTP API Gateway like so:
resource "aws_apigatewayv2_authorizer" "authorizer" {
api_id = module.api_gateway.this_apigatewayv2_api_id
name = "authorizer"
authorizer_payload_format_version = "2.0"
enable_simple_responses = true
authorizer_result_ttl_in_seconds = var.authorizer_result_ttl_in_seconds
authorizer_type = "REQUEST"
identity_sources = ["$request.header.Authorization"]
# Problem is below:
authorizer_uri = module.auth-authorizer-lambda.this_lambda_function_invoke_arn
}
when I use this_lambda_function_invoke_arn, this works fine but a Concurrency-provisioned version of the Lambda is not called (so the Lambda can work for, like, 4s). Typically one can refer to such a version by this_lambda_function_qualified_arn, but using it would result in an error:
Error: error updating API Gateway v2 authorizer: BadRequestException: Invalid Authorizer URI:
arn:aws:lambda:eu-west-1:<account-id>:function:authorizer:5.
Authorizer URI should be a valid API Gateway ARN that represents a Lambda function invocation.
How can I configure API gateway to use the particular version of an Authorizer lambda?
It is not shown what auth-authorizer-lambda module is, but the use of this_lambda_function_invoke_arn is incorrect. The correct form of authorizer_uri is shown in the following example:
arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:{account_id}:function:{lambda_function_name}/invocations
So you have to construct and provide the authorizer_uri in the form as shown above. Obviously, it must be adjusted with your region, account id and function name.
If anyone else runs into this... you can configure API gateway to use the particular version of an Authorizer lambda by using the invoke_arn of the alias.
resource "aws_lambda_alias" "test_lambda_alias" {
name = "my_alias"
description = "a sample description"
function_name = aws_lambda_function.lambda_function_test.arn
function_version = "1"
}
resource "aws_apigatewayv2_authorizer" "authorizer" {
api_id = module.api_gateway.this_apigatewayv2_api_id
name = "authorizer"
authorizer_payload_format_version = "2.0"
enable_simple_responses = true
authorizer_result_ttl_in_seconds = var.authorizer_result_ttl_in_seconds
authorizer_type = "REQUEST"
identity_sources = ["$request.header.Authorization"]
# Problem is below:
authorizer_uri = aws_lambda_alias.test_lambda_alias.invoke_arn
}
Then, as always don't forget to deploy the change to a stage to take effect.
The final reference will be this format:
arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:{account_id}:function:{lambda_function_name}:{lambda_alias}/invocations

Pointing API Gateway to specific version of a function or an alias

I have created a Lambda function using terraform like so:
module "my-lambda" {
source = "terraform-aws-modules/lambda/aws"
version = "~> v1.31.0"
function_name = "${var.environment_name}-${local.lambda_name}"
...
publish = true
}
#...
module "alias" {
source = "terraform-aws-modules/lambda/aws//modules/alias"
name = "${var.lambda_name}-latest"
function_name = module.my-lambda.this_lambda_function_name
function_version = module.my-lambda.this_lambda_function_version
}
so presently I have a published Lambda version with a number, to which I also added an alias, and also $LATEST version. The reason I need the published version is that Provisioned Concurrency can be only attached to a published version.
My API gateway integration looks like:
module "api_gateway" {
source = "terraform-aws-modules/apigateway-v2/aws"
version = "~> 0.9.0"
name = "${var.environment_name}-my-api"
# ...Abriged...
integrations = {
"GET /mymethod" = {
integration_type = "AWS_PROXY"
integration_http_method = "POST"
payload_format_version = "2.0"
lambda_arn = module.my-lambda.this_lambda_function_invoke_arn
}
}
However in console I can see that it is triggering the $LATEST version, not the published one. How I can alter the configuration so that a particular version (or alias) is triggered by the API Gateway integration?
I am not familiar with terraform but to invoke alias from api gateway you need to have lambda alias arn in API gateway integration.
You are getting lambda arn from lambda module but you need arn of alias.
lambda_arn = module.my-lambda.this_lambda_function_invoke_arn
Alias arn is nothing but <Lambda ARN>:<AliasName>.
So you try taking arn from module.alias or append alias name and lambda arn with : in integrations.

In AWS API Gateway, using Terraform, how do you create sub-collection resources?

I'm trying to create sub-collection resources under an existing resource, using a GET method; something like:
/customers/{customerId}/accounts or /customers/{customerId}/accounts/{accountId}
Using Terraform, I already managed to create my customers and customers/{customerId} resource – and they both work.
But when I try and add a resource under customers/{customerId}, I get the ever elusive Missing Authentication Token error (which I've come to learn is mostly just that API Gateway can't find the resource/implementation/lambda), even though everything seems to be wired-up correctly.
Example code:
resource "aws_api_gateway_resource" "customers" {
rest_api_id = "${aws_api_gateway_rest_api.my-api.id}"
parent_id = "${aws_api_gateway_rest_api.my-api.root_resource_id}"
path_part = "customers"
}
resource "aws_api_gateway_resource" "single-customer" {
rest_api_id = "${aws_api_gateway_rest_api.my-api.id}"
parent_id = "${aws_api_gateway_resource.customers.id}"
path_part = "{customerId}"
}
resource "aws_api_gateway_resource" "customers-accounts" {
rest_api_id = "${aws_api_gateway_rest_api.my-api.id}"
parent_id = "${aws_api_gateway_resource.single-customer.id}"
path_part = "accounts"
}
//----
// GET
//----
resource "aws_api_gateway_method" "get-customers-accounts" {
rest_api_id = "${aws_api_gateway_rest_api.my-api.id}"
resource_id = "${aws_api_gateway_resource.customers-accounts.id}"
http_method = "GET"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "get-customers-accounts-integration" {
content_handling = "CONVERT_TO_TEXT"
rest_api_id = "${aws_api_gateway_rest_api.my-api.id}"
resource_id = "${aws_api_gateway_resource.customers-accounts.id}"
http_method = "${aws_api_gateway_method.get-customers-accounts.http_method}"
type = "AWS_PROXY"
uri = "arn:aws:apigateway:${var.region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${var.region}:${var.account-id}:function:${var.customers-lambda}/invocations"
integration_http_method = "POST"
}
Ideas? The lambda does exist, everything looks right in the console, and I did reselect the lambda function in the API Gateway console (there's a bug AWS cli where you'll get the Missing Authentication Error if you don't go in a manually reselect your lambda in the console).
UPDATES
As I mentioned, the Terraform code appears to work – no error there. The literal message I get from trying to access the endpoint is
{ message: "Missing Authentication Token" }
No logs are outputted. If I try and test the resource/endpoint via the API Gateway Test button, I get a Malformed Lambda Proxy Response – but that's misleading, as many valid, working endpoints generate that same message when being run from the Test button
{ message: "Missing Authentication Token" }
I have encountered the same when tried to access the lambda via endpoint but later a day it works though I didn't modify any changes.
Check your lambda output or it might be a api-endpoint resolver issue.
Note: Make sure you had deployed api after creating api_gateway else you will get the same message.