Enable Provisioned Concurrency for a Lambda Authorizer - amazon-web-services

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

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
}

AWS HTTP API Gateway Getting Double Messages from Browser

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.

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.

How to set up a lambda alias with the same event source mapping as the LATEST/Unqualified lambda function in terraform

I'm trying to create a lambda alias for my lambda function using terraform. I've been able to successfully create the alias but the created alias is missing the dynamodb as the trigger.
how the event source is set up
resource "aws_lambda_event_source_mapping" "db_stream_trigger" {
batch_size = 10
event_source_arn = "${data.terraform_remote_state.testddb.table_stream_arn}"
enabled = true
function_name = "${aws_lambda_function.test_lambda.arn}"
starting_position = "LATEST"
}
how the alias is created
resource "aws_lambda_alias" "test_lambda_alias" {
count = "${var.create_alias ? 1 : 0}"
depends_on = [ "aws_lambda_function.test_lambda" ]
name = "test_alias"
description = "alias for my test lambda"
function_name = "${aws_lambda_function.test_lambda.arn}"
function_version = "${var.current_running_version}"
routing_config = {
additional_version_weights = "${map(
"${aws_lambda_function.test_lambda.version}", "0.5"
)}"
}
}
The lambda works with the dynamodb stream as a trigger
The Alias for the lambda is successfully created.
The Alias is using the correct version
The Alias is using the correct weight
The Alias is NOT using the dynamo-db stream as the event source
I had the wrong function_name for the resource "aws_lambda_event_source_mapping". I was providing it the main lambda function's arn as oppose to the alias lambda function's arn. Once i switched it to the alias's arn, I was able to successfully divide the traffic from the stream dependent on the weight!
From aws doc:
Simplify management of event source mappings – Instead of using Amazon Resource Names (ARNs) for Lambda function in event source mappings, you can use an alias ARN. This approach means that you don't need to update your event source mappings when you promote a new version or roll back to a previous version.
https://docs.aws.amazon.com/lambda/latest/dg/aliases-intro.html