Table of Contents
- Course Overview
- What is AWS Lambda?
- Module 1: Deploying a Lambda Function
- Complete Source Code
- Anatomy of a Python Lambda Handler
- Event-Driven Architecture with Lambda
- Lambda Configuration
- AWS Lambda Pricing
- Best Practices
- Advanced Handler Examples
- Expected Outcomes
- Additional Resources
Course Overview
This hands-on Ignition course guides learners step by step through creating and deploying an end-to-end AWS Lambda function. The concrete use case is a canary — a small standalone program that monitors website availability and stores the results as a JSON file in Amazon S3.
Learning Objectives
- Select the right runtime for a Lambda function
- Configure an IAM execution role with minimal required permissions (principle of least privilege)
- Deploy and test a Lambda function from the AWS console
- Integrate Lambda with Amazon S3 for result storage
- Understand configuration parameters: timeout, memory, handler
- Master the structure of a Python handler and its two required arguments (
event,context)
What is AWS Lambda?
AWS Lambda is a serverless compute service that runs code in response to events and automatically manages the underlying compute resources. There is no need to provision or manage servers.
Serverless Model
| Characteristic | Lambda (Serverless) | EC2 (Classic IaaS) |
|---|---|---|
| Provisioning | Automatic | Manual |
| Scaling | Automatic (up to thousands of parallel instances) | Manual or Auto Scaling |
| Billing | Per invocation + execution duration (ms) | Per active instance hour |
| OS Maintenance | AWS | Customer |
| Max Execution Time | 15 minutes | Unlimited |
Lambda Invocation Lifecycle
sequenceDiagram
participant Source as Source (Trigger)
participant Lambda as AWS Lambda Service
participant Env as Execution Environment
participant Code as Code (handler.py)
participant S3 as Amazon S3
Source->>Lambda: Event (event object)
Lambda->>Env: Initialize environment (if cold start)
Env->>Code: Load code + dependencies
Code->>Code: Execute lambda_handler(event, context)
Code->>S3: s3.put_object(...)
Code->>Lambda: Return response
Lambda->>Source: JSON Response
Cold Start vs. Warm Start
On the first invocation (or after a long period of inactivity), Lambda must initialize a new execution environment — this is the cold start. Subsequent invocations reuse the existing environment — this is the warm start, much faster.
flowchart LR
A[Invocation] --> B{Environment\navailable?}
B -- No --> C[Cold Start\n~100ms-1s+]
B -- Yes --> D[Warm Start\n~1-10ms]
C --> E[Init global code\nboto3, connections...]
D --> F[Execute handler\ndirectly]
E --> F
F --> G[Return result]
style C fill:#f9a,stroke:#c00
style D fill:#9f9,stroke:#060
Strategy: initialize SDK clients (
boto3) and connections outside the handler to benefit from environment reuse during warm starts.
Module 1: Deploying a Lambda Function
This module covers the complete deployment of a Lambda function that acts as a website monitoring canary — a program that periodically checks URL availability and records the results in S3.
Demo: Selecting a Lambda Runtime
Accessing Lambda from the AWS Console
- In the AWS console, use the search bar to find Lambda
- Click Lambda in the results
- Click Create a function
Lambda Function Creation Options
| Option | Description | When to Use |
|---|---|---|
| Author from scratch | Start from an empty template | Custom task or new business logic |
| Use a blueprint | Pre-configured templates provided by AWS | Quick starting point in different languages |
| Container image | Pre-built Docker image in Amazon ECR | Complex dependencies or custom images |
For this course, we choose Author from scratch as the task is custom.
Naming and Runtime Selection
- Function name:
myLambdaCanary - Selected runtime: Python 3.14
Why Runtime Selection Matters
Selecting a runtime is a critical step because:
- Each runtime (Python, Node.js, Java, etc.) has its own standard library dependencies
- Supported language versions may have different behaviors
- The runtime influences execution time and performance (cold start)
- Python 3.14 includes
urllib— a standard utility for making HTTP requests without external dependencies
Runtimes Supported by AWS Lambda
Python : 3.14, 3.13, 3.12, 3.11
Node.js : 22.x, 20.x
Java : 21, 17, 11, 8
.NET : 9, 8
Ruby : 3.4, 3.3
Go : 1.x (provided.al2023)
Handler Configuration
The handler is the reference to the code entry point. It follows the format:
<file_name>.<function_name>
Example: if the file is named handler.py and the function is lambda_handler:
handler.lambda_handler
To modify this setting in the console:
- Go to the Code tab
- Scroll down to Runtime settings
- Click Edit → modify the Handler field
Demo: Scoping Lambda Function Execution Roles
Concept of Execution Role
Every Lambda function must have an IAM execution role that defines the AWS actions it can perform. By default, AWS creates a basic role that only allows writing logs to Amazon CloudWatch Logs.
flowchart TD
Lambda[Lambda Function\nmyLambdaCanary] --> Role[IAM Execution Role]
Role --> P1[AWSLambdaBasicExecutionRole\nCloudWatch Logs: CreateLogGroup\nCreateLogStream, PutLogEvents]
Role --> P2[Custom Policy\nS3: PutObject on target bucket]
style Lambda fill:#FF9900,color:#000
style Role fill:#dd3,color:#000
style P1 fill:#9cf,color:#000
style P2 fill:#9cf,color:#000
Why Create a Custom Role
The canary needs to:
- Write logs to CloudWatch (basic permission provided automatically)
- Store JSON files in Amazon S3 (
s3:PutObjectpermission to add)
Role Configuration Steps
- In the Lambda console, go to the Permissions section
- Click Create a new role
- Leave Create new policy selected
- Paste the custom IAM policy (see permissions.json)
- Click Create role
- Assign the new role to the Lambda function from the dropdown
Verifying Permissions
To confirm the role has the correct permissions:
- Click View role details in IAM
- Verify that both policies are attached:
AWSLambdaBasicExecutionRole(CloudWatch logs)- Custom policy (
s3:PutObject)
Principle of Least Privilege
✅ Grant: s3:PutObject only on the target bucket
❌ Avoid: s3:* or full access to all S3 buckets
The permissions.json policy uses "Resource": "*" for educational purposes. In production, restrict to the specific bucket ARN:
"Resource": "arn:aws:s3:::bucket-name/website-health-checks/*"
Demo: Creating Your First Lambda Function
Finalizing the Configuration
After selecting the runtime and configuring the role, adjust the execution parameters:
| Parameter | Default Value | Configured Value | Reason |
|---|---|---|---|
| Timeout | 3 seconds | 3 minutes | Some websites take more than 3s to respond |
| Memory | 128 MB | 256 MB | Network operations and JSON processing require more memory |
Steps to Modify the Configuration
- Go to the Configuration tab
- Click Edit in the General configuration section
- Change the Timeout to 3 minutes (3 min 0 sec)
- Change the Memory to 256 MB
- Click Save
Note: increasing memory also proportionally increases the allocated CPU power.
Deploying the Code
The code is pasted into the AWS console’s inline editor (or deployed via ZIP file for larger projects). The function is configured to:
- Iterate through a list of websites to monitor (
WEBSITES) - Perform an HTTP GET request on each with
urllib.request - Record the status, HTTP code, and response time
- Save the results as JSON in an S3 bucket
Testing and Validation
- Click Test in the Lambda console
- Create a test event (can be empty
{}— the function does not use the event) - Check the execution log: Execution result: succeeded
- Go to Amazon S3 → configured bucket →
website-health-checks/folder - Download and open the generated JSON file
Complete Source Code
handler.py — Main Lambda Function
import json
import time
import urllib.error
import urllib.request
from datetime import datetime
import boto3
# Initialize S3 client OUTSIDE the handler to benefit from warm starts
s3 = boto3.client("s3")
# List of sites to monitor (canary targets)
WEBSITES = [
"https://www.google.com",
"https://www.github.com",
]
# S3 configuration — externalize as an environment variable in production
S3_BUCKET = "my-monitoring-bucket"
S3_PREFIX = "website-health-checks"
def check_website(url, timeout=5):
"""
Performs an HTTP GET request on the provided URL.
Returns a dictionary with the status, HTTP code, and response time.
"""
start = time.time()
result = {
"url": url,
"available": False,
"status_code": None,
"response_time_ms": None,
"error": None
}
try:
req = urllib.request.Request(url, method="GET")
with urllib.request.urlopen(req, timeout=timeout) as response:
result["status_code"] = response.getcode()
result["available"] = 200 <= response.getcode() < 400
except urllib.error.HTTPError as e:
result["status_code"] = e.code
result["error"] = str(e)
except urllib.error.URLError as e:
result["error"] = str(e.reason)
except Exception as e:
result["error"] = str(e)
finally:
result["response_time_ms"] = int((time.time() - start) * 1000)
return result
def lambda_handler(event, context):
"""
Main Lambda entry point.
Required arguments:
- event : JSON object containing the trigger event data
- context : runtime object with invocation metadata
"""
results = []
timestamp = datetime.utcnow().isoformat()
# Check each site in the list
for site in WEBSITES:
results.append(check_website(site))
# Build the results payload
payload = {
"timestamp": timestamp,
"checks": results
}
# Generate a unique S3 key based on the timestamp
time_suffix = datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')
key = f"{S3_PREFIX}/healthcheck-{time_suffix}.json"
# Write results to S3
s3.put_object(
Bucket=S3_BUCKET,
Key=key,
Body=json.dumps(payload, indent=2),
ContentType="application/json"
)
return {
"statusCode": 200,
"body": json.dumps({
"message": "Health check completed",
"s3_key": key,
"results": results
})
}
Key Elements of handler.py
| Element | Role |
|---|---|
import boto3 | AWS SDK for Python — allows interaction with S3 |
s3 = boto3.client("s3") | Initialized outside the handler → reused between invocations (warm start) |
urllib.request | Standard Python module for HTTP requests — no external dependencies |
lambda_handler(event, context) | Required entry point — naming configurable in Runtime settings |
datetime.utcnow().isoformat() | UTC timestamp for traceability |
s3.put_object(...) | Writes the JSON file to S3 with the correct ContentType |
permissions.json — IAM S3 Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": "*"
}
]
}
In production, replace
"Resource": "*"with the precise ARN:"Resource": "arn:aws:s3:::my-canary-bucket/website-health-checks/*"
Example JSON File Generated in S3
{
"timestamp": "2024-01-15T14:32:01.123456",
"checks": [
{
"url": "https://www.google.com",
"available": true,
"status_code": 200,
"response_time_ms": 145,
"error": null
},
{
"url": "https://www.github.com",
"available": false,
"status_code": 503,
"response_time_ms": 312,
"error": "HTTP Error 503: Service Unavailable"
}
]
}
Note: Some sites return a 503 when Lambda attempts to access them. They may block automated requests originating from cloud services.
Anatomy of a Python Lambda Handler
The event Object
The event object is a Python dictionary (converted from JSON) that contains the trigger event data. Its format depends on the service invoking the Lambda:
| Invocation Type | Event Format |
|---|---|
| Manual test (console) | Custom JSON object |
| S3 Event | Contains Records[].s3.bucket.name, Records[].s3.object.key |
| API Gateway | Contains httpMethod, path, body, headers, queryStringParameters |
| SQS | Contains Records[].body, Records[].messageId |
| EventBridge (Scheduler) | Contains detail, source, time |
| DynamoDB Streams | Contains Records[].dynamodb.NewImage |
JSON to Python Type Conversion
| JSON Type | Python Type |
|---|---|
object | dict |
array | list |
number | int or float |
string | str |
Boolean | bool |
null | None |
# Example: accessing event data
def lambda_handler(event, context):
# For an order event
order_id = event.get('order_id')
amount = event.get('amount', 0)
item = event.get('item', 'Unknown')
The context Object
The context object is automatically passed by Lambda and contains metadata about the current invocation:
def lambda_handler(event, context):
# Unique invocation identifier
request_id = context.aws_request_id
# Time remaining before timeout (in milliseconds)
remaining_ms = context.get_remaining_time_in_millis()
# Function name and version
fn_name = context.function_name
fn_version = context.function_version
# Allocated memory (in MB)
memory_mb = context.memory_limit_in_mb
print(f"Invocation {request_id} — {remaining_ms}ms remaining")
Environment Variables
Environment variables allow you to configure the function without modifying the code. This is the best practice for bucket names, endpoints, configuration keys, etc.
Defining a Variable in the Console
- Configuration tab → Environment variables
- Click Edit → Add environment variable
- Example:
KEY = S3_BUCKET,VALUE = my-canary-bucket
Reading a Variable in Code
import os
def lambda_handler(event, context):
# Read a required environment variable
bucket_name = os.environ.get('S3_BUCKET')
if not bucket_name:
raise ValueError("Environment variable S3_BUCKET is missing")
# Read with default value
prefix = os.environ.get('S3_PREFIX', 'website-health-checks')
Secure Version of handler.py with Environment Variables
import json
import os
import time
import urllib.error
import urllib.request
from datetime import datetime
import boto3
s3 = boto3.client("s3")
# Configuration via environment variables
S3_BUCKET = os.environ.get('S3_BUCKET', 'my-monitoring-bucket')
S3_PREFIX = os.environ.get('S3_PREFIX', 'website-health-checks')
WEBSITES = os.environ.get('WEBSITES', 'https://www.google.com,https://www.github.com').split(',')
def check_website(url, timeout=5):
start = time.time()
result = {"url": url, "available": False, "status_code": None,
"response_time_ms": None, "error": None}
try:
with urllib.request.urlopen(
urllib.request.Request(url, method="GET"), timeout=timeout
) as response:
result["status_code"] = response.getcode()
result["available"] = 200 <= response.getcode() < 400
except urllib.error.HTTPError as e:
result["status_code"] = e.code
result["error"] = str(e)
except urllib.error.URLError as e:
result["error"] = str(e.reason)
except Exception as e:
result["error"] = str(e)
finally:
result["response_time_ms"] = int((time.time() - start) * 1000)
return result
def lambda_handler(event, context):
timestamp = datetime.utcnow().isoformat()
results = [check_website(site) for site in WEBSITES]
payload = {"timestamp": timestamp, "checks": results}
time_suffix = datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')
key = f"{S3_PREFIX}/healthcheck-{time_suffix}.json"
s3.put_object(
Bucket=S3_BUCKET, Key=key,
Body=json.dumps(payload, indent=2),
ContentType="application/json"
)
return {"statusCode": 200, "body": json.dumps({"message": "Health check completed", "s3_key": key})}
Return Value
The handler can return a JSON-serializable value. The behavior depends on the invocation type:
| Invocation Type | Return Value Behavior |
|---|---|
Synchronous (RequestResponse) | Returned to the calling client (console, API Gateway) |
Asynchronous (Event) | Ignored (SQS, S3 Events, EventBridge) |
None (no return) | The runtime returns null |
# Standard return for API Gateway
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"message": "Success", "data": results})
}
# Simple return
return {"statusCode": 200, "message": "OK"}
Event-Driven Architecture with Lambda
Common Triggers
Lambda can be triggered by many AWS services:
mindmap
root((AWS Lambda\nTriggers))
API Gateway
HTTP REST API
WebSocket API
Amazon S3
s3:ObjectCreated
s3:ObjectRemoved
Amazon SQS
Standard Queue
FIFO Queue
DynamoDB Streams
INSERT / MODIFY / REMOVE
EventBridge
Scheduler cron
Event rules
SNS
Push Notifications
Kinesis
Data Streams
ALB
Application Load Balancer
Diagram: Lambda Triggered by S3, API Gateway and SQS
flowchart TB
subgraph Triggers
S3[Amazon S3\nNew file]
API[API Gateway\nHTTP Request]
SQS[Amazon SQS\nQueued message]
end
subgraph Lambda["AWS Lambda (Execution Environment)"]
direction TB
Init[Global init\nboto3 client, config]
Handler[lambda_handler\nevent, context]
Logic[Business logic]
Init --> Handler --> Logic
end
subgraph Destinations
DDB[DynamoDB]
S3Out[Amazon S3]
CW[CloudWatch Logs]
end
S3 -->|event: s3 record| Handler
API -->|event: http request| Handler
SQS -->|event: sqs records| Handler
Logic --> DDB
Logic --> S3Out
Handler -.->|automatic logs| CW
style Lambda fill:#FF9900,color:#000,stroke:#c60
style Triggers fill:#e8f4fd,stroke:#0088cc
style Destinations fill:#e8fde8,stroke:#00aa00
Diagram: Complete Canary Flow
flowchart LR
subgraph Scheduler["EventBridge Scheduler (optional)"]
CRON[Cron rule\ne.g.: every 5 min]
end
subgraph Lambda["Lambda: myLambdaCanary"]
direction TB
A[Iterate WEBSITES]
B[HTTP GET each URL\nurllib.request]
C[Collect: status_code\nresponse_time_ms, error]
D[Build JSON payload]
A --> B --> C --> D
end
subgraph S3["Amazon S3"]
E[Bucket: my-canary-bucket]
F[website-health-checks/\nhealthcheck-20240115T143201Z.json]
E --> F
end
subgraph IAM
G[Execution Role\nAWSLambdaBasicExecutionRole\n+ s3:PutObject]
end
CRON -->|Triggers| Lambda
D -->|s3.put_object| E
Lambda -.->|Assumes| G
Lambda -->|Logs| CW[CloudWatch Logs]
style Lambda fill:#FF9900,color:#000
style S3 fill:#3F8624,color:#fff
style IAM fill:#dd3,color:#000
Lambda Configuration
Key Parameters
| Parameter | Min | Max | Default | Impact |
|---|---|---|---|---|
| Memory | 128 MB | 10 240 MB | 128 MB | More memory = more CPU + higher cost |
| Timeout | 1 sec | 900 sec (15 min) | 3 sec | Must cover the estimated max execution time |
| Reserved Concurrency | 0 | Account limit | Not set | Limits the max parallelism of the function |
| Provisioned Concurrency | N/A | Account limit | 0 | Eliminates cold starts (additional cost) |
| Ephemeral Storage (/tmp) | 512 MB | 10 240 MB | 512 MB | Temporary disk space for the environment lifetime |
Lambda Layers
A Lambda Layer is a ZIP file containing libraries, custom runtimes, or other dependencies. It is shareable across multiple Lambda functions.
flowchart TB
subgraph Layer["Lambda Layer (shared)"]
pandas[pandas==2.0.0]
numpy[numpy==1.24.0]
requests[requests==2.31.0]
end
subgraph Functions
F1[Lambda A\nData Processing]
F2[Lambda B\nAPI Handler]
F3[Lambda C\nReport Generator]
end
Layer --> F1
Layer --> F2
Layer --> F3
style Layer fill:#9cf,stroke:#06c
Layer Use Cases
- Share Python libraries (
pandas,numpy,requests) across multiple Lambdas - Separate dependencies from application code → lighter deployment packages
- Maintain common libraries in one place
Layer Limits
- Maximum 5 layers per Lambda function
- Total deployed size (function + layers): 250 MB uncompressed
- Each layer can be up to 75 MB compressed
AWS Lambda Pricing
Lambda pricing is based on two components:
1. Number of Requests (Invocations)
| Tier | Price |
|---|---|
| First million invocations/month | Free |
| Beyond | $0.20 USD per million invocations |
2. Execution Duration (Compute Time)
Duration is measured in milliseconds, rounded to the nearest millisecond. Cost depends on allocated memory:
| Memory | Price per GB-second |
|---|---|
| 128 MB | ~$0.0000000021 |
| 256 MB | ~$0.0000000042 |
| 1024 MB | ~$0.0000000167 |
Formula:
$$\text{Cost} = \text{Invocations} \times \text{Duration (sec)} \times \frac{\text{Memory (MB)}}{1024} \times \text{Price/GB-sec}$$
Permanent Monthly Free Tier
- 1 million invocations free
- 400,000 GB-seconds of free compute
Example: a Lambda at 256 MB that executes for 100ms, invoked 10 million times per month, costs approximately $0.42 USD/month after the free tier.
Best Practices
Cold Start Optimization
# ✅ GOOD: initialization OUTSIDE the handler
import boto3
import os
# These lines execute once during environment initialization
s3_client = boto3.client('s3')
ddb_client = boto3.resource('dynamodb')
TABLE_NAME = os.environ.get('TABLE_NAME')
def lambda_handler(event, context):
# Reuse s3_client and ddb_client directly
table = ddb_client.Table(TABLE_NAME)
# ...
# ❌ BAD: re-initialization on every invocation
def lambda_handler(event, context):
s3_client = boto3.client('s3') # Created on EVERY invocation
ddb_client = boto3.resource('dynamodb') # Same
# ...
Cold Start Optimization Strategies
| Strategy | Description |
|---|---|
| Out-of-handler initialization | SDK clients, DB connections, config → init only once |
| Reduce package size | Package only necessary dependencies |
| Provisioned Concurrency | Pre-initialize N environments → eliminates cold starts (paid) |
| Choose a lightweight runtime | Python/Node.js start faster than Java/.NET |
| Use Layers | Separate bulky dependencies from business code |
| Caching in /tmp | Store static assets in /tmp (512 MB by default) |
Security and Least Privilege Principle
// ✅ Production: precise ARN
{
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": "arn:aws:s3:::my-canary-bucket/website-health-checks/*"
}
// ❌ Too permissive
{
"Effect": "Allow",
"Action": ["s3:*"],
"Resource": "*"
}
IAM security rules for Lambda:
- Never use
s3:*or*:*unless absolutely necessary - Restrict
Resourceto specific ARNs - Use IAM conditions to reinforce restrictions (e.g., MFA, source IP)
- Enable AWS CloudTrail to audit all invocations
- Encrypt sensitive environment variables with AWS KMS
Idempotency and Error Handling
Lambda may invoke your function more than once in case of errors or automatic retries (especially for asynchronous invocations). Code must be idempotent:
import hashlib
import json
def lambda_handler(event, context):
# Generate a deterministic key based on event content
event_hash = hashlib.md5(json.dumps(event, sort_keys=True).encode()).hexdigest()
key = f"results/{event_hash}.json"
# Check if the result already exists (idempotency)
try:
s3.head_object(Bucket=S3_BUCKET, Key=key)
return {"statusCode": 200, "message": "Already processed", "s3_key": key}
except Exception:
pass # Object doesn't exist yet, continue
# Normal processing
result = process(event)
s3.put_object(Bucket=S3_BUCKET, Key=key, Body=json.dumps(result))
return {"statusCode": 200, "s3_key": key}
Advanced Handler Examples
Handler with Environment Variables and Logging
import json
import logging
import os
import boto3
# Logger configuration
logger = logging.getLogger()
logger.setLevel(os.environ.get('LOG_LEVEL', 'INFO'))
# S3 client initialized outside the handler
s3_client = boto3.client('s3')
def upload_to_s3(bucket: str, key: str, content: str) -> None:
"""Upload a text file to S3."""
try:
s3_client.put_object(Bucket=bucket, Key=key, Body=content)
except Exception as e:
logger.error(f"S3 upload failed: {e}")
raise
def lambda_handler(event: dict, context) -> dict:
"""Main handler with error handling and logging."""
try:
order_id = event['order_id']
amount = event['amount']
item = event['item']
bucket_name = os.environ.get('RECEIPT_BUCKET')
if not bucket_name:
raise ValueError("Environment variable RECEIPT_BUCKET is missing")
receipt = f"OrderID: {order_id}\nAmount: ${amount}\nItem: {item}"
key = f"receipts/{order_id}.txt"
upload_to_s3(bucket_name, key, receipt)
logger.info(f"Order {order_id} processed → s3://{bucket_name}/{key}")
return {
"statusCode": 200,
"message": "Receipt processed successfully"
}
except KeyError as e:
logger.error(f"Missing field in event: {e}")
return {"statusCode": 400, "message": f"Required field missing: {e}"}
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise
Handler Triggered by S3 Event
When a file is uploaded to S3, Lambda receives an event with the file metadata:
import json
import urllib.parse
import boto3
s3 = boto3.client('s3')
def lambda_handler(event, context):
# The S3 event contains a list of Records
for record in event['Records']:
bucket = record['s3']['bucket']['name']
key = urllib.parse.unquote_plus(record['s3']['object']['key'])
size = record['s3']['object']['size']
print(f"New file: s3://{bucket}/{key} ({size} bytes)")
# Download and process the file
response = s3.get_object(Bucket=bucket, Key=key)
content = response['Body'].read().decode('utf-8')
# Processing logic
process_file(content, bucket, key)
return {"statusCode": 200}
def process_file(content, bucket, key):
"""Custom file processing."""
print(f"Processing {len(content)} characters from {key}")
S3 Event Format
{
"Records": [
{
"eventName": "ObjectCreated:Put",
"s3": {
"bucket": {"name": "my-bucket"},
"object": {
"key": "uploads/report-2024.csv",
"size": 1024
}
}
}
]
}
Handler Triggered by API Gateway
import json
def lambda_handler(event, context):
http_method = event.get('httpMethod', 'GET')
path = event.get('path', '/')
body = event.get('body', '{}')
params = event.get('queryStringParameters') or {}
if http_method == 'GET':
name = params.get('name', 'World')
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"message": f"Hello, {name}!"})
}
elif http_method == 'POST':
try:
data = json.loads(body)
except json.JSONDecodeError:
return {
"statusCode": 400,
"body": json.dumps({"error": "Invalid JSON body"})
}
return {
"statusCode": 201,
"body": json.dumps({"message": "Resource created", "data": data})
}
return {
"statusCode": 405,
"body": json.dumps({"error": "Method not allowed"})
}
API Gateway Event Format
{
"httpMethod": "POST",
"path": "/orders",
"headers": {"Content-Type": "application/json"},
"queryStringParameters": {"format": "json"},
"body": "{\"order_id\": \"12345\", \"amount\": 99.99}"
}
Handler Triggered by SQS
import json
import boto3
def lambda_handler(event, context):
processed = []
failed = []
for record in event['Records']:
message_id = record['messageId']
body = record['body']
try:
data = json.loads(body)
process_message(data)
processed.append(message_id)
print(f"Message {message_id} processed successfully")
except Exception as e:
print(f"Message {message_id} failed: {e}")
# Return failed messages so they go back into the queue
failed.append({"itemIdentifier": message_id})
# Partial failure report (batchItemFailures)
return {
"batchItemFailures": failed
}
def process_message(data: dict) -> None:
"""Business logic processing."""
order_id = data.get('order_id')
print(f"Processing order {order_id}")
Expected Outcomes
After completing this course, you should be able to:
- Create a Lambda function from the AWS console with the Python runtime
- Configure an IAM execution role with minimal permissions (S3 PutObject + CloudWatch Logs)
- Deploy Python code in the inline editor or via ZIP
- Test a Lambda function and interpret the results in the console
- Verify results stored in Amazon S3
- Understand the structure of a Python handler (
event,context, return value) - Apply best practices: out-of-handler initialization, environment variables, least privilege
Validation via S3
Success is confirmed by:
- Going to Amazon S3 → configured bucket
- Navigating to the
website-health-checks/folder - Downloading a
healthcheck-YYYYMMDDTHHMMSSZ.jsonfile - Verifying that health check results are present
Additional Resources
| Resource | URL |
|---|---|
| Course source code | github.com/pluralsight-cloud/aws-lambda-function-implement-ignition |
| AWS Lambda — Official documentation | docs.aws.amazon.com/lambda/latest/dg/welcome.html |
| Python handler reference | docs.aws.amazon.com/lambda/latest/dg/python-handler.html |
| Lambda context object (Python) | docs.aws.amazon.com/lambda/latest/dg/python-context.html |
| Supported Lambda runtimes | docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html |
| Lambda layers (Python) | docs.aws.amazon.com/lambda/latest/dg/python-layers.html |
| Lambda pricing | aws.amazon.com/lambda/pricing |
| IAM Best Practices | docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html |
| Boto3 S3 reference | boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html |
Search Terms
ignition · implement · lambda · function · aws · serverless · amazon · web · services · handler · configuration · event · triggered · api · cold · environment · execution · gateway · handler.py · role · runtime · variables · console · deploying