Lambda Function#

A flexible Terraform module for creating AWS Lambda functions with a kwargs-like approach for additional attributes, optional execution role creation, ARM64 architecture for optimal price-performance.

Features#

  • kwargs-like flexibility: Use additional_attributes to pass any Lambda function attribute

  • Type-safe core attributes: Essential attributes are explicitly defined with proper types

  • Dynamic configuration: Support for VPC, file system, tracing, and more

  • Backward compatible: Works with minimal configuration or complex setups

  • Comprehensive outputs: Expose all useful Lambda function attributes

  • Optional execution role: Automatically create and configure Lambda execution role with security best practices

  • Confused deputy protection: IAM trust policies include account conditions to prevent security attacks

  • Configuration validation: Ensures required parameters are provided at plan time, not apply time

  • Custom policies: Attach custom execution policies to the Lambda role

  • ARM64 by default: Optimized for price-performance with Graviton2/Graviton3 processors

  • Robust hash-based change detection: Use an explicit hash for reliable, efficient deployments

Modern Usage Example: Hash-Based Lambda Deployment#

This pattern ensures your Lambda ZIP is only rebuilt and redeployed when the source code changes, using a dedicated folder hash module and builder module.

module "lambda_one" {
  source = "./modules/lambda/function"

  function_name = "simple-lambda-one"
  description = "Simple Lambda function with minimal configuration..."
  create_execution_role = true
  zip_path = "path/to/zip_file.zip"
  source_code_hash = "hash_code"

  depends_on = [
    module.folder_hash_lambda_one,
    module.my_lambda_one_builder
  ]
}

Why this pattern?#

  • Efficient builds: The ZIP is only rebuilt when the source code changes.

  • Reliable deployments: Lambda is only updated when the code or configuration changes.

  • Explicit and reproducible: The hash is calculated in a dedicated module, making dependencies clear.

Note

The source_code_hash variable must be provided by the user. This module does not read .base64sha256 files automatically; you should pass the hash output from your builder or hash module.

ARM64 Architecture Benefits#

This module defaults to ARM64 architecture for the following benefits:

  • đź’° 20-34% cost savings compared to x86_64

  • ⚡ Better performance for most workloads

  • 🔋 Lower power consumption and carbon footprint

  • 🔄 Faster cold starts in many scenarios

  • 📦 Smaller deployment packages for some runtimes

You can still use x86_64 if needed for compatibility with specific libraries or requirements.

Integration with Lambda Builder Module#

locals {
  root_path = abspath(path.module)
  build_path = "${local.root_path}/.build"
  lambdas_path = "${local.root_path}/project/lambdas"
}

# 1. Calculate a hash of all source files in the Lambda directory
module "folder_hash_lambda_one" {
  source = "./modules/utils/folder_hash"
  source_dir = "${local.lambdas_path}/lambda_one"
}

# 2. Build the Lambda ZIP only when the source hash changes
module "my_lambda_one_builder" {
  source = "./modules/lambda/builder"

  project_dir = local.root_path
  source_dir = "${local.lambdas_path}/lambda_one"
  build_dir = "${local.build_path}/project/lambdas/lambda_one"
  output_zip = "${local.build_path}/project/lambdas/lambda_one/my_lambda.zip"
  source_hash = module.folder_hash_lambda_one.folder_hash

  depends_on = [
    module.folder_hash_lambda_one
  ]
}

# 3. Deploy the Lambda function, using the ZIP and hash
module "lambda_one" {
  source = "./modules/lambda/function"

  function_name = "simple-lambda-one"
  description = "Simple Lambda function with minimal configuration..."
  create_execution_role = true
  zip_path = module.my_lambda_one_builder.zip_file
  source_code_hash = module.folder_hash_lambda_one.folder_hash

  depends_on = [
    module.folder_hash_lambda_one,
    module.my_lambda_one_builder
  ]
}

Usage Examples#

Basic Usage with External Role#

