Deploying AWS Lambda Functions with Terraform

A comprehensive guide to deploying serverless functions on AWS Lambda using Terraform Infrastructure as Code

Deploying AWS Lambda Functions with Terraform

AWS Lambda lets you run code without provisioning servers. This guide shows how to deploy Lambda functions using Terraform for infrastructure as code.

Video Tutorial

Learn more about deploying AWS Lambda with Terraform in this comprehensive video tutorial:

View Source Code

Related Guides:

Prerequisites

  • AWS CLI configured
  • Terraform installed
  • Basic understanding of serverless architecture

Project Structure

aws-lambda-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars

Basic Lambda Configuration

provider "aws" {
  region = var.aws_region
}

# IAM role for Lambda
resource "aws_iam_role" "lambda" {
  name = "${var.project_name}-lambda-role"

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

# CloudWatch Logs policy
resource "aws_iam_role_policy_attachment" "lambda_logs" {
  role       = aws_iam_role.lambda.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# Lambda function
resource "aws_lambda_function" "main" {
  filename         = "lambda_function.zip"
  function_name    = "${var.project_name}-function"
  role            = aws_iam_role.lambda.arn
  handler         = "index.handler"
  runtime         = "nodejs14.x"

  environment {
    variables = {
      ENVIRONMENT = var.environment
    }
  }

  tags = {
    Environment = var.environment
  }
}

# CloudWatch Log Group
resource "aws_cloudwatch_log_group" "lambda" {
  name              = "/aws/lambda/${aws_lambda_function.main.function_name}"
  retention_in_days = 14
}

Function Code (index.js)

exports.handler = async (event) => {
  console.log('Event:', JSON.stringify(event));
  
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Hello from Lambda!'
    })
  };
};

Advanced Features

1. VPC Configuration

resource "aws_lambda_function" "vpc" {
  filename         = "lambda_function.zip"
  function_name    = "${var.project_name}-vpc-function"
  role            = aws_iam_role.lambda.arn
  handler         = "index.handler"
  runtime         = "nodejs14.x"

  vpc_config {
    subnet_ids         = var.subnet_ids
    security_group_ids = [aws_security_group.lambda.id]
  }
}

resource "aws_security_group" "lambda" {
  name        = "${var.project_name}-lambda-sg"
  description = "Security group for Lambda function"
  vpc_id      = var.vpc_id

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

2. Lambda Layers

resource "aws_lambda_layer_version" "dependencies" {
  filename            = "layer.zip"
  layer_name          = "${var.project_name}-dependencies"
  compatible_runtimes = ["nodejs14.x"]
}

resource "aws_lambda_function" "with_layer" {
  filename         = "lambda_function.zip"
  function_name    = "${var.project_name}-layered-function"
  role            = aws_iam_role.lambda.arn
  handler         = "index.handler"
  runtime         = "nodejs14.x"
  layers          = [aws_lambda_layer_version.dependencies.arn]
}

3. Function URL

resource "aws_lambda_function_url" "url" {
  function_name      = aws_lambda_function.main.function_name
  authorization_type = "NONE"

  cors {
    allow_credentials = true
    allow_origins     = ["*"]
    allow_methods     = ["*"]
    allow_headers     = ["date", "keep-alive"]
    expose_headers    = ["keep-alive", "date"]
    max_age          = 86400
  }
}

Monitoring Configuration

# CloudWatch Alarm for Errors
resource "aws_cloudwatch_metric_alarm" "lambda_errors" {
  alarm_name          = "${var.project_name}-errors"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "1"
  metric_name         = "Errors"
  namespace           = "AWS/Lambda"
  period             = "300"
  statistic          = "Sum"
  threshold          = "0"
  alarm_description  = "This metric monitors lambda function errors"
  alarm_actions      = [var.sns_topic_arn]

  dimensions = {
    FunctionName = aws_lambda_function.main.function_name
  }
}

# CloudWatch Dashboard
resource "aws_cloudwatch_dashboard" "lambda" {
  dashboard_name = "${var.project_name}-lambda"

  dashboard_body = jsonencode({
    widgets = [
      {
        type   = "metric"
        x      = 0
        y      = 0
        width  = 12
        height = 6

        properties = {
          metrics = [
            ["AWS/Lambda", "Invocations", "FunctionName", aws_lambda_function.main.function_name],
            ["AWS/Lambda", "Errors", "FunctionName", aws_lambda_function.main.function_name],
            ["AWS/Lambda", "Duration", "FunctionName", aws_lambda_function.main.function_name]
          ]
          period = 300
          stat   = "Sum"
          region = var.aws_region
          title  = "Lambda Metrics"
        }
      }
    ]
  })
}

Best Practices

  1. Function Size

    • Keep functions small and focused
    • Use layers for dependencies
    • Optimize package size
  2. Error Handling

    • Implement proper error handling
    • Set up monitoring and alerts
    • Use structured logging
  3. Security

    • Use IAM roles with least privilege
    • Encrypt environment variables
    • Regular security audits
  4. Performance

    • Optimize memory allocation
    • Reuse connections
    • Implement proper timeouts

Common Use Cases

  1. Event Processing
resource "aws_lambda_function" "event_processor" {
  filename         = "event_processor.zip"
  function_name    = "${var.project_name}-event-processor"
  role            = aws_iam_role.lambda.arn
  handler         = "index.handler"
  runtime         = "nodejs14.x"
  timeout         = 30
  memory_size     = 256
}
  1. Scheduled Tasks
resource "aws_cloudwatch_event_rule" "schedule" {
  name                = "${var.project_name}-schedule"
  description         = "Schedule for Lambda Function"
  schedule_expression = "rate(1 hour)"
}

resource "aws_cloudwatch_event_target" "schedule_lambda" {
  rule      = aws_cloudwatch_event_rule.schedule.name
  target_id = "ProcessScheduledEvent"
  arn       = aws_lambda_function.main.arn
}

resource "aws_lambda_permission" "allow_eventbridge" {
  statement_id  = "AllowEventBridgeInvoke"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.main.function_name
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.schedule.arn
}

Variables

variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-west-2"
}

variable "project_name" {
  description = "Project name"
  type        = string
}

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "dev"
}

variable "vpc_id" {
  description = "VPC ID for Lambda function"
  type        = string
}

variable "subnet_ids" {
  description = "Subnet IDs for Lambda function"
  type        = list(string)
}

variable "sns_topic_arn" {
  description = "SNS Topic ARN for alarms"
  type        = string
}

Outputs

output "function_name" {
  description = "Lambda function name"
  value       = aws_lambda_function.main.function_name
}

output "function_arn" {
  description = "Lambda function ARN"
  value       = aws_lambda_function.main.arn
}

output "function_url" {
  description = "Lambda function URL"
  value       = aws_lambda_function_url.url.url
}

Conclusion

This setup provides a comprehensive foundation for deploying AWS Lambda functions using Terraform. Remember to:

  • Follow security best practices
  • Implement proper monitoring
  • Optimize function performance
  • Use proper error handling

For integrating this Lambda function with API Gateway, see our guide on AWS Lambda with API Gateway.