diff --git a/lambda-durable-functions-nodejs-calling-ecs/.gitignore b/lambda-durable-functions-nodejs-calling-ecs/.gitignore new file mode 100644 index 000000000..dc6e3ed0b --- /dev/null +++ b/lambda-durable-functions-nodejs-calling-ecs/.gitignore @@ -0,0 +1,26 @@ +# Node modules +node_modules/ +package-lock.json + +# SAM build artifacts +.aws-sam/ +samconfig.toml + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Test outputs +response.json +*.log + +# Environment +.env +.env.local diff --git a/lambda-durable-functions-nodejs-calling-ecs/README.md b/lambda-durable-functions-nodejs-calling-ecs/README.md new file mode 100644 index 000000000..b0bd956c7 --- /dev/null +++ b/lambda-durable-functions-nodejs-calling-ecs/README.md @@ -0,0 +1,213 @@ +# AWS Lambda durable functions with Amazon ECS Integration + +This pattern demonstrates how to use AWS Lambda durable functions to orchestrate long-running Amazon ECS Fargate tasks. The Lambda function can wait up to 24 hours for ECS task completion without incurring compute charges during the wait period. + +**Important:** Please check the [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html) for regions currently supported by AWS Lambda durable functions. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/lambda-durable-functions-nodejs-calling-ecs + +## Architecture + +![Architecture Diagram](architecture.png) + +The pattern uses Lambda durable functions with the callback pattern to orchestrate ECS Fargate tasks cost-effectively. + +### Workflow Steps + +1. **Lambda function invoked** with task parameters (message, processing time) +2. **Durable function creates callback ID** using `context.waitForCallback()` +3. **ECS Fargate task started** with callback ID passed as environment variable +4. **Lambda function pauses** (no compute charges during wait) +5. **ECS task processes workload** and logs progress to CloudWatch +6. **ECS task completes** (in production, would call `SendDurableExecutionCallbackSuccess`) +7. **Lambda function resumes** and returns result + +## Key Features + +- ✅ **24-Hour Wait Time** - Can wait up to 24 hours for ECS task completion +- ✅ **No Compute Charges During Wait** - Function suspended during wait period +- ✅ **Callback Pattern** - ECS tasks call Lambda APIs directly to resume execution +- ✅ **CloudWatch Logs** - Full visibility into both Lambda and ECS execution +- ✅ **Generic Container** - Uses public Python image, easily replaceable +- ✅ **Fargate Serverless** - No EC2 instances to manage + +## Prerequisites + +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) installed +* Node.js runtime (see [supported runtimes for durable functions](https://docs.aws.amazon.com/lambda/latest/dg/durable-supported-runtimes.html)) + +## Deployment + +1. Navigate to the pattern directory: + ```bash + cd lambda-durable-functions-nodejs-calling-ecs + ``` + +2. Build the SAM application: + ```bash + sam build + ``` + +3. Deploy the application: + ```bash + sam deploy --guided + ``` + + During the guided deployment: + - Accept default values or customize as needed + - Allow SAM CLI to create IAM roles when prompted + - Note the function name from the outputs + +4. Note the `CallbackFunctionName` from the CloudFormation outputs + +## Testing + +### Test the Callback Pattern + +Invoke the Lambda function with a test payload: + +```bash +aws lambda invoke \ + --function-name :prod \ + --invocation-type Event \ + --payload '{"message":"Test ECS task","processingTime":8}' \ + --cli-binary-format raw-in-base64-out \ + response.json +``` + +**Note:** A qualified ARN (version or alias) is required for durable functions. See [invoking durable functions](https://docs.aws.amazon.com/lambda/latest/dg/durable-invoking.html#durable-invoking-qualified-arns). + +### Monitor Execution + +Check Lambda logs: +```bash +aws logs tail /aws/lambda/ --since 2m --follow +``` + +Check ECS task logs: +```bash +aws logs tail /ecs/lambda-ecs-durable-demo --since 2m --follow +``` + +### Expected Output + +**Lambda Logs:** +``` +Starting Lambda durable function - Callback Pattern +Callback ID created: +Starting ECS task with callback ID... +ECS task started: arn:aws:ecs:... +``` + +**ECS Logs:** +``` +=== ECS Task Started === +Callback ID: +Message: Test ECS task +Processing Time: 8 seconds +Simulating work... +=== Task Completed Successfully === +Result: {"status":"completed","message":"Processed: Test ECS task"} +Note: In production, call Lambda SendDurableExecutionCallbackSuccess API here +``` + +## How It Works + +### Lambda durable function (Node.js) + +The Lambda function uses the `@aws/durable-execution-sdk-js` package: + +```javascript +const { withDurableExecution } = require('@aws/durable-execution-sdk-js'); + +exports.handler = withDurableExecution(async (event, context) => { + // Create callback and start ECS task + const result = await context.waitForCallback( + 'ecs-task-callback', + async (callbackId) => { + // Start ECS task with callback ID + const response = await ecs.send(new RunTaskCommand({ + // ... pass callbackId as environment variable + })); + }, + { timeout: { hours: 1 } } + ); + + return result; +}); +``` + +### ECS Task (Python) + +The ECS container receives the callback ID and processes the workload. In production, it would call the Lambda API: + +```bash +aws lambda send-durable-execution-callback-success \ + --callback-id $CALLBACK_ID \ + --result '{"status":"completed","data":"..."}' +``` + +### Key Configuration + +**Lambda Function:** +- Runtime: `nodejs22.x` (see [supported runtimes](https://docs.aws.amazon.com/lambda/latest/dg/durable-supported-runtimes.html)) +- `AutoPublishAlias: prod` +- `DurableConfig` with execution timeout and retention period + +**ECS Task:** +- Launch type: `FARGATE` +- Public subnet with `assignPublicIp: ENABLED` +- Container image: `public.ecr.aws/docker/library/python:3.12-alpine` +- CloudWatch Logs enabled + +## Customization + +### Replace the ECS Container + +The pattern uses a generic Python container for demonstration. To use your own container: + +1. Update the `Image` in the `ECSTaskDefinition` resource +2. Ensure your container: + - Reads the `CALLBACK_ID` environment variable + - Calls `aws lambda send-durable-execution-callback-success` on completion + - Calls `aws lambda send-durable-execution-callback-failure` on error + +### Adjust Timeouts + +Modify the durable function timeout in `template.yaml`: + +```yaml +DurableConfig: + ExecutionTimeout: 86400 # 24 hours in seconds + RetentionPeriodInDays: 7 +``` + +And the callback timeout in the handler: + +```javascript +context.waitForCallback('ecs-task-callback', async (callbackId) => { + // ... +}, { + timeout: { hours: 24 }, // Maximum wait time + heartbeatTimeout: { minutes: 5 } // Optional heartbeat +}) +``` + +## Cleanup + +Delete the stack: + +```bash +sam delete +``` + +## Additional Resources + +- [AWS Lambda durable functions Documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html) +- [Amazon ECS on AWS Fargate](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html) +- [AWS SAM Documentation](https://docs.aws.amazon.com/serverless-application-model/) + +--- + +© 2026 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. diff --git a/lambda-durable-functions-nodejs-calling-ecs/example-pattern.json b/lambda-durable-functions-nodejs-calling-ecs/example-pattern.json new file mode 100644 index 000000000..ef35632e4 --- /dev/null +++ b/lambda-durable-functions-nodejs-calling-ecs/example-pattern.json @@ -0,0 +1,68 @@ +{ + "title": "AWS Lambda durable functions in NodeJS calling Amazon ECS", + "description": "Orchestrate long-running ECS Fargate tasks using Lambda durable functions with callback pattern", + "language": "Node.js", + "level": "200", + "framework": "AWS SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern demonstrates how to use AWS Lambda durable functions to orchestrate Amazon ECS Fargate tasks that can run for up to 24 hours.", + "The Lambda function creates a callback ID, starts an ECS task with that ID, and then automatically suspends execution without incurring compute charges during the wait period.", + "When the ECS task completes, it calls the Lambda SendDurableExecutionCallbackSuccess API to resume the durable function and return results.", + "This pattern is ideal for long-running batch jobs, data processing, ML training, or any workload that exceeds Lambda's 15-minute timeout." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-durable-functions-nodejs-calling-ecs", + "templateURL": "serverless-patterns/lambda-durable-functions-nodejs-calling-ecs", + "projectFolder": "lambda-durable-functions-nodejs-calling-ecs", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "AWS Lambda durable functions Documentation", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html" + }, + { + "text": "Amazon ECS on AWS Fargate", + "link": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html" + }, + { + "text": "Lambda Durable Execution SDK for JavaScript", + "link": "https://www.npmjs.com/package/@aws/durable-execution-sdk-js" + }, + { + "text": "SendDurableExecutionCallbackSuccess API", + "link": "https://docs.aws.amazon.com/lambda/latest/api/API_SendDurableExecutionCallbackSuccess.html" + } + ] + }, + "deploy": { + "text": [ + "sam build", + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "sam delete" + ] + }, + "authors": [ + { + "name": "Surya Sai D", + "image": "", + "bio": "Surya works as a Technical Account Manager at AWS. He is an expert in Serverless frameworks and Event Driven Architectures. Surya is also passionate on technical writing and has contributed to AWS blogs and other Open Source Content.", + "linkedin": "surya-sai-d-64920416a" + } + ] +} diff --git a/lambda-durable-functions-nodejs-calling-ecs/lambda-durable-functions-nodejs-calling-ecs.json b/lambda-durable-functions-nodejs-calling-ecs/lambda-durable-functions-nodejs-calling-ecs.json new file mode 100644 index 000000000..68b0c87d7 --- /dev/null +++ b/lambda-durable-functions-nodejs-calling-ecs/lambda-durable-functions-nodejs-calling-ecs.json @@ -0,0 +1,87 @@ +{ + "title": "AWS Lambda durable functions in NodeJS calling Amazon ECS", + "description": "Orchestrate long-running ECS Fargate tasks using Lambda durable functions with callback pattern", + "language": "Node.js", + "level": "200", + "framework": "AWS SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern demonstrates how to use AWS Lambda durable functions to orchestrate Amazon ECS Fargate tasks that can run for up to 24 hours.", + "The Lambda function creates a callback ID, starts an ECS task with that ID, and then automatically suspends execution without incurring compute charges during the wait period.", + "When the ECS task completes, it calls the Lambda SendDurableExecutionCallbackSuccess API to resume the durable function and return results.", + "This pattern is ideal for long-running batch jobs, data processing, ML training, or any workload that exceeds Lambda's 15-minute timeout." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-durable-functions-nodejs-calling-ecs", + "templateURL": "serverless-patterns/lambda-durable-functions-nodejs-calling-ecs", + "projectFolder": "lambda-durable-functions-nodejs-calling-ecs", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "AWS Lambda durable functions Documentation", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html" + }, + { + "text": "Amazon ECS on AWS Fargate", + "link": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html" + }, + { + "text": "Lambda Durable Execution SDK for JavaScript", + "link": "https://www.npmjs.com/package/@aws/durable-execution-sdk-js" + }, + { + "text": "SendDurableExecutionCallbackSuccess API", + "link": "https://docs.aws.amazon.com/lambda/latest/api/API_SendDurableExecutionCallbackSuccess.html" + } + ] + }, + "deploy": { + "text": [ + "sam build", + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "sam delete" + ] + }, + "authors": [ + { + "name": "Surya Sai D", + "image": "", + "bio": "Surya works as a Technical Account Manager at AWS. He is an expert in Serverless frameworks and Event Driven Architectures. Surya is also passionate on technical writing and has contributed to AWS blogs and other Open Source Content.", + "linkedin": "surya-sai-d-64920416a" + } + ], + "patternArch": { + "icon1": { + "x": 20, + "y": 50, + "service": "lambda", + "label": "AWS Lambda durable functions" + }, + "icon2": { + "x": 80, + "y": 50, + "service": "ecs", + "label": "Amazon ESC task" + }, + "line1": { + "from": "icon1", + "to": "icon2", + "label": "wait for callback" + } + } +} diff --git a/lambda-durable-functions-nodejs-calling-ecs/src/callback-handler.js b/lambda-durable-functions-nodejs-calling-ecs/src/callback-handler.js new file mode 100644 index 000000000..8704e4785 --- /dev/null +++ b/lambda-durable-functions-nodejs-calling-ecs/src/callback-handler.js @@ -0,0 +1,95 @@ +const { ECSClient, RunTaskCommand } = require('@aws-sdk/client-ecs'); +const { withDurableExecution } = require('@aws/durable-execution-sdk-js'); + +const ecs = new ECSClient({ region: process.env.AWS_REGION }); + +/** + * Lambda durable function - Callback Pattern + * + * This function demonstrates the callback pattern where: + * 1. Lambda creates a callback and gets a callback ID + * 2. Lambda starts an ECS task and passes the callback ID + * 3. Lambda waits for the callback (suspends without charges) + * 4. ECS task calls Lambda APIs directly to send success/failure + * 5. Lambda resumes and returns the result + */ +exports.handler = withDurableExecution(async (event, context) => { + context.logger.info('Starting Lambda durable function - Callback Pattern'); + context.logger.info('Event:', JSON.stringify(event)); + + const message = event.message || 'Hello from Lambda durable function'; + const processingTime = event.processingTime || 10; + + try { + // Use waitForCallback to create callback and start ECS task + const result = await context.waitForCallback( + 'ecs-task-callback', + async (callbackId) => { + context.logger.info('Callback ID created:', callbackId); + context.logger.info('Starting ECS task with callback ID...'); + + // Start ECS task with callback ID as environment variable + const runTaskParams = { + cluster: process.env.ECS_CLUSTER, + taskDefinition: process.env.ECS_TASK_DEFINITION, + launchType: 'FARGATE', + networkConfiguration: { + awsvpcConfiguration: { + subnets: process.env.ECS_SUBNETS.split(','), + securityGroups: [process.env.ECS_SECURITY_GROUP], + assignPublicIp: 'ENABLED' + } + }, + overrides: { + containerOverrides: [ + { + name: 'worker', + environment: [ + { name: 'CALLBACK_ID', value: callbackId }, + { name: 'MESSAGE', value: message }, + { name: 'PROCESSING_TIME', value: processingTime.toString() } + ] + } + ] + } + }; + + const response = await ecs.send(new RunTaskCommand(runTaskParams)); + + if (!response.tasks || response.tasks.length === 0) { + throw new Error('Failed to start ECS task'); + } + + const taskArn = response.tasks[0].taskArn; + context.logger.info('ECS task started:', taskArn); + }, + { + timeout: { hours: 1 }, // Wait up to 1 hour for callback + heartbeatTimeout: { minutes: 5 } // Expect heartbeat every 5 minutes + } + ); + + context.logger.info('Callback received with result:', result); + + return { + statusCode: 200, + body: JSON.stringify({ + message: 'ECS task completed successfully', + result: result, + pattern: 'callback' + }) + }; + + } catch (error) { + context.logger.error('Error in durable function:', error); + + return { + statusCode: 500, + body: JSON.stringify({ + message: 'ECS task failed', + error: error.message, + pattern: 'callback' + }) + }; + } +}); diff --git a/lambda-durable-functions-nodejs-calling-ecs/src/package.json b/lambda-durable-functions-nodejs-calling-ecs/src/package.json new file mode 100644 index 000000000..e0824d266 --- /dev/null +++ b/lambda-durable-functions-nodejs-calling-ecs/src/package.json @@ -0,0 +1,23 @@ +{ + "name": "lambda-ecs-durable-nodejs", + "version": "1.0.0", + "description": "Lambda durable functions calling ECS with Node.js", + "main": "callback-handler.js", + "dependencies": { + "@aws-sdk/client-ecs": "^3.700.0", + "@aws-sdk/client-lambda": "^3.700.0", + "@aws/durable-execution-sdk-js": "^1.0.0" + }, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "lambda", + "durable", + "ecs", + "nodejs" + ], + "author": "", + "license": "MIT-0" +} diff --git a/lambda-durable-functions-nodejs-calling-ecs/template.yaml b/lambda-durable-functions-nodejs-calling-ecs/template.yaml new file mode 100644 index 000000000..077193bb7 --- /dev/null +++ b/lambda-durable-functions-nodejs-calling-ecs/template.yaml @@ -0,0 +1,271 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + Lambda durable functions to ECS with Node.js - Callback Pattern + + This pattern demonstrates Lambda durable functions invoking ECS tasks using the callback pattern. + The ECS task directly calls Lambda APIs (SendDurableExecutionCallbackSuccess/Failure) instead of using DynamoDB. + +Parameters: + VpcCIDR: + Type: String + Default: 10.0.0.0/16 + Description: CIDR block for VPC + +Globals: + Function: + Timeout: 900 + MemorySize: 512 + Runtime: nodejs22.x + Architectures: + - arm64 + +Resources: + # VPC Configuration + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: !Ref VpcCIDR + EnableDnsHostnames: true + EnableDnsSupport: true + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-vpc + + PublicSubnet1: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: !Select [0, !Cidr [!Ref VpcCIDR, 4, 8]] + AvailabilityZone: !Select [0, !GetAZs ''] + MapPublicIpOnLaunch: true + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-public-1 + + PublicSubnet2: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: !Select [1, !Cidr [!Ref VpcCIDR, 4, 8]] + AvailabilityZone: !Select [1, !GetAZs ''] + MapPublicIpOnLaunch: true + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-public-2 + + InternetGateway: + Type: AWS::EC2::InternetGateway + Properties: + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-igw + + AttachGateway: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + VpcId: !Ref VPC + InternetGatewayId: !Ref InternetGateway + + PublicRouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-public-rt + + PublicRoute: + Type: AWS::EC2::Route + DependsOn: AttachGateway + Properties: + RouteTableId: !Ref PublicRouteTable + DestinationCidrBlock: 0.0.0.0/0 + GatewayId: !Ref InternetGateway + + SubnetRouteTableAssociation1: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PublicSubnet1 + RouteTableId: !Ref PublicRouteTable + + SubnetRouteTableAssociation2: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PublicSubnet2 + RouteTableId: !Ref PublicRouteTable + + ECSSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security group for ECS tasks + VpcId: !Ref VPC + SecurityGroupEgress: + - IpProtocol: -1 + CidrIp: 0.0.0.0/0 + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-ecs-sg + + # ECS Cluster + ECSCluster: + Type: AWS::ECS::Cluster + Properties: + ClusterName: !Sub ${AWS::StackName}-cluster + ClusterSettings: + - Name: containerInsights + Value: enabled + + # CloudWatch Log Group + ECSLogGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: !Sub /ecs/${AWS::StackName} + RetentionInDays: 7 + + # ECS Task Execution Role + ECSTaskExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: ecs-tasks.amazonaws.com + Action: sts:AssumeRole + ManagedPolicyArns: + - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy + Policies: + - PolicyName: CloudWatchLogs + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - logs:CreateLogStream + - logs:PutLogEvents + Resource: !GetAtt ECSLogGroup.Arn + + # ECS Task Role (for Lambda callback APIs) + ECSTaskRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: ecs-tasks.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: LambdaCallbackPolicy + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - lambda:SendDurableExecutionCallbackSuccess + - lambda:SendDurableExecutionCallbackFailure + - lambda:SendDurableExecutionCallbackHeartbeat + Resource: '*' + + # ECS Task Definition with Inline Node.js Code + ECSTaskDefinition: + Type: AWS::ECS::TaskDefinition + Properties: + Family: !Sub ${AWS::StackName}-task + NetworkMode: awsvpc + RequiresCompatibilities: + - FARGATE + Cpu: '256' + Memory: '512' + ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn + TaskRoleArn: !GetAtt ECSTaskRole.Arn + ContainerDefinitions: + - Name: worker + Image: public.ecr.aws/docker/library/python:3.12-alpine + Essential: true + EntryPoint: ["/bin/sh", "-c"] + Command: + - | + python3 -u -c " + import os, time, sys + print('=== ECS Task Started ===', flush=True) + print(f'Callback ID: {os.environ.get(\"CALLBACK_ID\", \"N/A\")}', flush=True) + print(f'Message: {os.environ.get(\"MESSAGE\", \"N/A\")}', flush=True) + print(f'Processing Time: {os.environ.get(\"PROCESSING_TIME\", \"10\")} seconds', flush=True) + print('', flush=True) + print('Simulating work...', flush=True) + time.sleep(int(os.environ.get('PROCESSING_TIME', '10'))) + print('', flush=True) + print('=== Task Completed Successfully ===', flush=True) + print('Result: {\"status\":\"completed\",\"message\":\"Processed: ' + os.environ.get('MESSAGE', '') + '\"}', flush=True) + print('', flush=True) + print('Note: In production, call Lambda SendDurableExecutionCallbackSuccess API here', flush=True) + print('Command: aws lambda send-durable-execution-callback-success --callback-id $CALLBACK_ID --result {...}', flush=True) + sys.exit(0) + " + LogConfiguration: + LogDriver: awslogs + Options: + awslogs-group: !Ref ECSLogGroup + awslogs-region: !Ref AWS::Region + awslogs-stream-prefix: ecs + + # Lambda durable function - Callback Pattern + CallbackLambdaFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: callback-handler.handler + Environment: + Variables: + ECS_CLUSTER: !Ref ECSCluster + ECS_TASK_DEFINITION: !Ref ECSTaskDefinition + ECS_SUBNETS: !Sub ${PublicSubnet1},${PublicSubnet2} + ECS_SECURITY_GROUP: !Ref ECSSecurityGroup + Policies: + - Statement: + - Effect: Allow + Action: + - ecs:RunTask + - ecs:DescribeTasks + Resource: !Sub 'arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:*' + - Effect: Allow + Action: + - iam:PassRole + Resource: + - !GetAtt ECSTaskExecutionRole.Arn + - !GetAtt ECSTaskRole.Arn + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*' + AutoPublishAlias: prod + DurableConfig: + ExecutionTimeout: 86400 + RetentionPeriodInDays: 7 + +Outputs: + CallbackLambdaFunctionArn: + Description: ARN of the Callback Lambda durable function + Value: !GetAtt CallbackLambdaFunction.Arn + + ECSClusterName: + Description: Name of the ECS Cluster + Value: !Ref ECSCluster + + ECSTaskDefinitionArn: + Description: ARN of the ECS Task Definition + Value: !Ref ECSTaskDefinition + + LogGroupName: + Description: CloudWatch Log Group for ECS tasks + Value: !Ref ECSLogGroup + + VPCId: + Description: VPC ID + Value: !Ref VPC