module "simple_lambda" {
  source = "./modules/lambda/function"

  function_name = "my-function"
  description = "A simple Lambda function (ARM64 by default)"
  role_arn = aws_iam_role.lambda_role.arn
  zip_path = "path/to/function.zip"
  source_code_hash = filebase64sha256("path/to/function.zip")
}

# Access outputs
output "lambda_arn" {
  value = module.simple_lambda.arn
}

output "lambda_architecture" {
  value = module.simple_lambda.architectures  # ["arm64"]
}

Basic Usage with Auto-Created Execution Role#

module "simple_lambda_with_role" {
  source = "./modules/lambda/function"

  function_name = "my-function"
  description = "A simple Lambda function with auto-created role"
  create_execution_role = true
  zip_path = "path/to/function.zip"
  source_code_hash = filebase64sha256("path/to/function.zip")
}

# Access execution role outputs
output "execution_role_arn" {
  value = module.simple_lambda_with_role.execution_role_arn
}

Using x86_64 Architecture#

module "x86_lambda" {
  source = "./modules/lambda/function"

  function_name = "x86-function"
  description = "Lambda function using x86_64 architecture"
  create_execution_role = true
  architectures = ["x86_64"]  # Override default ARM64

  zip_path = "path/to/function.zip"
  source_code_hash = filebase64sha256("path/to/function.zip")
}

Advanced Usage with VPC and Custom Policies#

module "vpc_lambda" {
  source = "./modules/lambda/function"

  function_name = "vpc-lambda"
  description = "Lambda function in VPC with custom policies"
  create_execution_role = true

  execution_policies = {
    "s3-access" = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Effect = "Allow"
          Action = ["s3:GetObject", "s3:PutObject"]
          Resource = "arn:aws:s3:::my-bucket/*"
        }
      ]
    })
    "dynamodb-access" = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Effect = "Allow"
          Action = ["dynamodb:GetItem", "dynamodb:PutItem"]
          Resource = "arn:aws:dynamodb:us-east-1:123456789012:table/my-table"
        }
      ]
    })
  }

  zip_path = "path/to/function.zip"
  source_code_hash = filebase64sha256("path/to/function.zip")

  additional_attributes = {
    vpc_config = {
      subnet_ids = ["subnet-12345678", "subnet-87654321"]
      security_group_ids = ["sg-12345678"]
    }
    tracing_config = {
      mode = "Active"
    }
    reserved_concurrent_executions = 10
  }
}

Available additional_attributes#

The additional_attributes variable accepts any valid AWS Lambda function attribute.

Block Attributes#

  • vpc_config - VPC configuration

  • file_system_config - EFS mount configuration

  • tracing_config - X-Ray tracing configuration

  • image_config - Container image configuration

  • dead_letter_config - Dead letter queue configuration

Simple Attributes#

  • reserved_concurrent_executions - Concurrency limit

  • publish - Publish version

  • layers - Lambda layers

  • kms_key_arn - KMS encryption key

  • package_type - Package type (Zip/Image)

  • image_uri - Container image URI

  • code_signing_config_arn - Code signing configuration

Tags#

  • tags - Additional tags (merged with base tags)

Inputs#

Name

Description

Type

Default

Required

function_name

Name of the Lambda function

string

n/a

yes

description

Description of the Lambda function

string

n/a

yes

role_arn

IAM role ARN for the Lambda function (required if create_execution_role = false)

string

null

no

create_execution_role

Whether to create a Lambda execution role automatically

bool

false

no

execution_policies

Map of custom execution policies to attach to the Lambda execution role

map(string)

{}

no

handler

Handler function

string

“handler.handler”

no

runtime

Lambda runtime

string

“python3.12”

no

architectures

Instruction set architecture for the Lambda function

list(string)

[“arm64”]

no

zip_path

Path to the Lambda deployment package

string

n/a

yes

source_code_hash

Code hash of the deployment package

string

n/a

yes

timeout

