A comprehensive Terraform module for deploying serverless applications on AWS, combining Lambda functions with API Gateway to create production-ready RESTful APIs.
This module provides a complete serverless deployment solution:
- ⚡ AWS Lambda: Configurable functions with multiple deployment options
- 🌐 API Gateway: REST API with Three-Level Resource Creation and Routing
- 🔄 Flexible Deployment: Support for ZIP packages, S3 storage, and container images
- 🔒 Security: Code signing, IAM roles, and CloudWatch logging
- 🏷️ Best Practices: Automatic runtime selection and resource tagging
- 📊 Monitoring: CloudWatch logs with configurable retention
Perfect for microservices, API backends, event-driven architectures, and serverless web applications requiring REST endpoints.
| Name | Version |
|---|---|
| Terraform | >= 1.9.5 |
| AWS Provider | >= 6.11.0 |
module "python_api" {
source = "yaalalabs/ak-serverless/aws"
region = "us-west-2"
product_alias = "myapp"
env_alias = "prod"
product_display_name = "My Application API"
module_name = "api"
function_name = "handler"
function_description = "Main API handler"
handler_path = "app.lambda_handler"
module_type = "python"
package_type = "LocalZip"
package_path = "${path.module}/dist/function.zip"
timeout = 30
memory_size = 512
environment_variables = {
ENVIRONMENT = "production"
LOG_LEVEL = "info"
}
# API Gateway
api_version = "v1"
api_base_path = "api"
agent_endpoint = "chat"
gateway_endpoints = [
{
path = "app/test",
method = "GET",
},
{
path = "data",
method = "POST",
}
]
tags = {
Environment = "production"
Service = "api"
}
}
output "api_url" {
value = module.python_api.agent_invoke_url
}# Create layer for dependencies
resource "aws_lambda_layer_version" "dependencies" {
layer_name = "myapp-dependencies"
filename = "${path.module}/dist/layer.zip"
compatible_runtimes = ["nodejs22.x"]
}
# Deploy Node.js function with layer
module "nodejs_api" {
source = "yaalalabs/ak-serverless/aws"
region = "us-west-2"
product_alias = "myapp"
env_alias = "prod"
product_display_name = "Node.js API"
module_name = "chat"
function_name = "handler"
function_description = "Chat API endpoint"
handler_path = "index.handler"
module_type = "nodejs"
package_type = "LocalZip"
package_path = "${path.module}/dist/function.zip"
layers = [aws_lambda_layer_version.dependencies.arn]
timeout = 60
memory_size = 1024
environment_variables = {
NODE_ENV = "production"
}
api_version = "v2"
agent_endpoint = "chat"
}# Use ECR module to build image
module "container_image" {
source = "yaalalabs/ak-common/aws//modules/ecr"
region = "us-west-2"
product_alias = "myapp"
env_alias = "prod"
module_name = "api"
source_path = "${path.module}/src"
}
# Deploy Lambda with container image
module "container_api" {
source = "yaalalabs/ak-serverless/aws"
region = "us-west-2"
product_alias = "myapp"
env_alias = "prod"
product_display_name = "Container API"
module_name = "api"
function_name = "processor"
function_description = "Containerized API handler"
handler_path = "app.handler" # Used for metadata only
module_type = "python"
package_type = "Image"
image_uri = module.container_image.docker_image_uri
timeout = 120
memory_size = 2048
environment_variables = {
WORKERS = "4"
}
api_version = "v1"
agent_endpoint = "process"
}# Upload package to S3
module "lambda_package" {
source = "yaalalabs/ak-common/aws//modules/lambda-package"
region = "us-west-2"
product_alias = "myapp"
env_alias = "prod"
module_name = "api"
package_dir_path = "${path.module}/dist/function.zip"
s3_bucket = module.storage.source_storage_s3_bucket
}
# Deploy with code signing
module "secure_api" {
source = "yaalalabs/ak-serverless/aws"
region = "us-west-2"
product_alias = "myapp"
env_alias = "prod"
product_display_name = "Secure API"
module_name = "api"
function_name = "handler"
function_description = "Production API with code signing"
handler_path = "app.handler"
module_type = "python"
package_type = "S3Zip"
package_path = "s3://${module.lambda_package.s3_bucket}/${module.lambda_package.s3_key}"
is_production = true # Enables code signing
timeout = 30
memory_size = 256
api_version = "v1"
agent_endpoint = "api"
}module "serverless_api_dynamodb" {
source = "yaalalabs/ak-serverless/aws"
region = "us-west-2"
product_alias = "myapp"
env_alias = "prod"
product_display_name = "Serverless API with DynamoDB"
module_name = "chat"
function_name = "handler"
function_description = "Chat API with DynamoDB session storage"
handler_path = "app.lambda_handler"
module_type = "python"
package_type = "LocalZip"
package_path = "${path.module}/dist/function.zip"
# Enable DynamoDB for session storage
create_dynamodb_memory_table = true
timeout = 30
memory_size = 512
environment_variables = {
ENVIRONMENT = "production"
# DynamoDB table name automatically injected as AK_SESSION__DYNAMODB__TABLE_NAME
}
api_version = "v1"
agent_endpoint = "chat"
}| Name | Description | Type | Default | Required |
|---|---|---|---|---|
region |
AWS region for deployment | string |
n/a | yes |
product_alias |
Short identifier for the product (e.g., "myapp") | string |
n/a | yes |
env_alias |
Environment identifier (e.g., "dev", "staging", "prod") | string |
n/a | yes |
product_display_name |
Human-readable product name for tagging | string |
"An Agent Kernel deployment" |
no |
module_type |
Runtime type: python or nodejs |
string |
"python" |
no |
module_name |
Module name for resource identification | string |
n/a | yes |
is_production |
Enable production features (code signing) | bool |
false |
no |
package_path |
Path to Lambda deployment package or S3 URI | string |
n/a | yes |
event_source_mapping |
Event source mapping configuration for triggers | any |
[] |
no |
environment_variables |
Environment variables for Lambda function | map(string) |
{} |
no |
timeout |
Lambda function timeout in seconds (max 900) | number |
30 |
no |
memory_size |
Lambda function memory size in MB (128-10240) | number |
128 |
no |
function_name |
Lambda function name suffix | string |
n/a | yes |
function_description |
Lambda function description | string |
n/a | yes |
handler_path |
Handler path (e.g., index.handler or app.main) |
string |
n/a | yes |
image_uri |
Container image URI (required for Image package type) | string |
null |
no |
package_type |
Deployment type: LocalZip, S3Zip, or Image |
string |
"LocalZip" |
no |
layers |
List of Lambda layer ARNs to attach | list(string) |
[] |
no |
api_version |
API version for endpoint path (e.g., v1, v2) |
string |
"v1" |
no |
agent_endpoint |
API endpoint name (e.g., chat, process) |
string |
"chat" |
no |
gateway_endpoints |
List of REST API Gateway endpoints to expose (e.g., app/test/func, app/check) limitation: only three-level resource creation |
list(object) |
[] |
no |
create_dynamodb_memory_table |
Enable DynamoDB table for session storage | bool |
false |
no |
tags |
Additional tags for resources | map(string) |
{} |
no |
| Name | Description | Example |
|---|---|---|
lambda_function_arn |
ARN of the Lambda function | arn:aws:lambda:us-west-2:123456789012:function:myapp-prod-api-handler |
lambda_function_name |
Name of the Lambda function | myapp-prod-api-handler |
lambda_function_invoke_arn |
Invoke ARN for API Gateway integration | arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/... |
agent_invoke_url |
Full HTTPS URL to invoke the API endpoint | https://abc123.execute-api.us-west-2.amazonaws.com/agents/api/v1/chat |
api_gateway_id |
API Gateway REST API ID | abc123defg |
api_gateway_stage_name |
API Gateway stage name | agents |
dynamodb_memory_table_arn |
DynamoDB table ARN (if enabled) | arn:aws:dynamodb:us-west-2:123456789012:table/myapp-prod-api-session_store |
dynamodb_memory_table_name |
DynamoDB table name (if enabled) | myapp-prod-api-session_store |
Multiple Deployment Methods:
- LocalZip: Deploy from local ZIP file (< 50 MB)
- S3Zip: Deploy from S3 bucket with optional code signing
- Image: Deploy from ECR container image (up to 10 GB)
Automatic Runtime Selection:
module_type = "python"→ Python 3.12 runtimemodule_type = "nodejs"→ Node.js 22.x runtime
Resource Optimization:
- Memory: 128 MB to 10,240 MB (in 1 MB increments)
- Timeout: 1 to 900 seconds (15 minutes)
- CPU scales automatically with memory
Structured Endpoint Path:
https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/api/{version}/{endpoint}
Example: /api/v1/chat
Features:
- REST API Gateway with Lambda proxy integration
- Stage name:
agents(production-ready) - CORS support configurable
- Custom domain support compatible
- IAM Roles: Automatic Lambda execution role creation
- CloudWatch Logs: 90-day retention by default
- Code Signing: Optional for production deployments
- VPC Support: Compatible with VPC-based Lambda
- Encryption: KMS encryption support for environment variables
Resources follow consistent naming:
- Lambda Function:
{product_alias}-{env_alias}-{module_name}-{function_name} - API Gateway:
{product_alias}-{env_alias}-{module_name}-api - CloudWatch Logs:
/aws/lambda/{function_name}
# Light API calls
memory_size = 256
timeout = 10
# Data processing
memory_size = 1024
timeout = 60
# Heavy compute
memory_size = 3008
timeout = 300# Separate dependencies into layers
# Benefits: Faster deployments, shared libraries, smaller function packages
module "api_function" {
# ... other config
package_path = "function-only.zip" # Small, code only
layers = [
aws_lambda_layer_version.dependencies.arn,
aws_lambda_layer_version.utilities.arn
]
}locals {
config = {
dev = {
memory_size = 256
timeout = 30
is_production = false
}
prod = {
memory_size = 1024
timeout = 60
is_production = true
}
}
env_config = local.config[var.env_alias]
}
module "api" {
# ... other config
memory_size = local.env_config.memory_size
timeout = local.env_config.timeout
is_production = local.env_config.is_production
}✅ Enable code signing (is_production = true)
✅ Use appropriate memory and timeout
✅ Configure CloudWatch alarms
✅ Enable X-Ray tracing
✅ Set up proper IAM policies
✅ Use environment variables for config
✅ Implement error handling
✅ Test with realistic load
| Memory (MB) | vCPU | Cost/ms (US East) | Use Case |
|---|---|---|---|
| 128 | 0.08 | $0.0000000021 | Simple APIs |
| 512 | 0.33 | $0.0000000083 | Standard APIs |
| 1024 | 0.58 | $0.0000000167 | Data processing |
| 3008 | 1.75 | $0.0000000500 | CPU-intensive |
| 10240 | 6.00 | $0.0000001700 | Heavy compute |
Tip: More memory = more CPU = faster execution = potentially lower cost!
- Minimize Package Size: Use layers, exclude dev dependencies
- Provisioned Concurrency: For latency-sensitive APIs (additional cost)
- Keep Warm: CloudWatch Events ping for critical functions
- Language Choice: Node.js/Python typically faster cold starts than Java/.NET
# Chat endpoint
module "chat_api" {
source = "yaalalabs/ak-serverless/aws"
module_name = "chat"
function_name = "handler"
agent_endpoint = "chat"
api_version = "v1"
# ... other config
}
# Process endpoint
module "process_api" {
source = "yaalalabs/ak-serverless/aws"
module_name = "process"
function_name = "handler"
agent_endpoint = "process"
api_version = "v1"
# ... other config
}
# Results in:
# /api/v1/chat
# /api/v1/processmodule "event_processor" {
source = "yaalalabs/ak-serverless/aws"
# ... basic config
event_source_mapping = [
{
event_source_arn = aws_sqs_queue.tasks.arn
batch_size = 10
}
]
}# V1 API
module "api_v1" {
source = "yaalalabs/ak-serverless/aws"
module_name = "api"
api_version = "v1"
# ... config
}
# V2 API (backward compatible)
module "api_v2" {
source = "yaalalabs/ak-serverless/aws"
module_name = "api"
api_version = "v2"
# ... config with new features
}Issue: Package too large
Error: Unzipped size must be smaller than...
Solutions:
- Use layers for dependencies
- Remove unnecessary files (tests, docs)
- Use container images for > 250 MB
- Optimize dependencies (use
--productionflag)
Issue: Lambda timeout or error
{"message": "Internal server error"}
Solutions:
- Check CloudWatch Logs:
aws logs tail /aws/lambda/function-name --follow
- Increase timeout if processing takes longer
- Check Lambda function returns proper response format
- Verify IAM permissions
Issue: First request slow
Solutions:
# Option 1: Provisioned Concurrency
resource "aws_lambda_provisioned_concurrency_config" "api" {
function_name = module.api.lambda_function_name
provisioned_concurrent_executions = 2
qualifier = aws_lambda_alias.live.name
}
# Option 2: Keep-warm ping
resource "aws_cloudwatch_event_rule" "keep_warm" {
name = "keep-lambda-warm"
schedule_expression = "rate(5 minutes)"
}
resource "aws_cloudwatch_event_target" "lambda" {
rule = aws_cloudwatch_event_rule.keep_warm.name
target_id = "KeepWarm"
arn = module.api.lambda_function_arn
input = jsonencode({ "warmer": true })
}Issue: Function can't read environment variables
Solution: Verify configuration:
module "api" {
# ... other config
environment_variables = {
DB_HOST = "database.example.com"
API_KEY = var.api_key # From secrets manager
}
}Access in code:
import os
db_host = os.environ.get('DB_HOST')# Example: 1M requests/month, 512MB, 200ms avg
requests = 1_000_000
memory_mb = 512
duration_ms = 200
# Free tier: 1M requests, 400,000 GB-seconds
billable_requests = max(0, requests - 1_000_000)
gb_seconds = (requests * memory_mb / 1024 * duration_ms / 1000)
billable_gb_seconds = max(0, gb_seconds - 400_000)
cost_requests = billable_requests * 0.0000002 # $0.20 per 1M
cost_compute = billable_gb_seconds * 0.0000166667 # $0.0000166667 per GB-second
total = cost_requests + cost_compute
print(f"Estimated monthly cost: ${total:.2f}")- Right-size memory: Test different configurations
- Reduce timeout: Don't set higher than needed
- Optimize code: Faster execution = lower cost
- Use layers: Reduce deployment package processing
- Cache responses: API Gateway caching available
- Lambda Package Module - For managing Lambda packages in S3
- ECR Module - For container-based deployments
- VPC Module - For VPC-enabled Lambda functions
- S3 Module - For Lambda package storage
Note: This module automatically creates IAM roles, CloudWatch log groups, and API Gateway resources. Ensure your AWS credentials have sufficient permissions to create these resources.