skip to Main Content

I am currently learning for the AWS Developer Certification. Most of the learning material shows examples in the AWS Console, but I am trying to recreate all of this in Terraform since I guess it is much more likely to be used this way in a professional environment.

In one chapter an Application Load Balancer is linked to a Lambda Function.
I have been trying to recreate this in Terraform for a few days no, but it never worked.

My current configuration is this

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 4.41.0"
    }
  }
}

provider "aws" {
  region = "eu-central-1"
}

# Create an IAM role for the Lambda function
resource "aws_iam_role" "lambda_role" {
  name = "lambda_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "lambda.amazonaws.com"
        }
      }
    ]
  })
}

# Lambda Function Code as Data Source
data "archive_file" "code" {  
  type = "zip"  
  source_file = "./code/hello_world.py"
  output_path = "code.zip"
}

# Create the Lambda function 
resource "aws_lambda_function" "test_lambda" {
  function_name = "test_lambda"
  filename      = "code.zip"
  handler       = "provider.handler"
  role          = aws_iam_role.lambda_role.arn
  runtime       = "python3.9"
  memory_size   = 128
  timeout       = 30
  source_code_hash = data.archive_file.code.output_base64sha256
}

# Create the target group
resource "aws_lb_target_group" "lambda" {
  name        = "lambda-target-group"
  target_type = "lambda"
  depends_on = [aws_iam_role_policy_attachment.alb_policy_attach]
}

# Attach the Lambda function to the target group
resource "aws_lambda_permission" "with_lb" {
  statement_id  = "AllowExecutionFromlb"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.test_lambda.function_name
  principal     = "elasticloadbalancing.amazonaws.com"
  source_arn    = aws_lb_target_group.lambda.arn
}

resource "aws_lb_target_group_attachment" "test" {
  target_group_arn = aws_lb_target_group.lambda.arn
  target_id        = aws_lambda_function.test_lambda.arn
  depends_on       = [aws_lambda_permission.with_lb]
}

# Create the load balancer
resource "aws_lb" "lambda" {
  name               = "lambda-lb"
  internal           = false
  load_balancer_type = "application"
  subnets            = ["subnet-ce43c7b2", "subnet-6e119a22"]
  security_groups = [aws_security_group.lb_sg.id]
}

# Create IAM role for ALB
resource "aws_iam_role" "alb_role" {
  name = "alb_role"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "elasticloadbalancing.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

# Attach permissions policy to ALB role
resource "aws_iam_policy" "alb_permissions" {
  name        = "alb_permissions"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "elasticloadbalancing:DeregisterTargets",
        "elasticloadbalancing:RegisterTargets",
        "elasticloadbalancing:Describe*",
        "lambda:InvokeFunction"  
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "alb_policy_attach" {
  role       = aws_iam_role.alb_role.name
  policy_arn = aws_iam_policy.alb_permissions.arn
}

# Create the load balancer listener
resource "aws_lb_listener" "lambda" {
  load_balancer_arn = aws_lb.lambda.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.lambda.arn
  }
}

resource "aws_security_group" "lb_sg" {
  name        = "lb-sg"
  description = "Allow incoming traffic to load balancer"

  ingress {
    from_port   = 80 
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] 
  }

  egress {
    from_port   = 0
    to_port     = 0 
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

It gives me a 502 when accessing the Load Balancer. Also no Cloudwatch Logs are written, despite the ALB having permission to do so.

I have tried:

  • I have already consulted the Terraform Documentation, the AWS Documentation, searched for best practice Examples via Google (which only returned examples done in the AWS Console).
  • I have also asked Chat GPT, Microsoft Copilot and Cody, but they never produced functioning examples there where always mayor errors that I was unable to fix.
  • I also consulted AmazonQ/CodeWhisperer since I figured the Amazon AI would be more precise in regards to AWS specific problems. But the example given by AmazonQ also had mayor errors and I was unable to deploy them even after a lot of adjusting.
  • I have also tried to create the infrastructure in the console and export it to terraform, but this would require me to get familiar with Terraformer or Former 2 which, as far as I can tell, are completely new "rabbit holes" that I would like to avoid for now.

Could anyone please provide me with a working/best practice terraform example for an alb that is calling a lambda function including all necessary permissions and security groups?

2

Answers


  1. You would need to put your lambda function inside the VPC in which the load balancer is and then assign a security group to the lambda function which allows traffic from the load balancer. The lambda target would be failing health checks in the target group and thus giving 502 errors.

    Login or Signup to reply.
  2. You mention that "no Cloudwatch Logs are written, despite the ALB having permission to do so", which is strange, because you can’t assign IAM permissions to ALBs. Indeed, the aws_iam_role.alb_role IAM role you are creating in your Terraform code is never actually assigned to anything. That role is completely unused and pointless.

    The resource resource "aws_lambda_permission" "with_lb" is the only thing you should have needed to create to give the ALB access to call the Lambda function.

    The only logging you can enable on a load balancer are Access Logs, and Connection Logs, both of which are only configurable to go to an S3 bucket at this time, not CloudWatch Logs. You don’t have either of those enabled in your Terraform code.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search