Getting lambda arn from an external module? - amazon-web-services

I'm using this module. Specifically, my lambda looks like this:
module "lambda_function_existing_package_s3" {
source = "terraform-aws-modules/lambda/aws"
function_name = "lambda"
description = "lambda"
lambda_role = aws_iam_role.iam_for_lambda.arn
handler = "lambda_function.lambda_handler"
runtime = "python3.8"
timeout = 30
create_package = false
s3_existing_package = {
bucket = var.bucket_id
key = "bucket/location"
}
vpc_subnet_ids = [var.subnet1, var.subnet2]
vpc_security_group_ids = [aws_security_group.allow_egress.id]
attach_network_policy = true
environment_variables = {
1 = "1",
2 = "2"
}
}
I'm trying to export the arn of this lambda as SSM, like this:
resource "aws_ssm_parameter" "lambda" {
name = "/lambda/arn"
type = "String"
value = module.lambda_function_existing_package_s3.this.arn
}
This is my latest attempt, which throws an error, saying that
this object doesn't have attribute "this", even though the docs mention it. I have also tried:
value = module.lambda_function_existing_package_s3.arn, and
value = aws_lambda_function.this.arn
None of it works, and I'm not sure why. I've managed to deploy the lambda without errors when I commented out the SSM part, and this is what the output looks like:
name: this
provider: hashicorp/aws
type: aws_lambda_function
module: lambda_function_existing_package_s3
What am I missing here?

According to the module outputs documentation, the namespace for the Lambda function ARN value would be module.<declared name>.lambda_function_arn.
resource "aws_ssm_parameter" "lambda" {
name = "/lambda/arn"
type = "String"
value = module.lambda_function_existing_package_s3.lambda_function_arn
}

Related

Terraform AWS: SQS destination for Lambda doesn't get added

I have a working AWS project that I'm trying to implement in Terraform.
One of the steps requires a lambda function to query athena and return results to SQS (I am using this module for lambda instead of the original resource). Here is the code:
data "archive_file" "go_package" {
type = "zip"
source_file = "./report_to_SQS_go/main"
output_path = "./report_to_SQS_go/main.zip"
}
resource "aws_sqs_queue" "emails_queue" {
name = "sendEmails_tf"
}
module "lambda_report_to_sqs" {
source = "terraform-aws-modules/lambda/aws"
function_name = "report_to_SQS_Go_tf"
handler = "main"
runtime = "go1.x"
create_package = false
local_existing_package = "./report_to_SQS_go/main.zip"
attach_policy_json = true
policy_json = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect : "Allow"
Action : [
"dynamodb:*",
"lambda:*",
"logs:*",
"athena:*",
"cloudwatch:*",
"s3:*",
"sqs:*"
]
Resource : ["*"]
}
]
})
destination_on_success = aws_sqs_queue.emails_queue.arn
timeout = 200
memory_size = 1024
}
The code works fine and produces the desired output; however, the problem is, SQS doesn't show up as a destination (although the Queue shows up in SQS normally and can send/recieve messages).
I don't think permissions are the problem because I can add SQS destinations manually from the console successfully.
The variable destination_on_success is only used if you also set create_async_event_config as true. Below is extracted from https://github.com/terraform-aws-modules/terraform-aws-lambda/blob/master
variables.tf
############################
# Lambda Async Event Config
############################
variable "create_async_event_config" {
description = "Controls whether async event configuration for Lambda Function/Alias should be created"
type = bool
default = false
}
variable "create_current_version_async_event_config" {
description = "Whether to allow async event configuration on current version of Lambda Function (this will revoke permissions from previous version because Terraform manages only current resources)"
type = bool
default = true
}
.....
variable "destination_on_failure" {
description = "Amazon Resource Name (ARN) of the destination resource for failed asynchronous invocations"
type = string
default = null
}
variable "destination_on_success" {
description = "Amazon Resource Name (ARN) of the destination resource for successful asynchronous invocations"
type = string
default = null
}
main.tf
resource "aws_lambda_function_event_invoke_config" "this" {
for_each = { for k, v in local.qualifiers : k => v if v != null && local.create && var.create_function && !var.create_layer && var.create_async_event_config }
function_name = aws_lambda_function.this[0].function_name
qualifier = each.key == "current_version" ? aws_lambda_function.this[0].version : null
maximum_event_age_in_seconds = var.maximum_event_age_in_seconds
maximum_retry_attempts = var.maximum_retry_attempts
dynamic "destination_config" {
for_each = var.destination_on_failure != null || var.destination_on_success != null ? [true] : []
content {
dynamic "on_failure" {
for_each = var.destination_on_failure != null ? [true] : []
content {
destination = var.destination_on_failure
}
}
dynamic "on_success" {
for_each = var.destination_on_success != null ? [true] : []
content {
destination = var.destination_on_success
}
}
}
}
}
So the destination_on_success is only used in this resource and this resources is only invoked if several conditions are met. The key one being var.create_async_event_config must be true.
You can see the example for this here https://github.com/terraform-aws-modules/terraform-aws-lambda/blob/be6cf9701071bf807cd7864fbcc751ed2552e434/examples/async/main.tf
module "lambda_function" {
source = "../../"
function_name = "${random_pet.this.id}-lambda-async"
handler = "index.lambda_handler"
runtime = "python3.8"
architectures = ["arm64"]
source_path = "${path.module}/../fixtures/python3.8-app1"
create_async_event_config = true
attach_async_event_policy = true
maximum_event_age_in_seconds = 100
maximum_retry_attempts = 1
destination_on_failure = aws_sns_topic.async.arn
destination_on_success = aws_sqs_queue.async.arn
}

