diff --git a/serverless-dev/POWER.md b/serverless-dev/POWER.md new file mode 100644 index 0000000..9779350 --- /dev/null +++ b/serverless-dev/POWER.md @@ -0,0 +1,72 @@ +--- +name: "serverless-dev" +displayName: "AWS Serverless Development" +description: "Build production-ready serverless applications on AWS using AWS SAM, Lambda, API Gateway, EventBridge, and other serverless services following AWS best practices" +keywords: ["serverless", "lambda", "sam", "api gateway", "eventbridge", "dynamodb", "s3", "step functions", "sqs", "sns", "cloudformation", "lambda durable functions", "workflow"] +mcpServers: ["awslabs.aws-serverless-mcp-server", "aws-knowledge-mcp-server"] +author: "AWS" +--- + +# Onboarding + +## Step 1: Validate AWS SAM CLI +Before building serverless applications, ensure AWS SAM CLI is installed: +- **AWS SAM CLI**: Required for building and deploying serverless applications + - Verify with: `sam --version` + - Install from: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html + - **CRITICAL**: If SAM CLI is not installed, guide the user to install it before proceeding + +## Step 2: Validate AWS CLI +- **AWS CLI**: Required for AWS operations + - Verify with: `aws --version` + - Check credentials: `aws sts get-caller-identity` + - If not configured, guide user to run: `aws configure` + +## Step 3: Validate Docker (Optional but Recommended) +- **Docker**: Required for local testing and building container-based Lambda functions + - Verify with: `docker --version` + - Note: Docker is optional but highly recommended for `sam build --use-container` and `sam local` commands + +# When to Load Steering Files + +- Initializing new serverless projects → `sam-init-workflow.md` +- Building and deploying applications → `sam-build-deploy.md` +- Creating Lambda functions → `lambda-best-practices.md` +- Building long-running workflows with Lambda Durable Functions → `lambda-durable-functions.md` +- Working with API Gateway → `api-gateway-patterns.md` +- Event-driven architectures with EventBridge, SQS, SNS → `event-driven-patterns.md` +- DynamoDB integration → `dynamodb-patterns.md` +- Local testing and debugging → `local-testing.md` +- Security and IAM → `security-iam.md` +- Performance optimization → `performance-optimization.md` + +# Core Principles + +## Always Use SAM for Serverless Development +- Prefer AWS SAM over raw CloudFormation for Lambda and API Gateway +- Use SAM CLI commands: `sam init`, `sam build`, `sam deploy`, `sam local invoke` +- Leverage SAM's simplified syntax for serverless resources + +## Infrastructure as Code +- All infrastructure defined in `template.yaml` (SAM template) +- Use SAM policy templates for common IAM patterns +- Version control all infrastructure code + +## Security First +- Apply least privilege IAM permissions +- Use SAM policy templates when possible +- Enable AWS X-Ray tracing for observability +- Use environment variables for configuration, AWS Secrets Manager for secrets + +## Performance and Cost Optimization +- Right-size Lambda memory and timeout +- Use Lambda Layers for shared dependencies +- Implement proper error handling and retries +- Consider Lambda SnapStart for Java functions +- Use provisioned concurrency only when needed + +## Testing Strategy +- Test locally with `sam local invoke` and `sam local start-api` +- Use `sam build` before deployment +- Implement unit tests for Lambda handlers +- Use `sam logs` for debugging deployed functions diff --git a/serverless-dev/README.md b/serverless-dev/README.md new file mode 100644 index 0000000..4f52649 --- /dev/null +++ b/serverless-dev/README.md @@ -0,0 +1,111 @@ +# AWS Serverless Development Kiro Power + +A comprehensive Kiro Power for building production-ready serverless applications on AWS using SAM (Serverless Application Model). + +## What This Power Provides + +This power gives Kiro instant expertise in: + +- **AWS SAM** - Initialize, build, and deploy serverless applications +- **Lambda Functions** - Best practices for handlers, performance, and error handling +- **API Gateway** - REST and HTTP APIs with authentication and authorization +- **Event-Driven Architecture** - EventBridge, SQS, SNS, S3 events, DynamoDB Streams +- **DynamoDB** - Single-table design, access patterns, and optimization +- **Local Testing** - SAM local invoke and API testing +- **Security & IAM** - Least privilege policies, secrets management, encryption +- **Performance Optimization** - Cold start reduction, memory tuning, cost optimization + +## Installation + +### From Local Path (Development) + +1. Clone or download this repository +2. Open Kiro IDE +3. Go to Powers panel +4. Click "Add power from Local Path" +5. Select the `serverless-dev` directory + +### From GitHub (Once Published) + +1. Open Kiro IDE +2. Go to Powers panel +3. Click "Add power from GitHub" +4. Enter the repository URL + +## Prerequisites + +This power requires: + +- **AWS SAM CLI** - Install from https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html +- **AWS CLI** - Configured with credentials +- **Docker** (optional but recommended) - For local testing and container builds + +The power will validate these dependencies on first use. + +## Usage + +Once installed, the power activates automatically when you mention serverless-related keywords: + +- "Create a Lambda function" +- "Build a serverless API" +- "Set up DynamoDB table" +- "Deploy with SAM" +- "Add EventBridge rule" + +## Example Prompts + +**Initialize a new project:** +``` +Create a new serverless API with Python Lambda functions and DynamoDB +``` + +**Add functionality:** +``` +Add an EventBridge rule that triggers a Lambda function when orders are placed +``` + +**Optimize performance:** +``` +Optimize this Lambda function for better cold start performance +``` + +**Deploy:** +``` +Build and deploy this application using SAM +``` + +## Steering Files + +The power includes specialized guidance for: + +- `sam-init-workflow.md` - Project initialization +- `sam-build-deploy.md` - Build and deployment +- `lambda-best-practices.md` - Lambda function patterns +- `api-gateway-patterns.md` - API Gateway configuration +- `event-driven-patterns.md` - Event-driven architectures +- `dynamodb-patterns.md` - DynamoDB integration +- `local-testing.md` - Local development and testing +- `security-iam.md` - Security and IAM best practices +- `performance-optimization.md` - Performance tuning + +## Best Practices Included + +- SAM policy templates for least privilege +- Arm64 architecture for better price-performance +- X-Ray tracing enabled by default +- Structured logging patterns +- Error handling and retry strategies +- Cold start optimization techniques +- Cost optimization recommendations + +## Contributing + +To improve this power: + +1. Add or update steering files in the `steering/` directory +2. Update `POWER.md` with new keywords or workflows +3. Test locally before sharing + +## License + +This power is provided as-is for use with Kiro IDE. diff --git a/serverless-dev/mcp.json b/serverless-dev/mcp.json new file mode 100644 index 0000000..b9426b2 --- /dev/null +++ b/serverless-dev/mcp.json @@ -0,0 +1,28 @@ +{ + "mcpServers": { + "awslabs.aws-serverless-mcp-server": { + "command": "uvx", + "args": [ + "awslabs.aws-serverless-mcp-server@latest", + "--allow-write", + "--allow-sensitive-data-access" + ], + "env": { + "AWS_PROFILE": "default", + "AWS_REGION": "us-west-2", + "FASTMCP_LOG_LEVEL": "ERROR" + }, + "disabled": false, + "autoApprove": [] + }, + "aws-knowledge-mcp-server": { + "type": "http", + "url": "https://knowledge-mcp.global.api.aws", + "disabled": false, + "autoApprove": [ + "aws___read_documentation", + "aws___search_documentation" + ] + } + } +} diff --git a/serverless-dev/steering/api-gateway-patterns.md b/serverless-dev/steering/api-gateway-patterns.md new file mode 100644 index 0000000..c62bb6a --- /dev/null +++ b/serverless-dev/steering/api-gateway-patterns.md @@ -0,0 +1,178 @@ +# API Gateway Patterns + +## REST API with SAM + +```yaml +ServerlessRestApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Cors: + AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'" + AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'" + AllowOrigin: "'*'" + Auth: + DefaultAuthorizer: MyCognitoAuthorizer + Authorizers: + MyCognitoAuthorizer: + UserPoolArn: !GetAtt UserPool.Arn + +GetItemFunction: + Type: AWS::Serverless::Function + Properties: + Events: + GetItem: + Type: Api + Properties: + RestApiId: !Ref ServerlessRestApi + Path: /items/{id} + Method: get +``` + +## HTTP API (Recommended for Lower Cost) + +```yaml +HttpApi: + Type: AWS::Serverless::HttpApi + Properties: + CorsConfiguration: + AllowOrigins: + - "*" + AllowMethods: + - GET + - POST + AllowHeaders: + - Content-Type + +MyFunction: + Type: AWS::Serverless::Function + Properties: + Events: + HttpApiEvent: + Type: HttpApi + Properties: + ApiId: !Ref HttpApi + Path: /items + Method: get +``` + +## Request/Response Patterns + +### Lambda Proxy Integration (Default) +Function receives full request and returns formatted response: + +```python +def lambda_handler(event, context): + # event contains: httpMethod, path, queryStringParameters, headers, body + return { + 'statusCode': 200, + 'headers': { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + 'body': json.dumps({'message': 'Success'}) + } +``` + +### Path Parameters +```yaml +Events: + GetItem: + Type: Api + Properties: + Path: /items/{id} + Method: get +``` + +Access in handler: +```python +item_id = event['pathParameters']['id'] +``` + +### Query Parameters +```python +query_params = event.get('queryStringParameters', {}) +limit = query_params.get('limit', '10') +``` + +## Authentication + +### Cognito Authorizer +```yaml +Auth: + DefaultAuthorizer: MyCognitoAuth + Authorizers: + MyCognitoAuth: + UserPoolArn: !GetAtt UserPool.Arn +``` + +### Lambda Authorizer +```yaml +Auth: + DefaultAuthorizer: MyLambdaAuth + Authorizers: + MyLambdaAuth: + FunctionArn: !GetAtt AuthFunction.Arn + Identity: + Headers: + - Authorization +``` + +## API Gateway Best Practices + +### Enable Throttling +```yaml +MethodSettings: + - ResourcePath: "/*" + HttpMethod: "*" + ThrottlingBurstLimit: 100 + ThrottlingRateLimit: 50 +``` + +### Enable Caching (REST API only) +```yaml +MethodSettings: + - ResourcePath: "/*" + HttpMethod: "GET" + CachingEnabled: true + CacheTtlInSeconds: 300 +``` + +### Request Validation +```yaml +RequestValidator: + Type: AWS::ApiGateway::RequestValidator + Properties: + RestApiId: !Ref ServerlessRestApi + ValidateRequestBody: true + ValidateRequestParameters: true +``` + +### API Keys and Usage Plans +```yaml +ApiKey: + Type: AWS::ApiGateway::ApiKey + Properties: + Enabled: true + +UsagePlan: + Type: AWS::ApiGateway::UsagePlan + Properties: + ApiStages: + - ApiId: !Ref ServerlessRestApi + Stage: Prod + Throttle: + RateLimit: 100 + BurstLimit: 200 +``` + +## Error Handling + +Return proper HTTP status codes: +- 200: Success +- 201: Created +- 400: Bad Request +- 401: Unauthorized +- 403: Forbidden +- 404: Not Found +- 500: Internal Server Error diff --git a/serverless-dev/steering/dynamodb-patterns.md b/serverless-dev/steering/dynamodb-patterns.md new file mode 100644 index 0000000..3045096 --- /dev/null +++ b/serverless-dev/steering/dynamodb-patterns.md @@ -0,0 +1,207 @@ +# DynamoDB Integration Patterns + +## Table Definition in SAM + +```yaml +MyTable: + Type: AWS::DynamoDB::Table + Properties: + TableName: !Sub ${AWS::StackName}-items + BillingMode: PAY_PER_REQUEST # On-demand pricing + AttributeDefinitions: + - AttributeName: PK + AttributeType: S + - AttributeName: SK + AttributeType: S + - AttributeName: GSI1PK + AttributeType: S + KeySchema: + - AttributeName: PK + KeyType: HASH + - AttributeName: SK + KeyType: RANGE + GlobalSecondaryIndexes: + - IndexName: GSI1 + KeySchema: + - AttributeName: GSI1PK + KeyType: HASH + - AttributeName: SK + KeyType: RANGE + Projection: + ProjectionType: ALL + StreamSpecification: + StreamViewType: NEW_AND_OLD_IMAGES + PointInTimeRecoverySpecification: + PointInTimeRecoveryEnabled: true +``` + +## Granting Permissions + +```yaml +MyFunction: + Type: AWS::Serverless::Function + Properties: + Policies: + - DynamoDBCrudPolicy: + TableName: !Ref MyTable +``` + +## Python SDK Usage + +```python +import boto3 +from boto3.dynamodb.conditions import Key, Attr + +dynamodb = boto3.resource('dynamodb') +table = dynamodb.Table(os.environ['TABLE_NAME']) + +# Put Item +table.put_item( + Item={ + 'PK': f'USER#{user_id}', + 'SK': f'PROFILE#{user_id}', + 'name': 'John Doe', + 'email': 'john@example.com' + } +) + +# Get Item +response = table.get_item( + Key={ + 'PK': f'USER#{user_id}', + 'SK': f'PROFILE#{user_id}' + } +) +item = response.get('Item') + +# Query +response = table.query( + KeyConditionExpression=Key('PK').eq(f'USER#{user_id}') +) +items = response['Items'] + +# Update Item +table.update_item( + Key={'PK': pk, 'SK': sk}, + UpdateExpression='SET #name = :name', + ExpressionAttributeNames={'#name': 'name'}, + ExpressionAttributeValues={':name': 'Jane Doe'} +) + +# Delete Item +table.delete_item( + Key={'PK': pk, 'SK': sk} +) +``` + +## Single-Table Design + +Use composite keys for multiple entity types: + +```python +# User entity +PK: USER#123 +SK: PROFILE#123 + +# Order entity +PK: USER#123 +SK: ORDER#456 + +# Product entity +PK: PRODUCT#789 +SK: METADATA#789 +``` + +## Access Patterns + +### Query by Partition Key +```python +response = table.query( + KeyConditionExpression=Key('PK').eq('USER#123') +) +``` + +### Query with Sort Key Condition +```python +response = table.query( + KeyConditionExpression=Key('PK').eq('USER#123') & Key('SK').begins_with('ORDER#') +) +``` + +### Query GSI +```python +response = table.query( + IndexName='GSI1', + KeyConditionExpression=Key('GSI1PK').eq('STATUS#PENDING') +) +``` + +### Scan (Avoid in Production) +```python +response = table.scan( + FilterExpression=Attr('status').eq('active') +) +``` + +## Best Practices + +### Key Design +- Use meaningful prefixes (USER#, ORDER#, PRODUCT#) +- Design keys to support access patterns +- Avoid hot partitions + +### Attributes +- Use sparse indexes for optional attributes +- Store related data together (denormalization) +- Use DynamoDB Streams for change data capture + +### Performance +- Use BatchGetItem and BatchWriteItem for bulk operations +- Enable DAX for read-heavy workloads +- Use projection expressions to fetch only needed attributes + +### Cost Optimization +- Use on-demand billing for unpredictable workloads +- Use provisioned capacity with auto-scaling for predictable workloads +- Archive old data to S3 + +## Transactions + +```python +dynamodb_client = boto3.client('dynamodb') + +dynamodb_client.transact_write_items( + TransactItems=[ + { + 'Put': { + 'TableName': table_name, + 'Item': {'PK': {'S': 'USER#123'}, 'balance': {'N': '100'}} + } + }, + { + 'Update': { + 'TableName': table_name, + 'Key': {'PK': {'S': 'ACCOUNT#456'}}, + 'UpdateExpression': 'SET balance = balance - :amount', + 'ExpressionAttributeValues': {':amount': {'N': '100'}} + } + } + ] +) +``` + +## Error Handling + +```python +from botocore.exceptions import ClientError + +try: + table.put_item(Item=item) +except ClientError as e: + if e.response['Error']['Code'] == 'ConditionalCheckFailedException': + # Handle condition failure + pass + elif e.response['Error']['Code'] == 'ProvisionedThroughputExceededException': + # Handle throttling + pass +``` diff --git a/serverless-dev/steering/event-driven-patterns.md b/serverless-dev/steering/event-driven-patterns.md new file mode 100644 index 0000000..b06ee1d --- /dev/null +++ b/serverless-dev/steering/event-driven-patterns.md @@ -0,0 +1,208 @@ +# Event-Driven Architecture Patterns + +## EventBridge + +### EventBridge Rule +```yaml +MyEventRule: + Type: AWS::Events::Rule + Properties: + EventBusName: default + EventPattern: + source: + - my.application + detail-type: + - Order Placed + Targets: + - Arn: !GetAtt ProcessOrderFunction.Arn + Id: ProcessOrderTarget + +ProcessOrderFunction: + Type: AWS::Serverless::Function + Properties: + Events: + EventBridgeRule: + Type: EventBridgeRule + Properties: + Pattern: + source: + - my.application + detail-type: + - Order Placed +``` + +### Publishing Events +```python +import boto3 +import json + +events = boto3.client('events') + +events.put_events( + Entries=[{ + 'Source': 'my.application', + 'DetailType': 'Order Placed', + 'Detail': json.dumps({ + 'orderId': '12345', + 'amount': 99.99 + }) + }] +) +``` + +## SQS Queue Integration + +```yaml +MyQueue: + Type: AWS::SQS::Queue + Properties: + VisibilityTimeout: 300 + MessageRetentionPeriod: 1209600 + RedrivePolicy: + deadLetterTargetArn: !GetAtt MyDLQ.Arn + maxReceiveCount: 3 + +MyDLQ: + Type: AWS::SQS::Queue + +ProcessQueueFunction: + Type: AWS::Serverless::Function + Properties: + Events: + SQSEvent: + Type: SQS + Properties: + Queue: !GetAtt MyQueue.Arn + BatchSize: 10 + MaximumBatchingWindowInSeconds: 5 +``` + +### SQS Handler Pattern +```python +def lambda_handler(event, context): + for record in event['Records']: + body = json.loads(record['body']) + # Process message + process_message(body) +``` + +## SNS Topic Integration + +```yaml +MyTopic: + Type: AWS::SNS::Topic + +NotifyFunction: + Type: AWS::Serverless::Function + Properties: + Events: + SNSEvent: + Type: SNS + Properties: + Topic: !Ref MyTopic +``` + +### Publishing to SNS +```python +sns = boto3.client('sns') + +sns.publish( + TopicArn='arn:aws:sns:region:account:topic', + Message=json.dumps({'event': 'data'}), + Subject='Notification' +) +``` + +## S3 Event Notifications + +```yaml +ProcessS3Function: + Type: AWS::Serverless::Function + Properties: + Events: + S3Event: + Type: S3 + Properties: + Bucket: !Ref MyBucket + Events: s3:ObjectCreated:* + Filter: + S3Key: + Rules: + - Name: prefix + Value: uploads/ + - Name: suffix + Value: .jpg + +MyBucket: + Type: AWS::S3::Bucket +``` + +### S3 Handler Pattern +```python +def lambda_handler(event, context): + for record in event['Records']: + bucket = record['s3']['bucket']['name'] + key = record['s3']['object']['key'] + # Process S3 object +``` + +## DynamoDB Streams + +```yaml +MyTable: + Type: AWS::DynamoDB::Table + Properties: + StreamSpecification: + StreamViewType: NEW_AND_OLD_IMAGES + +ProcessStreamFunction: + Type: AWS::Serverless::Function + Properties: + Events: + DynamoDBStream: + Type: DynamoDB + Properties: + Stream: !GetAtt MyTable.StreamArn + StartingPosition: LATEST + BatchSize: 100 + MaximumBatchingWindowInSeconds: 10 +``` + +## Step Functions + +```yaml +MyStateMachine: + Type: AWS::Serverless::StateMachine + Properties: + DefinitionUri: statemachine/workflow.asl.json + Policies: + - LambdaInvokePolicy: + FunctionName: !Ref ProcessFunction + Events: + ApiEvent: + Type: Api + Properties: + Path: /workflow + Method: post +``` + +## Event-Driven Best Practices + +### Idempotency +- Use idempotency tokens for duplicate prevention +- Store processed event IDs in DynamoDB + +### Error Handling +- Configure DLQs for all async invocations +- Set appropriate retry policies +- Monitor DLQ depth with CloudWatch alarms + +### Event Schema +- Use consistent event structure +- Include correlation IDs for tracing +- Version your event schemas + +### Decoupling +- Use queues/topics between services +- Avoid direct service-to-service calls +- Design for eventual consistency diff --git a/serverless-dev/steering/lambda-best-practices.md b/serverless-dev/steering/lambda-best-practices.md new file mode 100644 index 0000000..665f4fb --- /dev/null +++ b/serverless-dev/steering/lambda-best-practices.md @@ -0,0 +1,168 @@ +# Lambda Function Best Practices + +## Function Definition in SAM + +```yaml +MyFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: src/ + Handler: app.lambda_handler + Runtime: python3.13 + Architectures: + - arm64 # Graviton2 for better price-performance + MemorySize: 512 + Timeout: 30 + Tracing: Active + Environment: + Variables: + TABLE_NAME: !Ref MyTable + LOG_LEVEL: INFO + Policies: + - DynamoDBCrudPolicy: + TableName: !Ref MyTable + Events: + ApiEvent: + Type: Api + Properties: + Path: /items + Method: get +``` + +## Handler Code Structure + +### Python Handler Pattern +```python +import json +import os +import boto3 +from aws_lambda_powertools import Logger, Tracer, Metrics +from aws_lambda_powertools.utilities.typing import LambdaContext + +logger = Logger() +tracer = Tracer() +metrics = Metrics() + +# Initialize clients outside handler (reused across invocations) +dynamodb = boto3.resource('dynamodb') +table = dynamodb.Table(os.environ['TABLE_NAME']) + +@tracer.capture_lambda_handler +@metrics.log_metrics +@logger.inject_lambda_context +def lambda_handler(event: dict, context: LambdaContext) -> dict: + try: + # Business logic here + result = process_request(event) + + return { + 'statusCode': 200, + 'body': json.dumps(result) + } + except Exception as e: + logger.exception("Error processing request") + return { + 'statusCode': 500, + 'body': json.dumps({'error': str(e)}) + } +``` + +### Node.js Handler Pattern +```javascript +const { DynamoDBClient } = require('@aws-sdk/client-dynamodb'); +const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb'); + +// Initialize outside handler +const client = new DynamoDBClient({}); +const docClient = DynamoDBDocumentClient.from(client); + +exports.handler = async (event, context) => { + try { + // Business logic + const result = await processRequest(event); + + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } catch (error) { + console.error('Error:', error); + return { + statusCode: 500, + body: JSON.stringify({ error: error.message }) + }; + } +}; +``` + +## Performance Optimization + +### Memory Configuration +- Start with 512 MB, adjust based on CloudWatch metrics +- More memory = more CPU power +- Use Lambda Power Tuning tool to find optimal configuration + +### Cold Start Reduction +- Keep deployment package small +- Use Lambda Layers for dependencies +- Consider Lambda SnapStart (Java) +- Use Provisioned Concurrency for latency-sensitive functions + +### Initialization Best Practices +- Initialize SDK clients outside handler +- Reuse database connections +- Cache static data in global scope +- Use /tmp for temporary file storage (512 MB - 10 GB) + +## Error Handling + +### Implement Retries +```yaml +EventInvokeConfig: + MaximumRetryAttempts: 2 + MaximumEventAgeInSeconds: 3600 + DestinationConfig: + OnFailure: + Type: SQS + Destination: !GetAtt DeadLetterQueue.Arn +``` + +### Use Dead Letter Queues +```yaml +DeadLetterQueue: + Type: AWS::SQS::Queue + Properties: + MessageRetentionPeriod: 1209600 # 14 days +``` + +## Environment Variables + +- Use for configuration, not secrets +- For secrets, use AWS Secrets Manager or Parameter Store +- Reference other resources using !Ref or !GetAtt + +## Lambda Layers + +```yaml +MyLayer: + Type: AWS::Serverless::LayerVersion + Properties: + LayerName: shared-dependencies + ContentUri: layers/ + CompatibleRuntimes: + - python3.13 + RetentionPolicy: Retain + +MyFunction: + Type: AWS::Serverless::Function + Properties: + Layers: + - !Ref MyLayer +``` + +## Monitoring + +- Enable X-Ray tracing +- Use structured logging +- Set CloudWatch alarms for errors and throttles +- Use Lambda Insights for enhanced metrics diff --git a/serverless-dev/steering/lambda-durable-functions.md b/serverless-dev/steering/lambda-durable-functions.md new file mode 100644 index 0000000..e5ac444 --- /dev/null +++ b/serverless-dev/steering/lambda-durable-functions.md @@ -0,0 +1,424 @@ +# Lambda Durable Functions + +Lambda Durable Functions enable long-running workflows that can span hours, days, or up to 1 year. They automatically checkpoint state and resume execution, allowing workflows to wait for callbacks, handle retries, and maintain execution history. + +## Enabling Durable Functions + +Add `DurableConfig` to your function: + +```yaml +OrderProcessorFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: src/order-processor + Handler: index.handler + Runtime: nodejs22.x + Architectures: + - arm64 + Timeout: 900 # Function timeout: 15 minutes max + DurableConfig: + ExecutionTimeout: 3600 # Execution timeout: up to 1 year + RetentionPeriodInDays: 7 # Keep execution history +``` + +## Understanding Timeouts + +**Function Timeout** (`Timeout`): +- Controls each individual Lambda invocation +- Maximum: 15 minutes (900 seconds) +- Each checkpoint/resume is a new invocation with its own timeout + +**Execution Timeout** (`ExecutionTimeout`): +- Controls the entire workflow duration +- Maximum: 1 year (31,536,000 seconds) +- Workflow can pause, wait, and resume many times + +**Critical**: If execution timeout > function timeout, you MUST use asynchronous invocation. + +## SDK Installation + +### TypeScript/JavaScript +Add to `package.json`: +```json +{ + "dependencies": { + "@aws/durable-execution-sdk-js": "^1.0.0", + "@aws-sdk/client-lambda": "^3.0.0" + } +} +``` + +### Python +Add to `requirements.txt`: +``` +aws-durable-execution-sdk-python +boto3 +``` + +## TypeScript Build Configuration + +```yaml +OrderProcessorFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: src/order-processor + Handler: index.handler + Runtime: nodejs22.x + DurableConfig: + ExecutionTimeout: 3600 + RetentionPeriodInDays: 7 + Metadata: + BuildMethod: esbuild + BuildProperties: + EntryPoints: + - index.ts +``` + +## Durable Function Code Pattern + +### TypeScript Example +```typescript +import { DurableExecution } from '@aws/durable-execution-sdk-js'; + +export const handler = async (event: any) => { + const execution = new DurableExecution(); + + // Step 1: Initial processing + const orderId = event.orderId; + await saveOrder(orderId); + + // Checkpoint and wait for callback (up to 5 minutes) + const baristaResponse = await execution.waitForCallback({ + timeoutSeconds: 300 + }); + + // Step 2: Process callback result + if (baristaResponse.accepted) { + await prepareOrder(orderId); + + // Wait for completion callback + const completionResponse = await execution.waitForCallback({ + timeoutSeconds: 600 + }); + + return { status: 'completed', orderId }; + } + + return { status: 'rejected', orderId }; +}; +``` + +### Python Example +```python +from aws_durable_execution_sdk_python import DurableExecution + +def lambda_handler(event, context): + execution = DurableExecution() + + # Step 1: Initial processing + order_id = event['orderId'] + save_order(order_id) + + # Checkpoint and wait for callback + barista_response = execution.wait_for_callback( + timeout_seconds=300 + ) + + # Step 2: Process callback result + if barista_response['accepted']: + prepare_order(order_id) + + completion_response = execution.wait_for_callback( + timeout_seconds=600 + ) + + return {'status': 'completed', 'orderId': order_id} + + return {'status': 'rejected', 'orderId': order_id} +``` + +## IAM Permissions for Client Functions + +### Callback Functions +```yaml +BaristaCallbackFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: src/barista-callback + Handler: index.handler + Policies: + - Statement: + - Effect: Allow + Action: + - lambda:SendDurableExecutionCallbackSuccess + - lambda:SendDurableExecutionCallbackFailure + - lambda:SendDurableExecutionCallbackHeartbeat + Resource: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${OrderProcessorFunction}' +``` + +### Monitoring Functions +```yaml +MonitoringFunction: + Type: AWS::Serverless::Function + Properties: + Policies: + - Statement: + - Effect: Allow + Action: + - lambda:GetDurableExecution + - lambda:GetDurableExecutionHistory + - lambda:ListDurableExecutionsByFunction + - lambda:StopDurableExecution + Resource: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${OrderProcessorFunction}' +``` + +## Sending Callbacks + +### TypeScript +```typescript +import { LambdaClient, SendDurableExecutionCallbackSuccessCommand } from '@aws-sdk/client-lambda'; + +const lambda = new LambdaClient({}); + +await lambda.send(new SendDurableExecutionCallbackSuccessCommand({ + CallbackId: callbackId, + Result: JSON.stringify({ accepted: true }) +})); +``` + +### Python +```python +import boto3 +import json + +lambda_client = boto3.client('lambda') + +lambda_client.send_durable_execution_callback_success( + CallbackId=callback_id, + Result=json.dumps({'accepted': True}) +) +``` + +## Event-Driven Patterns + +### API Gateway Trigger (Async for Long Workflows) +```yaml +OrderProcessorFunction: + Type: AWS::Serverless::Function + Properties: + Timeout: 900 + DurableConfig: + ExecutionTimeout: 86400 # 24 hours + Events: + ApiEvent: + Type: Api + Properties: + Path: /orders + Method: post +``` + +### EventBridge Trigger +```yaml +OrderProcessorFunction: + Type: AWS::Serverless::Function + Properties: + Events: + EventBridgeRule: + Type: EventBridgeRule + Properties: + Pattern: + source: + - order.system + detail-type: + - OrderPlaced +``` + +### Scheduled Workflows +```yaml +DailyReportFunction: + Type: AWS::Serverless::Function + Properties: + DurableConfig: + ExecutionTimeout: 7200 # 2 hours + Events: + DailySchedule: + Type: Schedule + Properties: + Schedule: cron(0 9 * * ? *) +``` + +## Local Testing + +### Invoke Locally +```bash +sam local invoke OrderProcessorFunction --event events/order.json +``` + +### With Environment Variables +```bash +sam local invoke OrderProcessorFunction \ + --event events/order.json \ + --env-vars locals.json +``` + +locals.json: +```json +{ + "OrderProcessorFunction": { + "AWS_REGION": "us-east-1", + "ORDERS_TABLE": "CoffeeOrders" + } +} +``` + +### Track Execution State +```bash +# Get execution details +sam local execution get $EXECUTION_ARN + +# View execution history +sam local execution history $EXECUTION_ARN + +# Stop execution +sam local execution stop $EXECUTION_ARN +``` + +### Test Callbacks +```bash +# Success callback +sam local callback succeed $CALLBACK_ID --result '{"accepted": true}' + +# Failure callback +sam local callback fail $CALLBACK_ID --error '{"message": "Rejected"}' + +# Heartbeat +sam local callback heartbeat $CALLBACK_ID +``` + +## Remote Testing + +```bash +# Invoke deployed function +sam remote invoke OrderProcessorFunction --event events/order.json + +# Invoke specific version/alias +sam remote invoke OrderProcessorFunction:prod --event events/order.json + +# Get execution details +sam remote execution get $EXECUTION_ARN + +# View execution history +sam remote execution history $EXECUTION_ARN +``` + +## Configuration Best Practices + +### Execution Timeout +- Short workflows (minutes): 600-3600 seconds +- Medium workflows (hours): 3600-86400 seconds +- Long workflows (days): 86400-2592000 seconds +- Maximum: 31,536,000 seconds (1 year) + +### Function Timeout +- Quick operations: 30-60 seconds +- Standard processing: 60-300 seconds +- Heavy processing: 300-900 seconds +- Maximum: 900 seconds (15 minutes) + +### Retention Period +- Development: 7 days +- Production: 30-90 days +- Compliance: 90+ days + +### Memory Configuration +- Start with 512 MB +- Durable functions typically use less memory than non-durable +- Adjust based on CloudWatch metrics + +## Monitoring + +### Enable X-Ray Tracing +```yaml +Globals: + Function: + Tracing: Active +``` + +### CloudWatch Logs Insights Query +``` +fields @timestamp, @message +| filter @message like /CHECKPOINT/ +| sort @timestamp desc +| limit 100 +``` + +### CloudWatch Alarms +```yaml +ExecutionFailureAlarm: + Type: AWS::CloudWatch::Alarm + Properties: + AlarmName: OrderProcessorFailures + MetricName: Errors + Namespace: AWS/Lambda + Statistic: Sum + Period: 300 + EvaluationPeriods: 1 + Threshold: 5 + ComparisonOperator: GreaterThanThreshold + Dimensions: + - Name: FunctionName + Value: !Ref OrderProcessorFunction +``` + +## Common Use Cases + +### Approval Workflows +Wait for human approval with timeout: +```typescript +const approval = await execution.waitForCallback({ + timeoutSeconds: 86400 // 24 hours +}); +``` + +### Multi-Step Processing +Chain operations with checkpoints: +```typescript +await step1(); +await execution.checkpoint(); + +await step2(); +await execution.checkpoint(); + +await step3(); +``` + +### Retry with Backoff +Implement custom retry logic: +```typescript +for (let i = 0; i < 3; i++) { + try { + await processOrder(); + break; + } catch (error) { + if (i === 2) throw error; + await execution.sleep(Math.pow(2, i) * 1000); + } +} +``` + +## Troubleshooting + +### Validation Error on Invoke +If execution timeout > function timeout, use async invocation. + +### Individual Invocation Timeout +Increase `Timeout` property (max 900 seconds). + +### Workflow Timeout +Increase `ExecutionTimeout` in `DurableConfig` (max 1 year). + +### Permission Denied for Callbacks +Ensure client functions have `SendDurableExecutionCallback*` permissions. + +### Execution History Not Available +Check `RetentionPeriodInDays` - history may have expired. diff --git a/serverless-dev/steering/local-testing.md b/serverless-dev/steering/local-testing.md new file mode 100644 index 0000000..a14e802 --- /dev/null +++ b/serverless-dev/steering/local-testing.md @@ -0,0 +1,164 @@ +# Local Testing with SAM + +## Local Function Invocation + +### Invoke with Event File +```bash +sam local invoke MyFunction -e events/event.json +``` + +### Invoke with Inline Event +```bash +echo '{"key": "value"}' | sam local invoke MyFunction +``` + +### Invoke Specific Function +```bash +sam local invoke MyFunction --parameter-overrides Environment=dev +``` + +## Local API Gateway + +### Start Local API +```bash +sam local start-api +``` + +Access at: http://localhost:3000 + +### Custom Port +```bash +sam local start-api --port 8080 +``` + +### With Environment Variables +```bash +sam local start-api --env-vars env.json +``` + +env.json: +```json +{ + "MyFunction": { + "TABLE_NAME": "local-table", + "LOG_LEVEL": "DEBUG" + } +} +``` + +## Debugging + +### Python with Debugger +```bash +sam local invoke -d 5858 MyFunction +``` + +Then attach debugger to port 5858. + +### Node.js with Debugger +```bash +sam local invoke -d 9229 MyFunction +``` + +## Generate Sample Events + +```bash +sam local generate-event apigateway aws-proxy > events/api-event.json +sam local generate-event s3 put > events/s3-event.json +sam local generate-event dynamodb update > events/dynamodb-event.json +``` + +## Testing with Docker + +### Use Container for Build +```bash +sam build --use-container +``` + +### Specify Docker Network +```bash +sam local start-api --docker-network my-network +``` + +Useful for connecting to local databases. + +## Logs + +### View Function Logs +```bash +sam logs -n MyFunction --stack-name my-stack --tail +``` + +### Filter Logs +```bash +sam logs -n MyFunction --filter "ERROR" +``` + +### Logs from Specific Time +```bash +sam logs -n MyFunction --start-time "10min ago" +``` + +## Testing Best Practices + +### Create Test Events +Store test events in `events/` directory: +``` +events/ +├── api-get-request.json +├── api-post-request.json +├── sqs-event.json +└── s3-event.json +``` + +### Environment Variables +- Use `env.json` for local environment variables +- Never commit secrets to version control +- Use AWS Secrets Manager for sensitive data + +### Unit Tests +```python +import pytest +from src import app + +def test_lambda_handler(): + event = { + 'httpMethod': 'GET', + 'path': '/items' + } + response = app.lambda_handler(event, None) + assert response['statusCode'] == 200 +``` + +### Integration Tests +Test with `sam local invoke` in CI/CD pipeline: +```bash +sam build +sam local invoke MyFunction -e tests/integration/event.json +``` + +## Troubleshooting + +### Function Timeout +Increase timeout in template.yaml: +```yaml +Timeout: 60 +``` + +### Memory Issues +Increase memory: +```yaml +MemorySize: 1024 +``` + +### Docker Issues +- Ensure Docker is running +- Check Docker has enough resources +- Use `--skip-pull-image` to use cached images + +### Permission Issues +Test IAM policies locally by setting AWS credentials: +```bash +export AWS_PROFILE=dev +sam local invoke MyFunction +``` diff --git a/serverless-dev/steering/performance-optimization.md b/serverless-dev/steering/performance-optimization.md new file mode 100644 index 0000000..456edcb --- /dev/null +++ b/serverless-dev/steering/performance-optimization.md @@ -0,0 +1,240 @@ +# Performance Optimization + +## Lambda Configuration + +### Memory and CPU +- Memory range: 128 MB to 10,240 MB +- CPU scales proportionally with memory +- Use Lambda Power Tuning to find optimal configuration + +```yaml +MyFunction: + Type: AWS::Serverless::Function + Properties: + MemorySize: 1024 # Start here, tune based on metrics + Timeout: 30 +``` + +### Architecture +```yaml +Architectures: + - arm64 # Graviton2: 20% better price-performance +``` + +## Cold Start Optimization + +### Keep Package Size Small +- Remove unnecessary dependencies +- Use Lambda Layers for shared code +- Minimize deployment package size + +### Provisioned Concurrency +```yaml +MyFunction: + Type: AWS::Serverless::Function + Properties: + ProvisionedConcurrencyConfig: + ProvisionedConcurrentExecutions: 5 +``` + +Use for latency-sensitive functions only (adds cost). + +### Lambda SnapStart (Java) +```yaml +MyJavaFunction: + Type: AWS::Serverless::Function + Properties: + Runtime: java21 + SnapStart: + ApplyOn: PublishedVersions +``` + +## Code Optimization + +### Initialize Outside Handler +```python +# Good: Initialize once +import boto3 +dynamodb = boto3.resource('dynamodb') +table = dynamodb.Table(os.environ['TABLE_NAME']) + +def lambda_handler(event, context): + # Use initialized resources + table.get_item(Key={'id': '123'}) +``` + +```python +# Bad: Initialize on every invocation +def lambda_handler(event, context): + dynamodb = boto3.resource('dynamodb') + table = dynamodb.Table(os.environ['TABLE_NAME']) + table.get_item(Key={'id': '123'}) +``` + +### Connection Pooling +```python +import urllib3 + +# Reuse connection pool +http = urllib3.PoolManager(maxsize=10) + +def lambda_handler(event, context): + response = http.request('GET', 'https://api.example.com') +``` + +### Use /tmp for Caching +```python +import os +import json + +CACHE_FILE = '/tmp/cache.json' + +def lambda_handler(event, context): + if os.path.exists(CACHE_FILE): + with open(CACHE_FILE) as f: + cache = json.load(f) + else: + cache = fetch_data() + with open(CACHE_FILE, 'w') as f: + json.dump(cache, f) +``` + +## DynamoDB Optimization + +### Use BatchGetItem +```python +# Good: Batch operation +response = dynamodb.batch_get_item( + RequestItems={ + 'MyTable': { + 'Keys': [{'id': '1'}, {'id': '2'}, {'id': '3'}] + } + } +) +``` + +### Use Projection Expressions +```python +# Only fetch needed attributes +response = table.get_item( + Key={'id': '123'}, + ProjectionExpression='name,email' +) +``` + +### Enable DAX for Read-Heavy Workloads +```yaml +DAXCluster: + Type: AWS::DAX::Cluster + Properties: + NodeType: dax.t3.small + ReplicationFactor: 3 +``` + +## API Gateway Optimization + +### Enable Caching +```yaml +ServerlessRestApi: + Type: AWS::Serverless::Api + Properties: + CacheClusterEnabled: true + CacheClusterSize: '0.5' + MethodSettings: + - ResourcePath: "/*" + HttpMethod: "GET" + CachingEnabled: true + CacheTtlInSeconds: 300 +``` + +### Use HTTP API for Lower Latency +HTTP APIs have lower latency and cost than REST APIs. + +## Async Processing + +### Use SQS for Decoupling +```yaml +ProcessQueue: + Type: AWS::SQS::Queue + +ApiFunction: + Type: AWS::Serverless::Function + Properties: + Handler: api.handler + Environment: + Variables: + QUEUE_URL: !Ref ProcessQueue +``` + +Send to queue instead of processing synchronously: +```python +sqs = boto3.client('sqs') +sqs.send_message( + QueueUrl=os.environ['QUEUE_URL'], + MessageBody=json.dumps(data) +) +``` + +## Monitoring and Tuning + +### Key Metrics to Monitor +- Duration +- Memory utilization +- Cold start frequency +- Throttles +- Errors + +### CloudWatch Insights Query +``` +filter @type = "REPORT" +| stats avg(@duration), max(@duration), avg(@maxMemoryUsed/1024/1024) as avgMemoryUsedMB +``` + +### Lambda Insights +```yaml +MyFunction: + Type: AWS::Serverless::Function + Properties: + Layers: + - !Sub 'arn:aws:lambda:${AWS::Region}:580247275435:layer:LambdaInsightsExtension:21' +``` + +## Cost Optimization + +### Right-Size Memory +- Monitor actual memory usage +- Reduce memory if consistently under 50% utilization +- Increase if approaching limit + +### Use Arm64 Architecture +20% better price-performance than x86_64. + +### Optimize Execution Time +- Reduce cold starts +- Optimize code efficiency +- Use async processing where possible + +### Set Appropriate Timeouts +Don't use default 3 seconds if function needs more time, but don't set unnecessarily high timeouts. + +## Performance Testing + +### Load Testing with Artillery +```yaml +config: + target: 'https://api.example.com' + phases: + - duration: 60 + arrivalRate: 10 + +scenarios: + - flow: + - get: + url: "/items" +``` + +### Monitor During Load Tests +- Watch CloudWatch metrics +- Check for throttling +- Monitor error rates +- Observe cold start impact diff --git a/serverless-dev/steering/sam-build-deploy.md b/serverless-dev/steering/sam-build-deploy.md new file mode 100644 index 0000000..d52fda9 --- /dev/null +++ b/serverless-dev/steering/sam-build-deploy.md @@ -0,0 +1,100 @@ +# SAM Build and Deploy Workflow + +## Building Applications + +### Basic Build +```bash +sam build +``` + +### Build with Container (Recommended for Dependencies) +```bash +sam build --use-container +``` + +### Build Specific Function +```bash +sam build MyFunction +``` + +## Deployment + +### Guided Deployment (First Time) +```bash +sam deploy --guided +``` + +This creates `samconfig.toml` with deployment settings. + +### Subsequent Deployments +```bash +sam deploy +``` + +### Deploy to Specific Environment +```bash +sam deploy --config-env prod +``` + +## samconfig.toml Structure + +```toml +version = 0.1 + +[default.deploy.parameters] +stack_name = "my-app" +s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket" +s3_prefix = "my-app" +region = "us-east-1" +capabilities = "CAPABILITY_IAM" +parameter_overrides = "Environment=dev" + +[prod.deploy.parameters] +stack_name = "my-app-prod" +region = "us-east-1" +capabilities = "CAPABILITY_IAM" +parameter_overrides = "Environment=prod" +``` + +## Deployment Best Practices + +### Use Stack Outputs +```yaml +Outputs: + ApiUrl: + Description: API Gateway endpoint URL + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + FunctionArn: + Description: Lambda Function ARN + Value: !GetAtt MyFunction.Arn +``` + +### Enable Rollback Configuration +```yaml +DeploymentPreference: + Type: Canary10Percent5Minutes + Alarms: + - !Ref CanaryErrorsAlarm +``` + +### Tag Resources +```yaml +Tags: + Environment: !Ref Environment + Application: my-app + ManagedBy: SAM +``` + +## Validation Before Deploy + +Always validate template: +```bash +sam validate +``` + +## Cleanup + +Delete stack when no longer needed: +```bash +sam delete +``` diff --git a/serverless-dev/steering/sam-init-workflow.md b/serverless-dev/steering/sam-init-workflow.md new file mode 100644 index 0000000..f70321b --- /dev/null +++ b/serverless-dev/steering/sam-init-workflow.md @@ -0,0 +1,72 @@ +# SAM Project Initialization + +## Creating New Projects + +Use `sam init` to bootstrap new serverless applications: + +```bash +sam init --runtime python3.13 --name hello-serverless --app-template hello-world --dependency-manager pip --no-interactive + +``` + +### Recommended Runtimes +- **Python**: python3.13, python3.12 +- **Node.js**: nodejs22.x, nodejs20.x +- **Java**: java21, java17 +- **.NET**: dotnet8 + +### Common Templates +- `hello-world` - Basic Lambda function with API Gateway +- `quick-start-web` - Web backend with API Gateway +- `step-functions-sample-app` - Step Functions workflow +- `eventbridge-schema-app` - EventBridge integration + +## Project Structure + +Standard SAM project layout: +``` +my-app/ +├── template.yaml # SAM template +├── samconfig.toml # SAM CLI configuration +├── src/ # Function code +│ └── handler.py +├── events/ # Test events +│ └── event.json +└── tests/ # Unit tests +``` + +## Template.yaml Best Practices + +### Use SAM Transform +```yaml +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: My serverless application +``` + +### Define Globals for Common Settings +```yaml +Globals: + Function: + Timeout: 30 + MemorySize: 512 + Runtime: python3.13 + Tracing: Active + Environment: + Variables: + LOG_LEVEL: INFO +``` + +### Use Parameters for Configuration +```yaml +Parameters: + Environment: + Type: String + Default: dev + AllowedValues: [dev, staging, prod] +``` + +## Naming Conventions +- Use descriptive, kebab-case names for resources +- Prefix resources with application name +- Include environment in resource names for multi-stage deployments diff --git a/serverless-dev/steering/security-iam.md b/serverless-dev/steering/security-iam.md new file mode 100644 index 0000000..81836e0 --- /dev/null +++ b/serverless-dev/steering/security-iam.md @@ -0,0 +1,255 @@ +# Security and IAM Best Practices + +## SAM Policy Templates + +Use built-in policy templates for common permissions: + +```yaml +MyFunction: + Type: AWS::Serverless::Function + Properties: + Policies: + # DynamoDB + - DynamoDBCrudPolicy: + TableName: !Ref MyTable + - DynamoDBReadPolicy: + TableName: !Ref MyTable + + # S3 + - S3ReadPolicy: + BucketName: !Ref MyBucket + - S3CrudPolicy: + BucketName: !Ref MyBucket + + # SQS + - SQSSendMessagePolicy: + QueueName: !GetAtt MyQueue.QueueName + - SQSPollerPolicy: + QueueName: !GetAtt MyQueue.QueueName + + # SNS + - SNSPublishMessagePolicy: + TopicName: !GetAtt MyTopic.TopicName + + # Secrets Manager + - AWSSecretsManagerGetSecretValuePolicy: + SecretArn: !Ref MySecret + + # CloudWatch Logs + - CloudWatchPutMetricPolicy: {} +``` + +## Custom IAM Policies + +```yaml +MyFunction: + Type: AWS::Serverless::Function + Properties: + Policies: + - Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - dynamodb:GetItem + - dynamodb:PutItem + Resource: !GetAtt MyTable.Arn + - Effect: Allow + Action: + - s3:GetObject + Resource: !Sub '${MyBucket.Arn}/*' +``` + +## Least Privilege Principle + +### Scope Permissions to Specific Resources +```yaml +Policies: + - Statement: + - Effect: Allow + Action: dynamodb:GetItem + Resource: !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/MyTable' +``` + +### Use Conditions +```yaml +Policies: + - Statement: + - Effect: Allow + Action: s3:GetObject + Resource: !Sub '${MyBucket.Arn}/*' + Condition: + StringLike: + s3:prefix: 'public/*' +``` + +## Secrets Management + +### AWS Secrets Manager +```yaml +MySecret: + Type: AWS::SecretsManager::Secret + Properties: + SecretString: !Sub | + { + "api_key": "${ApiKey}", + "db_password": "${DBPassword}" + } + +MyFunction: + Type: AWS::Serverless::Function + Properties: + Environment: + Variables: + SECRET_ARN: !Ref MySecret + Policies: + - AWSSecretsManagerGetSecretValuePolicy: + SecretArn: !Ref MySecret +``` + +Retrieve in code: +```python +import boto3 +import json + +secrets = boto3.client('secretsmanager') +response = secrets.get_secret_value(SecretId=os.environ['SECRET_ARN']) +secret = json.loads(response['SecretString']) +api_key = secret['api_key'] +``` + +### Parameter Store +```yaml +MyFunction: + Type: AWS::Serverless::Function + Properties: + Environment: + Variables: + PARAM_NAME: /myapp/config/api-key + Policies: + - SSMParameterReadPolicy: + ParameterName: myapp/config/* +``` + +## API Gateway Security + +### API Keys +```yaml +ServerlessRestApi: + Type: AWS::Serverless::Api + Properties: + Auth: + ApiKeyRequired: true + +ApiKey: + Type: AWS::ApiGateway::ApiKey + Properties: + Enabled: true + +UsagePlan: + Type: AWS::ApiGateway::UsagePlan + Properties: + ApiStages: + - ApiId: !Ref ServerlessRestApi + Stage: Prod +``` + +### Cognito Authorizer +```yaml +UserPool: + Type: AWS::Cognito::UserPool + +ServerlessRestApi: + Type: AWS::Serverless::Api + Properties: + Auth: + DefaultAuthorizer: CognitoAuth + Authorizers: + CognitoAuth: + UserPoolArn: !GetAtt UserPool.Arn +``` + +### Lambda Authorizer +```yaml +AuthFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: authorizer/ + Handler: app.handler + +ServerlessRestApi: + Type: AWS::Serverless::Api + Properties: + Auth: + DefaultAuthorizer: LambdaAuth + Authorizers: + LambdaAuth: + FunctionArn: !GetAtt AuthFunction.Arn +``` + +## Resource Policies + +### S3 Bucket Policy +```yaml +MyBucket: + Type: AWS::S3::Bucket + +BucketPolicy: + Type: AWS::S3::BucketPolicy + Properties: + Bucket: !Ref MyBucket + PolicyDocument: + Statement: + - Effect: Allow + Principal: + Service: lambda.amazonaws.com + Action: s3:GetObject + Resource: !Sub '${MyBucket.Arn}/*' +``` + +## Encryption + +### Enable Encryption at Rest +```yaml +MyTable: + Type: AWS::DynamoDB::Table + Properties: + SSESpecification: + SSEEnabled: true + SSEType: KMS + KMSMasterKeyId: !Ref MyKMSKey + +MyBucket: + Type: AWS::S3::Bucket + Properties: + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 +``` + +## VPC Configuration + +```yaml +MyFunction: + Type: AWS::Serverless::Function + Properties: + VpcConfig: + SecurityGroupIds: + - !Ref LambdaSecurityGroup + SubnetIds: + - !Ref PrivateSubnet1 + - !Ref PrivateSubnet2 +``` + +## Security Checklist + +- [ ] Use SAM policy templates for common permissions +- [ ] Apply least privilege to all IAM policies +- [ ] Store secrets in Secrets Manager or Parameter Store +- [ ] Enable encryption at rest for all data stores +- [ ] Use API Gateway authorizers for authentication +- [ ] Enable AWS X-Ray for security monitoring +- [ ] Implement input validation in Lambda functions +- [ ] Use VPC for functions accessing private resources +- [ ] Enable CloudTrail for audit logging +- [ ] Regularly rotate credentials and secrets