Skip to content

Commit 5c77b82

Browse files
committed
feat: add AWS Lambda warmer Terraform module with configuration and documentation
1 parent eac1df6 commit 5c77b82

File tree

6 files changed

+482
-1
lines changed

6 files changed

+482
-1
lines changed

README.md

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,122 @@
1-
# tag-based-lambda-warmer
1+
# Terraform Module tag-based-lambda-warmer
2+
3+
A Terraform module for deploying an automated Lambda function warming solution. This module helps prevent cold starts in your Lambda functions by periodically invoking them based on tags.
4+
5+
## Overview
6+
7+
This module creates a Lambda function that automatically warms up other Lambda functions based on specified tags. It uses EventBridge Scheduler to trigger the warming process at regular intervals.
8+
9+
## Features
10+
11+
- Tag-based function selection for warming
12+
- Configurable warming schedule
13+
- Separate IAM roles for Lambda and EventBridge Scheduler
14+
- CloudWatch logging for monitoring
15+
- Customizable tag key/value pairs for targeting functions
16+
17+
## Usage
18+
19+
```hcl
20+
module "lambda_warmer" {
21+
source = "../modules/lambda_warmer"
22+
23+
aws_region = "ap-northeast-1"
24+
environment = "production"
25+
26+
# Optional: Custom configuration
27+
prewarm_tag_key = "Project"
28+
prewarm_tag_value = "MyProject"
29+
lambda_schedule_expression = "rate(5 minutes)"
30+
scheduler_max_retry_attempts = 0
31+
}
32+
```
33+
34+
### Tagging Lambda Functions for Warming
35+
36+
To mark a Lambda function for warming, add the appropriate tags:
37+
38+
```hcl
39+
resource "aws_lambda_function" "example" {
40+
# ... other configuration ...
41+
42+
tags = {
43+
Prewarm = "true" # Default tags
44+
# Or use custom tags as configured in the module
45+
Project = "MyProject"
46+
}
47+
}
48+
```
49+
50+
## Requirements
51+
52+
| Name | Version |
53+
|------|---------|
54+
| Terraform | >= 1.8.0 |
55+
| AWS Provider | >= 5.54 |
56+
| Python | >= 3.11 (for Lambda runtime) |
57+
58+
## Variables
59+
60+
### Required Variables
61+
62+
| Name | Description | Type |
63+
|------|-------------|------|
64+
| aws_region | AWS region where the Lambda warmer will be deployed | string |
65+
| environment | Environment name (e.g., prod, dev, staging) | string |
66+
67+
### Optional Variables
68+
69+
| Name | Description | Type | Default |
70+
|------|-------------|------|---------|
71+
| lambda_schedule_expression | Schedule expression for the warmer | string | "rate(5 minutes)" |
72+
| scheduler_max_retry_attempts | Max retry attempts for scheduler | number | 0 |
73+
| prewarm_tag_key | Tag key for identifying functions to warm | string | "Prewarm" |
74+
| prewarm_tag_value | Expected value of the warming tag | string | "true" |
75+
76+
## Outputs
77+
78+
| Name | Description |
79+
|------|-------------|
80+
| scheduler_group_arn | ARN of the EventBridge Scheduler Group |
81+
| scheduler_arn | ARN of the EventBridge Scheduler |
82+
| lambda_function_name | Name of the Lambda warmer function |
83+
| lambda_function_arn | ARN of the Lambda warmer function |
84+
| lambda_role_name | Name of the Lambda IAM Role |
85+
| lambda_role_arn | ARN of the Lambda IAM Role |
86+
87+
## Directory Structure
88+
89+
```plaintext
90+
.
91+
├── README.md
92+
├── main.tf # Main Terraform configuration
93+
├── variables.tf # Input variables
94+
├── outputs.tf # Output definitions
95+
└── lambda_warmer/ # Lambda function code
96+
└── lambda_function.py # Python implementation
97+
```
98+
99+
## IAM Roles and Permissions
100+
101+
The module creates two separate IAM roles:
102+
103+
1. Lambda Role:
104+
- CloudWatch Logs access
105+
- Lambda function invocation
106+
107+
2. EventBridge Scheduler Role:
108+
- Permission to invoke the warmer Lambda function
109+
110+
## Schedule Expression Examples
111+
112+
- Every 5 minutes: `rate(5 minutes)`
113+
- Every hour: `rate(1 hour)`
114+
- Daily at 2 AM UTC: `cron(0 2 * * ? *)`
115+
116+
## Monitoring and Logs
117+
118+
The Lambda warmer function logs its activities to CloudWatch Logs. You can monitor:
119+
120+
- Functions being warmed
121+
- Warming success/failure
122+
- Number of functions processed