Way to implement Multiple AWS lambda integrate with single API Gateway using Terraform

I want to execute multiple lambdas in a single API gateway using terraform module. I define my variable for lambdas like
variable "lambdas" {
description = "Map of Lambda function names and API gateway resource paths."
type = map(string)
default = {
"name" = "get-lambda-function",
"name" = "post-lambda-function",
"name" = "put-lambda-function",
"name" = "qr-lambda-function"
}
}
And my lambda.tf looks like
resource "aws_lambda_function" "lambda_functions" {
for_each = var.lambdas
function_name = each.value.name
filename = data.archive_file.lambda.output_path
source_code_hash = filebase64sha256(data.archive_file.lambda.output_path)
handler = var.handler
runtime = var.runtime
depends_on = [
#aws_iam_role_policy_attachment.lambda_logs
#aws_iam_role_policy_attachment.lambda_vpc,
aws_cloudwatch_log_group.lambda_fun
]
role = aws_iam_role.lambda_role.arn
}
Here for Lambda integration with API, invoking multiple lambdas with [each.key] URL looks like :
resource "aws_api_gateway_integration" "lambda" {
for_each = aws_api_gateway_method.proxyMethod
rest_api_id = each.value.rest_api_id
resource_id = each.value.resource_id
http_method = each.value.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.lambda_functions[each.key].invoke_arn
}
So is it possible to integrate multiple lambdas in single api gateway using terraform?

How to create a dynamic map in Terraform?

I have created 2 modules. One for SQS and another for SSM. My SQS creates 4 queues and i am trying to create corresponding entries in the parameter store for their url and arn. I am importing the SSM module inside my SQS module such that it creates the parameters right after SQS creation is done.
This is what my SQS module looks like :-
resource "aws_sqs_queue" "FirstStandardSQSQueue" {
name = "sqs-${var.stage}-one"
message_retention_seconds = var.SQSMsgRetention
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.FirstDeadLetterQueue.arn
maxReceiveCount = 2
})
}
resource "aws_sqs_queue" "FirstDeadLetterQueue" {
name = "sqs-dead-letter-${var.stage}-one"
receive_wait_time_seconds = var.SQSRecvMsgWaitTime
}
resource "aws_sqs_queue" "SecondStandardSQSQueue" {
name = "sqs-${var.stage}-two"
message_retention_seconds = var.SQSMsgRetention
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.SecondDeadLetterQueue.arn
maxReceiveCount = 3
})
}
resource "aws_sqs_queue" "SecondDeadLetterQueue" {
name = "sqs-dead-letter-${var.stage}-two"
receive_wait_time_seconds = var.SQSRecvMsgWaitTime
}
module "ssm" {
source = "../ssm/"
// How do i create a dynamic map???
for_each = var.Queues
name = "/new/${var.stage}/${each.key}"
type = "SecureString"
value = "${each.value}"
}
My SSM module looks like this :-
resource "aws_ssm_parameter" "main" {
name = var.name
type = var.type
value = var.value
}
I am trying to create either a map or somehow dynamically be able to pass values in my ssm module using for_each? I tried setting up something like this :-
variable "Queues" {
type = map
default = {
"FirstStandardSQSQueueUrl" = aws_sqs_queue.FirstStandardSQSQueue.url
"FirstStandardSQSQueueArn" = aws_sqs_queue.FirstStandardSQSQueue.arn
"SecondStandardSQSQueueUrl" = aws_sqs_queue.SecondStandardSQSQueue.url
"SecondStandardSQSQueueArn" = aws_sqs_queue.SecondStandardSQSQueue.arn
}
}
But this is invalid because i keep running into
Error: Variables not allowed line 40: Variables may not be used here.
Can someone suggest a better/right way to do it? Thank you.
As the error msg writes, you can't have dynamic variables. locals should be used instead.
locals {
Queues = {
"FirstStandardSQSQueueUrl" = aws_sqs_queue.FirstStandardSQSQueue.url
"FirstStandardSQSQueueArn" = aws_sqs_queue.FirstStandardSQSQueue.arn
"SecondStandardSQSQueueUrl" = aws_sqs_queue.SecondStandardSQSQueue.url
"SecondStandardSQSQueueArn" = aws_sqs_queue.SecondStandardSQSQueue.arn
}
}
Then you refer to the value as local.Queues in other parts of your code.

