From 0d3bd4fa7fa298dbf3dc4eaa3db575676c727fa1 Mon Sep 17 00:00:00 2001 From: Eamonn Moloney Date: Thu, 10 Mar 2022 14:04:18 +0100 Subject: [PATCH 1/3] Add a resource to create the aws_lambda_permission necessary for the target group to send requests to a lambda --- README.md | 84 ++++++++++++++++++++++++++++++++--- examples/complete-alb/main.tf | 8 +--- main.tf | 27 +++++++++++ 3 files changed, 108 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 590d619..b25a4b2 100644 --- a/README.md +++ b/README.md @@ -31,16 +31,89 @@ module "alb" { backend_protocol = "HTTP" backend_port = 80 target_type = "instance" - targets = [ - { + targets = { + my_target = { target_id = "i-0123456789abcdefg" port = 80 - }, - { + } + my_other_target = { target_id = "i-a1b2c3d4e5f6g7h8i" port = 8080 } - ] + } + } + ] + + https_listeners = [ + { + port = 443 + protocol = "HTTPS" + certificate_arn = "arn:aws:iam::123456789012:server-certificate/test_cert-123456789012" + target_group_index = 0 + } + ] + + http_tcp_listeners = [ + { + port = 80 + protocol = "HTTP" + target_group_index = 0 + } + ] + + tags = { + Environment = "Test" + } +} +``` + +HTTP and HTTPS listeners to lambda: + +The targets block has 7 attributes specific to lambda. The details about available here [https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission#action](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission#action) + +| Name | Default | Required | +|-----------------------------|--------------------------------------|----------| +| `lambda_function_name` | | `yes` | +| `lambda_action` | `lambda:InvokeFunction` | `no` | +| `lambda_event_source_token` | `null` | `no` | +| `lambda_principal` | `elasticloadbalancing.amazonaws.com` | `no` | +| `lambda_qualifier` | `null` | `no` | +| `lambda_source_account` | `null` | `no` | +| `lambda_statement_id` | `AllowExecutionFromLb` | `no` | + + +```hcl +module "alb" { + source = "terraform-aws-modules/alb/aws" + version = "~> 6.0" + + name = "my-alb" + + load_balancer_type = "application" + + vpc_id = "vpc-abcde012" + subnets = ["subnet-abcde012", "subnet-bcde012a"] + security_groups = ["sg-edcd9784", "sg-edcd9785"] + + access_logs = { + bucket = "my-alb-logs" + } + + target_groups = [ + { + name_prefix = "l1-" + target_type = "lambda" + lambda_multi_value_headers_enabled = true + targets = { + my_lambda = { + target_id = "i-0123456789abcdefg" + lambda_function_name = "my_lambda_function_name" + } + my_other_lambda = { + target_id = "i-a1b2c3d4e5f6g7h8i" + lambda_function_name = "my_other_lambda_function_name" + } + } } ] @@ -309,6 +382,7 @@ No modules. | Name | Type | |------|------| +| [aws_lambda_permission.with_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | | [aws_lb.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource | | [aws_lb_listener.frontend_http_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource | | [aws_lb_listener.frontend_https](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource | diff --git a/examples/complete-alb/main.tf b/examples/complete-alb/main.tf index 401a9c6..2f4a540 100644 --- a/examples/complete-alb/main.tf +++ b/examples/complete-alb/main.tf @@ -373,13 +373,9 @@ module "alb" { target_type = "lambda" lambda_multi_value_headers_enabled = true targets = { - # Lambda function permission should be granted before - # it is used. There can be an error: - # NB: Error registering targets with target group: - # AccessDenied: elasticloadbalancing principal does not - # have permission to invoke ... from target group ... my_lambda = { - target_id = module.lambda_function.lambda_function_arn + target_id = module.lambda_function.lambda_function_arn + lambda_function_name = module.lambda_function.lambda_function_name } } }, diff --git a/main.tf b/main.tf index fba0654..d92b415 100644 --- a/main.tf +++ b/main.tf @@ -129,6 +129,31 @@ locals { if k == "targets" ] ])...) + + # Filter out the attachments for lambda functions. The ALB target group needs permission to forward a request on to + # the specified lambda function. This filtered list is used to create those permission resources + target_group_attachments_lambda = merge(flatten([ + for target_group_attachments_key, target_group_attachments_value in local.target_group_attachments : [ + for k, v in target_group_attachments_value : { (target_group_attachments_key) = target_group_attachments_value } + if k == "lambda_function_name" + ] + ])...) +} + +resource "aws_lambda_permission" "with_lb" { + for_each = var.create_lb && local.target_group_attachments_lambda != null ? local.target_group_attachments_lambda : {} + + # required + function_name = each.value.lambda_function_name + + # optional + action = lookup(each.value, "lambda_action", null) != null ? each.value.lambda_action : "lambda:InvokeFunction" + event_source_token = lookup(each.value, "lambda_event_source_token", null) + principal = lookup(each.value, "lambda_principal", null) != null ? each.value.lambda_principal : "elasticloadbalancing.amazonaws.com" + qualifier = lookup(each.value, "lambda_qualifier", null) + source_account = lookup(each.value, "lambda_source_account", null) + source_arn = aws_lb_target_group.main[each.value.tg_index].arn + statement_id = lookup(each.value, "lambda_statement_id", null) != null ? each.value.lambda_statement_id : "AllowExecutionFromLb" } resource "aws_lb_target_group_attachment" "this" { @@ -138,6 +163,8 @@ resource "aws_lb_target_group_attachment" "this" { target_id = each.value.target_id port = lookup(each.value, "port", null) availability_zone = lookup(each.value, "availability_zone", null) + + depends_on = [aws_lambda_permission.with_lb] } resource "aws_lb_listener_rule" "https_listener_rule" { From 9327a7ee83116ef332dc3b2731285f4fb5401164 Mon Sep 17 00:00:00 2001 From: Eamonn Moloney Date: Thu, 10 Mar 2022 14:33:46 +0100 Subject: [PATCH 2/3] Removed the trigger attribute from the lambda that was used to configure permissions for the alb. This is now covered in the module itself --- examples/complete-alb/main.tf | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/complete-alb/main.tf b/examples/complete-alb/main.tf index 2f4a540..6e95439 100644 --- a/examples/complete-alb/main.tf +++ b/examples/complete-alb/main.tf @@ -478,12 +478,5 @@ module "lambda_function" { create_package = false local_existing_package = local.downloaded - allowed_triggers = { - AllowExecutionFromELB = { - service = "elasticloadbalancing" - source_arn = module.alb.target_group_arns[1] # index should match the correct target_group - } - } - depends_on = [null_resource.download_package] } From bc8b13df46a6e2f5314465658316dfb209db574f Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Fri, 20 May 2022 13:48:19 +0200 Subject: [PATCH 3/3] Fixed code and example --- .pre-commit-config.yaml | 2 +- README.md | 75 +-------------------------------- examples/complete-alb/README.md | 3 +- examples/complete-alb/main.tf | 48 ++++++++++++++++++--- main.tf | 29 ++++++------- 5 files changed, 59 insertions(+), 98 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bbf2a55..19dda01 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.67.0 + rev: v1.71.0 hooks: - id: terraform_fmt - id: terraform_validate diff --git a/README.md b/README.md index a67be11..4b90900 100644 --- a/README.md +++ b/README.md @@ -69,79 +69,6 @@ module "alb" { } ``` -HTTP and HTTPS listeners to lambda: - -The targets block has 7 attributes specific to lambda. The details about available here [https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission#action](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission#action) - -| Name | Default | Required | -|-----------------------------|--------------------------------------|----------| -| `lambda_function_name` | | `yes` | -| `lambda_action` | `lambda:InvokeFunction` | `no` | -| `lambda_event_source_token` | `null` | `no` | -| `lambda_principal` | `elasticloadbalancing.amazonaws.com` | `no` | -| `lambda_qualifier` | `null` | `no` | -| `lambda_source_account` | `null` | `no` | -| `lambda_statement_id` | `AllowExecutionFromLb` | `no` | - - -```hcl -module "alb" { - source = "terraform-aws-modules/alb/aws" - version = "~> 6.0" - - name = "my-alb" - - load_balancer_type = "application" - - vpc_id = "vpc-abcde012" - subnets = ["subnet-abcde012", "subnet-bcde012a"] - security_groups = ["sg-edcd9784", "sg-edcd9785"] - - access_logs = { - bucket = "my-alb-logs" - } - - target_groups = [ - { - name_prefix = "l1-" - target_type = "lambda" - lambda_multi_value_headers_enabled = true - targets = { - my_lambda = { - target_id = "i-0123456789abcdefg" - lambda_function_name = "my_lambda_function_name" - } - my_other_lambda = { - target_id = "i-a1b2c3d4e5f6g7h8i" - lambda_function_name = "my_other_lambda_function_name" - } - } - } - ] - - https_listeners = [ - { - port = 443 - protocol = "HTTPS" - certificate_arn = "arn:aws:iam::123456789012:server-certificate/test_cert-123456789012" - target_group_index = 0 - } - ] - - http_tcp_listeners = [ - { - port = 80 - protocol = "HTTP" - target_group_index = 0 - } - ] - - tags = { - Environment = "Test" - } -} -``` - HTTP to HTTPS redirect and HTTPS cognito authentication: ```hcl @@ -384,7 +311,7 @@ No modules. | Name | Type | |------|------| -| [aws_lambda_permission.with_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | +| [aws_lambda_permission.lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | | [aws_lb.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource | | [aws_lb_listener.frontend_http_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource | | [aws_lb_listener.frontend_https](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource | diff --git a/examples/complete-alb/README.md b/examples/complete-alb/README.md index dc8e98d..ead2e9a 100644 --- a/examples/complete-alb/README.md +++ b/examples/complete-alb/README.md @@ -38,7 +38,8 @@ Note that this example may create resources which cost money. Run `terraform des |------|--------|---------| | [acm](#module\_acm) | terraform-aws-modules/acm/aws | ~> 3.0 | | [alb](#module\_alb) | ../../ | n/a | -| [lambda\_function](#module\_lambda\_function) | terraform-aws-modules/lambda/aws | ~> 3.0 | +| [lambda\_with\_allowed\_triggers](#module\_lambda\_with\_allowed\_triggers) | terraform-aws-modules/lambda/aws | ~> 3.0 | +| [lambda\_without\_allowed\_triggers](#module\_lambda\_without\_allowed\_triggers) | terraform-aws-modules/lambda/aws | ~> 3.0 | | [lb\_disabled](#module\_lb\_disabled) | ../../ | n/a | | [security\_group](#module\_security\_group) | terraform-aws-modules/security-group/aws | ~> 4.0 | diff --git a/examples/complete-alb/main.tf b/examples/complete-alb/main.tf index b896d5d..54814a8 100644 --- a/examples/complete-alb/main.tf +++ b/examples/complete-alb/main.tf @@ -405,9 +405,18 @@ module "alb" { target_type = "lambda" lambda_multi_value_headers_enabled = true targets = { - my_lambda = { - target_id = module.lambda_function.lambda_function_arn - lambda_function_name = module.lambda_function.lambda_function_name + lambda_with_allowed_triggers = { + target_id = module.lambda_with_allowed_triggers.lambda_function_arn + } + } + }, + { + name_prefix = "l2-" + target_type = "lambda" + targets = { + lambda_without_allowed_triggers = { + target_id = module.lambda_without_allowed_triggers.lambda_function_arn + attach_lambda_permission = true } } }, @@ -496,12 +505,36 @@ resource "null_resource" "download_package" { } } -module "lambda_function" { +module "lambda_with_allowed_triggers" { + source = "terraform-aws-modules/lambda/aws" + version = "~> 3.0" + + function_name = "${random_pet.this.id}-with-allowed-triggers" + description = "My awesome lambda function (with allowed triggers)" + handler = "index.lambda_handler" + runtime = "python3.8" + + publish = true + + create_package = false + local_existing_package = local.downloaded + + allowed_triggers = { + AllowExecutionFromELB = { + service = "elasticloadbalancing" + source_arn = module.alb.target_group_arns[1] # index should match the correct target_group + } + } + + depends_on = [null_resource.download_package] +} + +module "lambda_without_allowed_triggers" { source = "terraform-aws-modules/lambda/aws" version = "~> 3.0" - function_name = "${random_pet.this.id}-lambda" - description = "My awesome lambda function" + function_name = "${random_pet.this.id}-without-allowed-triggers" + description = "My awesome lambda function (without allowed triggers)" handler = "index.lambda_handler" runtime = "python3.8" @@ -510,5 +543,8 @@ module "lambda_function" { create_package = false local_existing_package = local.downloaded + # Allowed triggers will be managed by ALB module + allowed_triggers = {} + depends_on = [null_resource.download_package] } diff --git a/main.tf b/main.tf index e825ec2..6c0a168 100644 --- a/main.tf +++ b/main.tf @@ -136,28 +136,25 @@ locals { # Filter out the attachments for lambda functions. The ALB target group needs permission to forward a request on to # the specified lambda function. This filtered list is used to create those permission resources - target_group_attachments_lambda = merge(flatten([ - for target_group_attachments_key, target_group_attachments_value in local.target_group_attachments : [ - for k, v in target_group_attachments_value : { (target_group_attachments_key) = target_group_attachments_value } - if k == "lambda_function_name" - ] - ])...) + target_group_attachments_lambda = { + for k, v in local.target_group_attachments : + (k) => merge(v, { lambda_function_name = split(":", v.target_id)[6] }) + if try(v.attach_lambda_permission, false) + } } -resource "aws_lambda_permission" "with_lb" { +resource "aws_lambda_permission" "lb" { for_each = var.create_lb && local.target_group_attachments_lambda != null ? local.target_group_attachments_lambda : {} - # required function_name = each.value.lambda_function_name + qualifier = try(each.value.lambda_qualifier, null) - # optional - action = lookup(each.value, "lambda_action", null) != null ? each.value.lambda_action : "lambda:InvokeFunction" - event_source_token = lookup(each.value, "lambda_event_source_token", null) - principal = lookup(each.value, "lambda_principal", null) != null ? each.value.lambda_principal : "elasticloadbalancing.amazonaws.com" - qualifier = lookup(each.value, "lambda_qualifier", null) - source_account = lookup(each.value, "lambda_source_account", null) + statement_id = try(each.value.lambda_statement_id, "AllowExecutionFromLb") + action = try(each.value.lambda_action, "lambda:InvokeFunction") + principal = try(each.value.lambda_principal, "elasticloadbalancing.amazonaws.com") source_arn = aws_lb_target_group.main[each.value.tg_index].arn - statement_id = lookup(each.value, "lambda_statement_id", null) != null ? each.value.lambda_statement_id : "AllowExecutionFromLb" + source_account = try(each.value.lambda_source_account, null) + event_source_token = try(each.value.lambda_event_source_token, null) } resource "aws_lb_target_group_attachment" "this" { @@ -168,7 +165,7 @@ resource "aws_lb_target_group_attachment" "this" { port = lookup(each.value, "port", null) availability_zone = lookup(each.value, "availability_zone", null) - depends_on = [aws_lambda_permission.with_lb] + depends_on = [aws_lambda_permission.lb] } resource "aws_lb_listener_rule" "https_listener_rule" {