lambda_warmer/lambda_function.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import json
2+
import logging
3+
import os
4+
5+
import boto3
6+
7+
# Initialize AWS Lambda client and logger
8+
lambda_client = boto3.client("lambda")
9+
logger = logging.getLogger()
10+
logger.setLevel(logging.INFO)
11+
12+
# Get tag key and value from environment variables
13+
PREWARM_TAG_KEY = os.environ.get("PREWARM_TAG_KEY", "Prewarm")
14+
PREWARM_TAG_VALUE = os.environ.get("PREWARM_TAG_VALUE", "true")
15+
16+
17+
def get_prewarm_functions():
18+
"""
19+
Fetch all Lambda functions and filter those with the specified tag key and value
20+
from environment variables.
21+
22+
Environment Variables:
23+
PREWARM_TAG_KEY: The tag key to check (e.g., "Project")
24+
PREWARM_TAG_VALUE: The expected value for the tag (e.g., "DemoProject")
25+
26+
Returns:
27+
list: A list of function names that require prewarming.
28+
"""
29+
try:
30+
31+
logger.info(
32+
"Searching for functions with %s=%s", PREWARM_TAG_KEY, PREWARM_TAG_VALUE
33+
)
34+
35+
functions = []
36+
next_marker = None # Marker for pagination
37+
38+
# Iterate through all pages of Lambda functions
39+
while True:
40+
if next_marker:
41+
response = lambda_client.list_functions(Marker=next_marker)
42+
else:
43+
response = lambda_client.list_functions()
44+
45+
# Check each function's tags to identify prewarm candidates
46+
for func in response["Functions"]:
47+
function_name = func["FunctionName"]
48+
function_arn = func["FunctionArn"]
49+
50+
# Retrieve tags for the current function
51+
tags = lambda_client.list_tags(Resource=function_arn)
52+
53+
# Check if the function has the specified tag and value
54+
if tags.get("Tags", {}).get(PREWARM_TAG_KEY) == PREWARM_TAG_VALUE:
55+
functions.append(function_name)
56+
logger.info(
57+
"Function %s matched criteria: %s=%s",
58+
function_name,
59+
PREWARM_TAG_KEY,
60+
PREWARM_TAG_VALUE,
61+
)
62+
63+
# Check if there are more pages
64+
next_marker = response.get("NextMarker")
65+
if not next_marker:
66+
break
67+
68+
logger.info("Found %d functions to prewarm: %s", len(functions), functions)
69+
return functions
70+
71+
except Exception as e:
72+
logger.error("Error while fetching functions: %s", e)
73+
raise
74+
75+
76+
def invoke_lambda(function_name):
77+
"""
78+
Trigger a specified Lambda function to keep it warm.
79+
80+
Args:
81+
function_name (str): The name of the Lambda function to prewarm.
82+
"""
83+
try:
84+
# Use Event invocation type to avoid blocking
85+
lambda_client.invoke(
86+
FunctionName=function_name,
87+
InvocationType="Event", # Asynchronous invocation to reduce latency
88+
Payload=json.dumps({"action": "PREWARM"}), # Send a custom prewarm signal
89+
)
90+
logger.info("Successfully prewarmed %s", function_name)
91+
except Exception as e:
92+
logger.error("Failed to prewarm %s: %s", function_name, e)
93+
94+
95+
def lambda_handler(event, context):
96+
"""
97+
Entry point for the Lambda function. Retrieves the list of Lambda functions to prewarm
98+
and invokes them asynchronously.
99+
100+
Args:
101+
event (dict): AWS event data (not used in this implementation).
102+
context (object): AWS Lambda context object (not used in this implementation).
103+
104+
Returns:
105+
dict: A status object with the results of the prewarm operation.
106+
"""
107+
logger.info("Starting prewarmer...")
108+
try:
109+
# Step 1: Get the list of functions to prewarm
110+
prewarm_functions = get_prewarm_functions()
111+
112+
# Step 2: Invoke each function asynchronously
113+
for function_name in prewarm_functions:
114+
invoke_lambda(function_name)
115+
116+
logger.info("Prewarm process completed.")
117+
return {"status": "SUCCESS", "prewarmed_functions": prewarm_functions}
118+
except Exception as e:
119+
logger.error("Prewarmer encountered an error: %s", e)
120+
return {"status": "ERROR", "message": str(e)}

0 commit comments

Comments
 (0)