Terraform For Using AWS Lambda With Amazon Lex

I have been having some trouble trying to get the fulfillment_activity codehook to work so that I can use Lambda Functions for the backend. For some reason, I am getting this error message from Terraform.
Error: error waiting for Lex Bot (helloBot) create: unexpected state 'FAILED', wanted target 'NOT_BUILT, READY, READY_BASIC_TESTING'. last error: Intent 'sample_intent' has an invalid message version defined for its fulfillment.
Here is my Terraform config:
# AWS Lex Bot
resource "aws_lex_bot" "helloBot" {
depends_on = [ aws_lex_intent.sample_intent ]
locale = "en-US"
name = "helloBot"
process_behavior = "BUILD"
voice_id = "Salli"
create_version = true
idle_session_ttl_in_seconds = 300
child_directed = false
abort_statement {
message {
content = "Abort Abort!"
content_type = "PlainText"
}
}
clarification_prompt {
max_attempts = 2
message {
content = "No Idea What You're Saying!"
content_type = "PlainText"
}
}
intent {
intent_name = "sampleIntentName"
intent_version = aws_lex_intent.sample_intent.version
}
}
resource "aws_lambda_permission" "lex_sample_intent_lambda" {
statement_id = "AllowExecutionFromAmazonLex"
action = "lambda:InvokeFunction"
function_name = "someLambdaFunctionName"
principal = "lex.amazonaws.com"
# https://docs.aws.amazon.com/lex/latest/dg/gs-cli-update-lambda.html
source_arn = "arn:aws:lex:myRegion:accountId:intent:sampleIntentName:*"
}
# AWS Lex Intents
data "aws_lambda_function" "existing" {
function_name = "someLambdaFunctionName"
qualifier = "dev"
}
resource "aws_lex_intent" "sample_intent" {
create_version = true
name = "sampleIntentName"
fulfillment_activity {
type = "CodeHook"
code_hook {
message_version = "1.0"
uri = data.aws_lambda_function.existing.qualified_arn
}
}
sample_utterances = [
"hi",
"hello"
]
}
I looked at the cli documentation and it appears that we are supposed to use "1.0" for the message version.
It looks like the Terraform configuration should be correct. The problem was related to the data type of the message version when it was a variable. It was incorrectly set as a number instead of a string.

Why isn't my Event Bridge rule executing my Lambda function?

I am trying to create an Event Bridge rule that will run my Lambda function every 30 mins.
I based my code on this answer I found here on SO Use terraform to set up a lambda function triggered by a scheduled event source
Here is my terraform code:
monitoring/main.tf:
...
module "cloudwatch_event_rule" {
source = "./cloudwatch_event_rule"
extra_tags = local.extra_tags
}
module "lambda_function" {
source = "./lambda_functions"
extra_tags = local.extra_tags
alb_names = var.alb_names
slack_webhook_url = var.slack_webhook_url
environment_tag = local.environment_tag
}
module "cloudwatch_event_target" {
source = "./cloudwatch_event_target"
lambda_function_arn = module.lambda_function.detect_bad_rejects_on_alb_lambda_arn
cloudwatch_event_rule_name = module.cloudwatch_event_rule.cloudwatch_event_rule_name
extra_tags = local.extra_tags
}
monitoring/lambda_functions/main.tf:
resource "aws_lambda_function" "detect_bad_rejects_on_alb" {
filename = var.filename
function_name = var.function_name
role = aws_iam_role.detect_bad_reject_on_alb.arn
handler = var.handler
source_code_hash = filebase64sha256(var.filename)
runtime = var.runtime
timeout = var.timeout
environment {
...
}
}
monitoring/cloudwatch_event_rule/main.tf
resource "aws_cloudwatch_event_rule" "event_rule" {
name = var.rule_name
description = var.description
schedule_expression = var.schedule_expression
tags = ...
}
monitoring/cloudwatch_event_rule/variables.tf
...
variable "schedule_expression" {
type = string
default = "rate(30 minutes)"
}
...
monitoring/cloudwatch_event_target/main.tf
resource "aws_cloudwatch_event_target" "event_target" {
arn = var.lambda_function_arn
rule = var.cloudwatch_event_rule_name
input = var.input
}
This ends up creating the lambda function and the event bridge rule with my lambda function as its target with the schedule expression "rate(30 minutes)" but the lambda function is never executed? What am I doing wrong?
From what you posted is seems that you are not adding permissions for invocations. Your code does not show creation of aws_lambda_permission with proper rules. So you should add such permissions so that EventBridge can invoke your function (example):
resource "aws_lambda_permission" "event-invoke" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = var.function_name
principal = "events.amazonaws.com"
source_arn = module.cloudwatch_event_rule.cloudwatch_event_rule_arn
}
Make sure source_arn correctly points to the ARN of your event rule.