--- name: aws-lambda description: Build and deploy serverless functions on AWS Lambda. Configure triggers, manage permissions, and optimize performance. Use when implementing serverless applications. license: MIT metadata: author: devops-skills version: "1.0" --- # AWS Lambda Build serverless applications with AWS Lambda, covering function creation, event sources, layers, SAM templates, and cold start optimization. ## When to Use This Skill - Building event-driven applications triggered by API Gateway, S3, SQS, or EventBridge - Running scheduled tasks (cron) without managing servers - Processing data streams from Kinesis or DynamoDB - Building lightweight APIs with API Gateway or function URLs - Implementing webhooks, Slack bots, or automation scripts - Reducing compute costs for intermittent or bursty workloads ## Prerequisites - AWS CLI v2 installed and configured - IAM permissions: `lambda:*`, `iam:PassRole`, `logs:*`, `apigateway:*`, `s3:*` - Python 3.11+, Node.js 20+, or another supported runtime installed locally - (Optional) AWS SAM CLI for local development and deployment ## Create and Deploy a Function ```bash # Create a deployment package cd my-function zip -r function.zip app.py # Create the Lambda function aws lambda create-function \ --function-name my-api-handler \ --runtime python3.12 \ --handler app.handler \ --role arn:aws:iam::123456789012:role/LambdaExecRole \ --zip-file fileb://function.zip \ --memory-size 256 \ --timeout 30 \ --environment 'Variables={STAGE=production,LOG_LEVEL=INFO}' \ --architectures arm64 \ --tracing-config Mode=Active \ --tags '{"Team":"backend","Environment":"production"}' # Update function code aws lambda update-function-code \ --function-name my-api-handler \ --zip-file fileb://function.zip # Update function configuration aws lambda update-function-configuration \ --function-name my-api-handler \ --memory-size 512 \ --timeout 60 \ --environment 'Variables={STAGE=production,LOG_LEVEL=WARNING}' # Publish a version (immutable snapshot) aws lambda publish-version \ --function-name my-api-handler \ --description "v1.2.0 - added rate limiting" # Create an alias pointing to the version aws lambda create-alias \ --function-name my-api-handler \ --name live \ --function-version 3 # Weighted alias for canary deployments (90% v3, 10% v4) aws lambda update-alias \ --function-name my-api-handler \ --name live \ --function-version 4 \ --routing-config '{"AdditionalVersionWeights":{"3":0.9}}' ``` ## Function Code Examples ```python # app.py - API Gateway handler with structured logging import json import logging import os logger = logging.getLogger() logger.setLevel(os.environ.get("LOG_LEVEL", "INFO")) def handler(event, context): """Handle API Gateway proxy event.""" logger.info("Request: %s %s", event["httpMethod"], event["path"]) try: body = json.loads(event.get("body", "{}")) result = process_request(body) return { "statusCode": 200, "headers": { "Content-Type": "application/json", "X-Request-Id": context.aws_request_id }, "body": json.dumps(result) } except ValueError as e: logger.warning("Validation error: %s", e) return {"statusCode": 400, "body": json.dumps({"error": str(e)})} except Exception as e: logger.exception("Unhandled error") return {"statusCode": 500, "body": json.dumps({"error": "Internal server error"})} def process_request(body): return {"message": "OK", "data": body} ``` ```python # sqs_processor.py - SQS batch processor with partial failure reporting import json import logging logger = logging.getLogger() logger.setLevel("INFO") def handler(event, context): """Process SQS messages with partial batch failure reporting.""" failed_ids = [] for record in event["Records"]: try: body = json.loads(record["body"]) logger.info("Processing message: %s", record["messageId"]) process_message(body) except Exception as e: logger.error("Failed message %s: %s", record["messageId"], e) failed_ids.append(record["messageId"]) # Return failed items so only those get retried return { "batchItemFailures": [ {"itemIdentifier": msg_id} for msg_id in failed_ids ] } def process_message(body): pass # your logic here ``` ## Lambda Layers ```bash # Build a layer for Python dependencies mkdir -p layer/python pip install requests boto3-stubs -t layer/python/ cd layer zip -r ../my-layer.zip python/ # Publish the layer aws lambda publish-layer-version \ --layer-name common-deps \ --description "Shared Python dependencies" \ --zip-file fileb://my-layer.zip \ --compatible-runtimes python3.11 python3.12 \ --compatible-architectures arm64 x86_64 # Attach layer to a function aws lambda update-function-configuration \ --function-name my-api-handler \ --layers "arn:aws:lambda:us-east-1:123456789012:layer:common-deps:1" # List available layers aws lambda list-layers --compatible-runtime python3.12 ``` ## Event Source Mappings ```bash # SQS trigger with batch processing aws lambda create-event-source-mapping \ --function-name sqs-processor \ --event-source-arn arn:aws:sqs:us-east-1:123456789012:my-queue \ --batch-size 10 \ --maximum-batching-window-in-seconds 5 \ --function-response-types ReportBatchItemFailures # DynamoDB Streams trigger aws lambda create-event-source-mapping \ --function-name stream-processor \ --event-source-arn arn:aws:dynamodb:us-east-1:123456789012:table/my-table/stream/2026-01-01T00:00:00.000 \ --batch-size 100 \ --starting-position LATEST \ --maximum-retry-attempts 3 \ --bisect-batch-on-function-error \ --destination-config '{"OnFailure":{"Destination":"arn:aws:sqs:us-east-1:123456789012:dlq"}}' # S3 event notification (via Lambda permission + S3 config) aws lambda add-permission \ --function-name image-processor \ --statement-id s3-trigger \ --action lambda:InvokeFunction \ --principal s3.amazonaws.com \ --source-arn arn:aws:s3:::my-uploads-bucket \ --source-account 123456789012 aws s3api put-bucket-notification-configuration \ --bucket my-uploads-bucket \ --notification-configuration '{ "LambdaFunctionConfigurations": [{ "LambdaFunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:image-processor", "Events": ["s3:ObjectCreated:*"], "Filter": {"Key": {"FilterRules": [{"Name": "suffix", "Value": ".jpg"}]}} }] }' # Schedule with EventBridge (cron) aws events put-rule \ --name daily-cleanup \ --schedule-expression "cron(0 2 * * ? *)" \ --state ENABLED aws lambda add-permission \ --function-name daily-cleanup \ --statement-id eventbridge \ --action lambda:InvokeFunction \ --principal events.amazonaws.com \ --source-arn arn:aws:events:us-east-1:123456789012:rule/daily-cleanup aws events put-targets \ --rule daily-cleanup \ --targets '[{"Id":"1","Arn":"arn:aws:lambda:us-east-1:123456789012:function:daily-cleanup"}]' ``` ## Function URLs (No API Gateway Needed) ```bash # Create a function URL (public HTTPS endpoint) aws lambda create-function-url-config \ --function-name my-api-handler \ --auth-type NONE \ --cors '{ "AllowOrigins": ["https://myapp.com"], "AllowMethods": ["GET", "POST"], "AllowHeaders": ["Content-Type"], "MaxAge": 86400 }' # Grant public invoke for function URL aws lambda add-permission \ --function-name my-api-handler \ --statement-id function-url-public \ --action lambda:InvokeFunctionUrl \ --principal "*" \ --function-url-auth-type NONE ``` ## Cold Start Optimization ```bash # Enable provisioned concurrency to eliminate cold starts aws lambda put-provisioned-concurrency-config \ --function-name my-api-handler \ --qualifier live \ --provisioned-concurrent-executions 10 # Set reserved concurrency (throttle limit) aws lambda put-function-concurrency \ --function-name my-api-handler \ --reserved-concurrent-executions 100 # Enable SnapStart for Java functions (near-zero cold starts) aws lambda update-function-configuration \ --function-name my-java-handler \ --snap-start '{"ApplyOn": "PublishedVersions"}' aws lambda publish-version --function-name my-java-handler ``` Cold start reduction tips: - Use `arm64` architecture (Graviton) for faster init and lower cost - Minimize deployment package size; use layers for large dependencies - Initialize SDK clients outside the handler function - Avoid VPC unless required (VPC cold starts are longer) - Use provisioned concurrency for latency-sensitive paths ## SAM Template ```yaml # template.yaml - AWS SAM application AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: My serverless API Globals: Function: Runtime: python3.12 Architectures: [arm64] MemorySize: 256 Timeout: 30 Tracing: Active Environment: Variables: STAGE: !Ref Stage LOG_LEVEL: INFO Parameters: Stage: Type: String Default: dev AllowedValues: [dev, staging, prod] Resources: ApiFunction: Type: AWS::Serverless::Function Properties: FunctionName: !Sub "${Stage}-api-handler" Handler: app.handler CodeUri: src/ Layers: - !Ref DepsLayer Events: GetItems: Type: Api Properties: Path: /items Method: get PostItem: Type: Api Properties: Path: /items Method: post Policies: - DynamoDBCrudPolicy: TableName: !Ref ItemsTable QueueProcessor: Type: AWS::Serverless::Function Properties: FunctionName: !Sub "${Stage}-queue-processor" Handler: sqs_processor.handler CodeUri: src/ Events: SQSEvent: Type: SQS Properties: Queue: !GetAtt ProcessingQueue.Arn BatchSize: 10 FunctionResponseTypes: - ReportBatchItemFailures DepsLayer: Type: AWS::Serverless::LayerVersion Properties: LayerName: common-deps ContentUri: layer/ CompatibleRuntimes: - python3.12 ItemsTable: Type: AWS::DynamoDB::Table Properties: TableName: !Sub "${Stage}-items" BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: id AttributeType: S KeySchema: - AttributeName: id KeyType: HASH ProcessingQueue: Type: AWS::SQS::Queue Properties: QueueName: !Sub "${Stage}-processing" VisibilityTimeout: 360 Outputs: ApiEndpoint: Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod" ``` ```bash # SAM CLI commands sam build sam local invoke ApiFunction --event events/get-items.json sam local start-api --port 3000 sam deploy --guided sam logs --name ApiFunction --stack-name my-stack --tail ``` ## Troubleshooting | Problem | Cause | Fix | |---|---|---| | Function times out | Timeout too low or downstream slow | Increase timeout; check VPC/NAT config | | Out of memory | Memory limit too small | Increase `--memory-size`; profile with CloudWatch Insights | | Permission denied on AWS API | Execution role missing policy | Attach required policy to the execution role | | Cold starts > 5s | Large package or VPC overhead | Use layers, arm64, provisioned concurrency; remove VPC if not needed | | SQS messages reprocessed | Visibility timeout < function timeout | Set queue visibility timeout to 6x function timeout | | Event source mapping disabled | Too many consecutive errors | Fix the function error; re-enable the mapping | | Layer not found | Wrong region or deleted version | Verify layer ARN region matches function region | | Canary deployment not shifting | Alias routing config wrong | Verify version numbers in routing config | | Cannot invoke function URL | Missing resource-based policy | Add `lambda:InvokeFunctionUrl` permission | ## Related Skills - [aws-iam](../aws-iam/) - Execution roles and permissions - [terraform-aws](../terraform-aws/) - IaC deployment for Lambda - [aws-s3](../aws-s3/) - S3 event triggers - [aws-vpc](../aws-vpc/) - VPC configuration for Lambda - [aws-cost-optimization](../aws-cost-optimization/) - Optimizing Lambda spend