Execution timeout in seconds

number

3

no

memory_size

Amount of memory in MB

number

128

no

environment_variables

Environment variables

map(string)

{}

no

tags

Tags to apply

map(string)

{}

no

additional_attributes

Additional attributes (kwargs-like)

map(any)

{}

no

Outputs#

Name

Description

name

The Lambda Function name

arn

The Lambda Function ARN

invoke_arn

The Lambda Function invoke ARN (used for API Gateway integration)

qualified_arn

The Lambda Function qualified ARN (includes version)

version

The Lambda Function version

last_modified

The date the Lambda Function was last modified

source_code_size

The size of the Lambda Function deployment package in bytes

runtime

The Lambda Function runtime

handler

The Lambda Function handler

architectures

The Lambda Function architectures (ARM64 for better price-performance)

memory_size

The Lambda Function memory size in MB

timeout

The Lambda Function timeout in seconds

environment_variables

The Lambda Function environment variables

vpc_config

The Lambda Function VPC configuration (if configured)

layers

The Lambda Function layers (if configured)

tags

The Lambda Function tags

function_url

The Lambda Function URL (if configured via additional_attributes)

reserved_concurrent_executions

The Lambda Function reserved concurrency limit (if configured)

kms_key_arn

The Lambda Function KMS key ARN (if configured)

signing_job_arn

The Lambda Function signing job ARN (if code signing is configured)

signing_profile_version_arn

The Lambda Function signing profile version ARN (if code signing is configured)

execution_role_arn

The Lambda execution role ARN (only when create_execution_role = true)

execution_role_name

The Lambda execution role name (only when create_execution_role = true)

execution_role_id

The Lambda execution role ID (only when create_execution_role = true)

Execution Role Features#

When create_execution_role = true, the module automatically:

  1. Creates IAM Role: Lambda execution role with secure assume role policy

    • Includes aws:SourceAccount condition to prevent confused deputy attacks

    • Follows AWS security best practices for service-to-service access

  2. Attaches Basic Policy: AWSLambdaBasicExecutionRole for CloudWatch logs

  3. Attaches VPC Policy: AWSLambdaVPCAccessExecutionRole if VPC is configured

  4. Attaches Custom Policies: Any policies provided in execution_policies

  5. Applies Tags: Uses the same tags as the Lambda function

  6. Validates Configuration: Ensures role_arn is provided when create_execution_role = false

Automatic Policy Attachments#

  • Basic Execution: Always attached for CloudWatch logs

  • VPC Access: Automatically attached when VPC configuration is present

  • Custom Policies: Attached based on execution_policies variable

ARM64 Architecture Considerations#

Benefits#

  • Cost Savings: 20-34% lower costs compared to x86_64

  • Performance: Better performance for most workloads

  • Efficiency: Lower power consumption and carbon footprint

  • Cold Starts: Faster cold starts in many scenarios

Compatibility#

  • Most AWS SDKs: Fully compatible with ARM64

  • Popular Runtimes: Python, Node.js, Java, .NET, Go, Ruby

  • Layers: Many public layers available for ARM64

  • Custom Binaries: May need ARM64 versions of native libraries

When to Use x86_64#

  • Native Dependencies: Libraries that don’t have ARM64 versions

  • Legacy Code: Applications with x86_64-specific optimizations

  • Third-party Tools: Tools that don’t support ARM64

  • Testing: When testing compatibility with existing x86_64 deployments

Best Practices#

  1. Use explicit attributes for commonly used configurations

  2. Use additional_attributes for advanced or rarely used features

  3. Document complex configurations in your module usage

  4. Validate additional_attributes in your calling code

  5. Use consistent naming for your additional attributes

  6. Leverage outputs for integration with other AWS services

  7. Use create_execution_role for simple Lambda functions

  8. Use external roles for complex IAM requirements

  9. Prefer ARM64 for cost and performance benefits

  10. Test compatibility when migrating from x86_64 to ARM64