From dbb0c7918dcdb2654cc2baa9be695fde4d46a154 Mon Sep 17 00:00:00 2001 From: Theophilus Ajayi Date: Mon, 15 Dec 2025 14:23:41 +0100 Subject: [PATCH 1/2] Lambda Managed Instances CDK Typescript --- lambda-managed-instances-cdk-ts/.gitignore | 65 ++++++ lambda-managed-instances-cdk-ts/README.md | 131 ++++++++++++ .../bin/lambda-managed-instances-cdk-ts.ts | 21 ++ lambda-managed-instances-cdk-ts/cdk.json | 65 ++++++ .../example-pattern.json | 63 ++++++ .../instructions.txt | 193 ++++++++++++++++++ .../lib/lambda-managed-instances-stack.ts | 142 +++++++++++++ lambda-managed-instances-cdk-ts/package.json | 30 +++ .../test-function.sh | 45 ++++ lambda-managed-instances-cdk-ts/tsconfig.json | 30 +++ 10 files changed, 785 insertions(+) create mode 100644 lambda-managed-instances-cdk-ts/.gitignore create mode 100644 lambda-managed-instances-cdk-ts/README.md create mode 100644 lambda-managed-instances-cdk-ts/bin/lambda-managed-instances-cdk-ts.ts create mode 100644 lambda-managed-instances-cdk-ts/cdk.json create mode 100644 lambda-managed-instances-cdk-ts/example-pattern.json create mode 100644 lambda-managed-instances-cdk-ts/instructions.txt create mode 100644 lambda-managed-instances-cdk-ts/lib/lambda-managed-instances-stack.ts create mode 100644 lambda-managed-instances-cdk-ts/package.json create mode 100755 lambda-managed-instances-cdk-ts/test-function.sh create mode 100644 lambda-managed-instances-cdk-ts/tsconfig.json diff --git a/lambda-managed-instances-cdk-ts/.gitignore b/lambda-managed-instances-cdk-ts/.gitignore new file mode 100644 index 000000000..e8bbe777c --- /dev/null +++ b/lambda-managed-instances-cdk-ts/.gitignore @@ -0,0 +1,65 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out + +# Parcel default cache directory +.parcel-cache + +# npm +npm-debug.log* +.npm + +# Yarn +yarn-error.log + +# IDEs +.vscode/ +.idea/ + +# OS +.DS_Store +Thumbs.db + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Dependency directories +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# AWS SAM +.aws-sam/ \ No newline at end of file diff --git a/lambda-managed-instances-cdk-ts/README.md b/lambda-managed-instances-cdk-ts/README.md new file mode 100644 index 000000000..18b8ed076 --- /dev/null +++ b/lambda-managed-instances-cdk-ts/README.md @@ -0,0 +1,131 @@ +# Lambda Managed Instances with AWS CDK TypeScript + +This pattern demonstrates how to create and deploy AWS Lambda Managed Instances using AWS CDK in TypeScript. Lambda Managed Instances allow you to run Lambda functions on dedicated EC2 instances for workloads that require more control over the underlying infrastructure. + +Learn more about this pattern at Serverless Land Patterns: [Lambda Managed Instances](https://serverlessland.com/patterns/lambda-managed-instances) + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Node.js](https://nodejs.org/) (version 18.x or later) +* [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html) installed (`npm install -g aws-cdk`) +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) + +## Architecture + +This CDK stack creates: + +1. **IAM Roles**: + - Lambda execution role with basic execution permissions + - Capacity provider operator role for managing EC2 instances + +2. **VPC Resources**: + - New VPC with CIDR 10.0.0.0/16 + - Private subnet with NAT Gateway for outbound internet access + - Security group for Lambda Managed Instances + +3. **Lambda Capacity Provider**: + - Manages EC2 instances (x86_64 architecture) + - Maximum 30 vCPUs scaling configuration + +4. **Lambda Function**: + - Node.js 20.x runtime + - 2048 MB memory allocation + - 512 MB ephemeral storage + - Configured to use the managed instances capacity provider + +## Deployment Instructions + +1. Clone this repository and navigate to the pattern directory: + ```bash + cd lambda-managed-instances-cdk-ts + ``` + +2. Install dependencies: + ```bash + npm install + ``` + +3. Build the TypeScript code: + ```bash + npm run build + ``` + +4. Bootstrap CDK (if you haven't done this before in your account/region): + ```bash + cdk bootstrap + ``` + +5. Deploy the stack: + ```bash + cdk deploy + ``` + +6. Note the outputs from the CDK deployment process. These contain the resource names and ARNs which are used for testing. + +## How it works + +1. **Infrastructure Setup**: The CDK creates all necessary infrastructure including VPC, subnets, security groups, and IAM roles. + +2. **Capacity Provider**: A Lambda capacity provider is created that manages EC2 instances in your VPC. This provider can scale up to 30 vCPUs based on demand. + +3. **Lambda Function**: The Lambda function is configured to use the managed instances capacity provider instead of the standard serverless execution environment. + +4. **Function Execution**: When invoked, the Lambda function runs on dedicated EC2 instances managed by the capacity provider, providing more control over the execution environment. + +## Testing + +1. Test the Lambda function using the AWS CLI: + ```bash + aws lambda invoke \ + --function-name my-managed-instance-function \ + --payload '{"test": "data"}' \ + response.json + ``` + +2. Check the response: + ```bash + cat response.json + ``` + +3. You should see a response like: + ```json + { + "statusCode": 200, + "body": "{\"message\":\"Hello from Lambda Managed Instances!\",\"event\":{\"test\":\"data\"}}" + } + ``` + +4. Monitor the function execution in CloudWatch Logs to see the detailed execution logs. + +## Useful CDK Commands + +* `npm run build` - compile typescript to js +* `npm run watch` - watch for changes and compile +* `cdk deploy` - deploy this stack to your default AWS account/region +* `cdk diff` - compare deployed stack with current state +* `cdk synth` - emits the synthesized CloudFormation template +* `cdk destroy` - delete the stack + +## Cleanup + +1. Delete the stack: + ```bash + cdk destroy + ``` + +2. Confirm when prompted to delete the stack and all its resources. + +## Notes + +- Lambda Managed Instances require VPC configuration and have different networking requirements compared to standard Lambda functions. +- The capacity provider manages EC2 instances automatically, scaling based on function invocation demand. +- This pattern is suitable for workloads that need more control over the execution environment or have specific networking requirements. + +---- +Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/lambda-managed-instances-cdk-ts/bin/lambda-managed-instances-cdk-ts.ts b/lambda-managed-instances-cdk-ts/bin/lambda-managed-instances-cdk-ts.ts new file mode 100644 index 000000000..8af78045b --- /dev/null +++ b/lambda-managed-instances-cdk-ts/bin/lambda-managed-instances-cdk-ts.ts @@ -0,0 +1,21 @@ +#!/usr/bin/env node +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import { LambdaManagedInstancesStack } from '../lib/lambda-managed-instances-stack'; + +const app = new cdk.App(); +new LambdaManagedInstancesStack(app, 'LambdaManagedInstancesStack', { + /* If you don't specify 'env', this stack will be environment-agnostic. + * Account/Region-dependent features and context lookups will not work, + * but a single synthesized template can be deployed anywhere. */ + + /* Uncomment the next line to specialize this stack for the AWS Account + * and Region that are implied by the current CLI configuration. */ + // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, + + /* Uncomment the next line if you know exactly what Account and Region you + * want to deploy the stack to. */ + // env: { account: '123456789012', region: 'us-east-1' }, + + /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ +}); \ No newline at end of file diff --git a/lambda-managed-instances-cdk-ts/cdk.json b/lambda-managed-instances-cdk-ts/cdk.json new file mode 100644 index 000000000..dbbb8fc88 --- /dev/null +++ b/lambda-managed-instances-cdk-ts/cdk.json @@ -0,0 +1,65 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/lambda-managed-instances-cdk-ts.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/core:bootstrapQualifier": "simple", + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableLogging": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForSourceAction": true + } +} \ No newline at end of file diff --git a/lambda-managed-instances-cdk-ts/example-pattern.json b/lambda-managed-instances-cdk-ts/example-pattern.json new file mode 100644 index 000000000..868998781 --- /dev/null +++ b/lambda-managed-instances-cdk-ts/example-pattern.json @@ -0,0 +1,63 @@ +{ + "title": "Lambda Managed Instances with CDK TypeScript", + "description": "Create AWS Lambda Managed Instances using AWS CDK in TypeScript.", + "language": "TypeScript", + "level": "300", + "framework": "AWS CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to create and deploy AWS Lambda Managed Instances using AWS CDK in TypeScript. Lambda Managed Instances allow you to run Lambda functions on dedicated EC2 instances for workloads that require more control over the underlying infrastructure.", + "The pattern creates all necessary infrastructure including VPC, subnets, security groups, IAM roles, and a Lambda capacity provider that manages EC2 instances. The Lambda function is configured to use the managed instances capacity provider instead of the standard serverless execution environment.", + "This pattern deploys a VPC with public and private subnets, NAT Gateway, Lambda capacity provider, Lambda function with Node.js runtime, and all required IAM roles and policies." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-managed-instances-cdk-ts", + "templateURL": "serverless-patterns/lambda-managed-instances-cdk-ts", + "projectFolder": "lambda-managed-instances-cdk-ts", + "templateFile": "lambda-managed-instances-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "Lambda Managed Instances Documentation", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/managed-instances.html" + }, + { + "text": "AWS Lambda Developer Guide", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/" + }, + { + "text": "AWS CDK TypeScript Reference", + "link": "https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda-readme.html" + } + ] + }, + "deploy": { + "text": [ + "cdk deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk destroy." + ] + }, + "authors": [ + { + "name": "AWS Serverless Patterns", + "image": "https://serverlessland.com/assets/images/logos/serverless-land-logo.png", + "bio": "AWS Serverless Patterns Collection", + "linkedin": "", + "twitter": "AWSOpen" + } + ] +} diff --git a/lambda-managed-instances-cdk-ts/instructions.txt b/lambda-managed-instances-cdk-ts/instructions.txt new file mode 100644 index 000000000..248a716b1 --- /dev/null +++ b/lambda-managed-instances-cdk-ts/instructions.txt @@ -0,0 +1,193 @@ +Creating a Lambda Managed Instance function (AWS CLI) + +Prerequisites +Before you begin, make sure you have the following: + +AWS CLI – Install and configure the AWS CLI. For more information, see Installing or updating the latest version of the AWS CLI. + +IAM permissions – Your IAM user or role must have permissions to create Lambda functions, capacity providers, and pass IAM roles. Note that you'll also need iam:CreateServiceLinkedRole if it's the first time creating a capacity provider in the account or if the Service Linked Role (SLR) was deleted. + +Step 1: Create the required IAM roles +Lambda Managed Instances require two IAM roles: an execution role for your function and an operator role for the capacity provider. The operator role allows Lambda to launch, terminate, and monitor Amazon EC2 instances on your behalf. The function execution role grants the function permissions to access other AWS services and resources. + +To create the Lambda execution role + +Create a trust policy document that allows Lambda to assume the role: + + +cat > lambda-trust-policy.json << 'EOF' +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} +EOF +Create the execution role: + + +aws iam create-role \ + --role-name MyLambdaExecutionRole \ + --assume-role-policy-document file://lambda-trust-policy.json +Attach the basic execution policy: + + +aws iam attach-role-policy \ + --role-name MyLambdaExecutionRole \ + --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole +To create the capacity provider operator role + +Create a trust policy document that allows Lambda to assume the operator role: + + +cat > operator-trust-policy.json << 'EOF' +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} +EOF +Create the operator role: + + +aws iam create-role \ + --role-name MyCapacityProviderOperatorRole \ + --assume-role-policy-document file://operator-trust-policy.json +Attach the required EC2 permissions policy: + + +aws iam attach-role-policy \ + --role-name MyCapacityProviderOperatorRole \ + --policy-arn arn:aws:iam::aws:policy/AWSLambdaManagedEC2ResourceOperator +Step 2: Set up VPC resources +Lambda Managed Instances run in your VPC and require a subnet and security group. + +To create VPC resources + +Create a VPC: + + +VPC_ID=$(aws ec2 create-vpc \ + --cidr-block 10.0.0.0/16 \ + --query 'Vpc.VpcId' \ + --output text) +Create a subnet: + + +SUBNET_ID=$(aws ec2 create-subnet \ + --vpc-id $VPC_ID \ + --cidr-block 10.0.1.0/24 \ + --query 'Subnet.SubnetId' \ + --output text) +Create a security group: + + +SECURITY_GROUP_ID=$(aws ec2 create-security-group \ + --group-name my-capacity-provider-sg \ + --description "Security group for Lambda Managed Instances" \ + --vpc-id $VPC_ID \ + --query 'GroupId' \ + --output text) +Note: Your Lambda Managed Instances functions require VPC configuration to access resources outside the VPC and to transmit telemetry data to CloudWatch Logs and X-Ray. For configuration details, see Networking for Lambda Managed Instances. + +Step 3: Create a capacity provider +A capacity provider manages the EC2 instances that run your Lambda functions. + +To create a capacity provider + + +ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) + +aws lambda create-capacity-provider \ + --capacity-provider-name my-capacity-provider \ + --vpc-config SubnetIds=[$SUBNET_ID],SecurityGroupIds=[$SECURITY_GROUP_ID] \ + --permissions-config CapacityProviderOperatorRoleArn=arn:aws:iam::${ACCOUNT_ID}:role/MyCapacityProviderOperatorRole \ + --instance-requirements Architectures=[x86_64] \ + --capacity-provider-scaling-config MaxVCpuCount=30 +This command creates a capacity provider with the following configuration: + +VPC configuration – Specifies the subnet and security group for the EC2 instances + +Permissions – Defines the IAM role that Lambda uses to manage EC2 instances + +Instance requirements – Specifies the x86_64 architecture + +Scaling configuration – Sets a maximum of 30 vCPUs for the capacity provider + +Step 4: Create a Lambda function with inline code +To create a function with inline code + +First, create a simple nodejs function and package it inline: + + +# Create a temporary directory for the function code +mkdir -p /tmp/my-lambda-function +cd /tmp/my-lambda-function + +# Create a simple nodejs handler +cat > lambda_function.py << 'EOF' +import json + +def lambda_handler(event, context): + return { + 'statusCode': 200, + 'body': json.dumps({ + 'message': 'Hello from Lambda Managed Instances!', + 'event': event + }) + } +EOF + +# Create a ZIP file +zip function.zip lambda_function.py +Create the Lambda function using the inline ZIP file: + + +ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) +REGION=$(aws configure get region) + +aws lambda create-function \ + --function-name my-managed-instance-function \ + --package-type Zip \ + --runtime {use latest nodejs runtime} \ + --handler lambda_function.lambda_handler \ + --zip-file fileb:///tmp/my-lambda-function/function.zip \ + --role arn:aws:iam::${ACCOUNT_ID}:role/MyLambdaExecutionRole \ + --architectures x86_64 \ + --memory-size 2048 \ + --ephemeral-storage Size=512 \ + --capacity-provider-config LambdaManagedInstancesCapacityProviderConfig={CapacityProviderArn=arn:aws:lambda:${REGION}:${ACCOUNT_ID}:capacity-provider:my-capacity-provider} +The function is created with: + +Runtime – use latest nodejs runtime + +Handler – The lambda_handler function in lambda_function.py + +Memory – 2048 MB + +Ephemeral storage – 512 MB + +Capacity provider – Links to the capacity provider you created + +Step 5: Publish a function version +To run your function on Lambda Managed Instances, you must publish a version. + +To publish a function version + + +aws lambda publish-version \ + --function-name my-managed-instance-function +This command publishes version 1 of your function and deploys it to the capacity provider. \ No newline at end of file diff --git a/lambda-managed-instances-cdk-ts/lib/lambda-managed-instances-stack.ts b/lambda-managed-instances-cdk-ts/lib/lambda-managed-instances-stack.ts new file mode 100644 index 000000000..3bf2608e9 --- /dev/null +++ b/lambda-managed-instances-cdk-ts/lib/lambda-managed-instances-stack.ts @@ -0,0 +1,142 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as path from 'path'; + +export class LambdaManagedInstancesStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // Step 1: Create the required IAM roles (following instructions exactly) + + // Lambda execution role + const lambdaExecutionRole = new iam.Role(this, 'LambdaExecutionRole', { + roleName: 'MyLambdaExecutionRole', + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + managedPolicies: [ + iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole') + ] + }); + + // Capacity provider operator role + const capacityProviderOperatorRole = new iam.Role(this, 'CapacityProviderOperatorRole', { + roleName: 'MyCapacityProviderOperatorRole', + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + managedPolicies: [ + iam.ManagedPolicy.fromAwsManagedPolicyName('AWSLambdaManagedEC2ResourceOperator') + ] + }); + + // Step 2: Set up VPC resources (following instructions exactly) + + // Create VPC with CIDR 10.0.0.0/16 + const vpc = new ec2.Vpc(this, 'LambdaManagedInstancesVpc', { + ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'), + maxAzs: 1, // Instructions show single subnet + subnetConfiguration: [ + { + cidrMask: 24, + name: 'lambda-subnet', + subnetType: ec2.SubnetType.PUBLIC, // Instructions create in public for simplicity + } + ], + natGateways: 0 // No NAT needed for public subnet + }); + + // Create security group (following instructions exactly) + const securityGroup = new ec2.SecurityGroup(this, 'LambdaManagedInstancesSecurityGroup', { + vpc: vpc, + description: 'Security group for Lambda Managed Instances', + securityGroupName: 'my-capacity-provider-sg' + }); + + // Step 3: Create capacity provider (using native CDK L1 construct) + const capacityProvider = new lambda.CfnCapacityProvider(this, 'CapacityProvider', { + capacityProviderName: 'my-capacity-provider', + vpcConfig: { + subnetIds: [vpc.publicSubnets[0].subnetId], + securityGroupIds: [securityGroup.securityGroupId] + }, + permissionsConfig: { + capacityProviderOperatorRoleArn: capacityProviderOperatorRole.roleArn + }, + instanceRequirements: { + architectures: ['x86_64'] + }, + capacityProviderScalingConfig: { + maxVCpuCount: 30 + } + }); + + // Step 4: Create Lambda function with managed instances + const managedInstanceFunction = new lambda.CfnFunction(this, 'ManagedInstanceFunction', { + functionName: 'my-managed-instance-function', + runtime: 'nodejs24.x', // Using nodejs24.x as requested + handler: 'index.handler', + code: { + zipFile: ` +exports.handler = async (event, context) => { + console.log('Event:', JSON.stringify(event, null, 2)); + + return { + statusCode: 200, + body: JSON.stringify({ + message: 'Hello from Lambda Managed Instances!', + event: event + }) + }; +};` + }, + role: lambdaExecutionRole.roleArn, + architectures: ['x86_64'], + memorySize: 2048, + ephemeralStorage: { + size: 512 + }, + capacityProviderConfig: { + lambdaManagedInstancesCapacityProviderConfig: { + capacityProviderArn: capacityProvider.capacityProviderRef.capacityProviderArn + } + } + }); + + // Step 5: Publish function version (following instructions exactly) + const functionVersion = new lambda.CfnVersion(this, 'ManagedInstanceFunctionVersion', { + functionName: managedInstanceFunction.ref, + description: 'Version 1 of Lambda Managed Instance function' + }); + + // Outputs (matching the instructions) + new cdk.CfnOutput(this, 'VpcId', { + value: vpc.vpcId, + description: 'VPC ID for Lambda Managed Instances' + }); + + new cdk.CfnOutput(this, 'SubnetId', { + value: vpc.publicSubnets[0].subnetId, + description: 'Subnet ID for Lambda Managed Instances' + }); + + new cdk.CfnOutput(this, 'SecurityGroupId', { + value: securityGroup.securityGroupId, + description: 'Security Group ID for Lambda Managed Instances' + }); + + new cdk.CfnOutput(this, 'LambdaFunctionName', { + value: managedInstanceFunction.ref, + description: 'Lambda function name' + }); + + new cdk.CfnOutput(this, 'LambdaFunctionArn', { + value: managedInstanceFunction.attrArn, + description: 'Lambda function ARN' + }); + + new cdk.CfnOutput(this, 'CapacityProviderOperatorRoleArn', { + value: capacityProviderOperatorRole.roleArn, + description: 'Capacity Provider Operator Role ARN' + }); + } +} \ No newline at end of file diff --git a/lambda-managed-instances-cdk-ts/package.json b/lambda-managed-instances-cdk-ts/package.json new file mode 100644 index 000000000..62de73111 --- /dev/null +++ b/lambda-managed-instances-cdk-ts/package.json @@ -0,0 +1,30 @@ +{ + "name": "lambda-managed-instances-cdk-ts", + "version": "0.1.0", + "bin": { + "lambda-managed-instances-cdk-ts": "bin/lambda-managed-instances-cdk-ts.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk", + "deploy": "cdk deploy", + "destroy": "cdk destroy", + "synth": "cdk synth" + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/node": "18.14.6", + "aws-cdk": "^2.1034.0", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "~4.9.5" + }, + "dependencies": { + "aws-cdk-lib": "2.232.2", + "constructs": "^10.0.0", + "source-map-support": "^0.5.21" + } +} diff --git a/lambda-managed-instances-cdk-ts/test-function.sh b/lambda-managed-instances-cdk-ts/test-function.sh new file mode 100755 index 000000000..1968fa229 --- /dev/null +++ b/lambda-managed-instances-cdk-ts/test-function.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# Test script for Lambda Managed Instances function +# This script tests the deployed Lambda function + +set -e + +FUNCTION_NAME="my-managed-instance-function" +PAYLOAD='{"test": "Hello from test script", "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' + +echo "Testing Lambda Managed Instances function..." +echo "Function Name: $FUNCTION_NAME" +echo "Payload: $PAYLOAD" +echo "" + +# Invoke the function +echo "Invoking function..." +aws lambda invoke \ + --function-name "$FUNCTION_NAME" \ + --payload "$PAYLOAD" \ + --cli-binary-format raw-in-base64-out \ + response.json + +echo "" +echo "Response:" +cat response.json +echo "" + +# Check if response file exists and contains expected content +if [ -f "response.json" ]; then + if grep -q "Hello from Lambda Managed Instances!" response.json; then + echo "✅ Test PASSED: Function returned expected message" + else + echo "❌ Test FAILED: Function did not return expected message" + exit 1 + fi +else + echo "❌ Test FAILED: No response file generated" + exit 1 +fi + +# Clean up +rm -f response.json + +echo "✅ Test completed successfully!" \ No newline at end of file diff --git a/lambda-managed-instances-cdk-ts/tsconfig.json b/lambda-managed-instances-cdk-ts/tsconfig.json new file mode 100644 index 000000000..fa55b1fec --- /dev/null +++ b/lambda-managed-instances-cdk-ts/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} \ No newline at end of file From a619dbbfd7678c0032e021a664f1e09ef6cc59b4 Mon Sep 17 00:00:00 2001 From: Theophilus Ajayi Date: Mon, 15 Dec 2025 15:20:31 +0100 Subject: [PATCH 2/2] Lambda Managed Instances CDK Python --- .../.gitignore | 222 ++++++++++++++++++ lambda-managed-instances-cdk-python/README.md | 209 +++++++++++++++++ lambda-managed-instances-cdk-python/app.py | 25 ++ lambda-managed-instances-cdk-python/cdk.json | 62 +++++ .../example-pattern.json | 59 +++++ .../__init__.py | 0 .../lambda_managed_instances_stack.py | 159 +++++++++++++ .../requirements.txt | 2 + .../src/app.js | 10 + .../template.yaml | 16 ++ 10 files changed, 764 insertions(+) create mode 100644 lambda-managed-instances-cdk-python/.gitignore create mode 100644 lambda-managed-instances-cdk-python/README.md create mode 100644 lambda-managed-instances-cdk-python/app.py create mode 100644 lambda-managed-instances-cdk-python/cdk.json create mode 100644 lambda-managed-instances-cdk-python/example-pattern.json create mode 100644 lambda-managed-instances-cdk-python/lambda_managed_instances_cdk_python/__init__.py create mode 100644 lambda-managed-instances-cdk-python/lambda_managed_instances_cdk_python/lambda_managed_instances_stack.py create mode 100644 lambda-managed-instances-cdk-python/requirements.txt create mode 100644 lambda-managed-instances-cdk-python/src/app.js create mode 100644 lambda-managed-instances-cdk-python/template.yaml diff --git a/lambda-managed-instances-cdk-python/.gitignore b/lambda-managed-instances-cdk-python/.gitignore new file mode 100644 index 000000000..1834a5e66 --- /dev/null +++ b/lambda-managed-instances-cdk-python/.gitignore @@ -0,0 +1,222 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +*.sh +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt +instructions.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# CDK specific +*.swp +*.swo +*~ +.DS_Store +cdk.out/ +cdk.context.json +.cdk.staging/ +.parcel-cache/ + +# AWS specific +.aws-sam/ +samconfig.toml + +# IDE specific +.vscode/ +.idea/ +*.iml + +# OS specific +Thumbs.db +ehthumbs.db +Desktop.ini + +# Temporary files +*.tmp +*.temp +response.json + +# Node modules (if any) +node_modules/ + +# Logs +*.log +logs/ + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage/ + +# nyc test coverage +.nyc_output + +# Dependency directories +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port \ No newline at end of file diff --git a/lambda-managed-instances-cdk-python/README.md b/lambda-managed-instances-cdk-python/README.md new file mode 100644 index 000000000..5457b8ca5 --- /dev/null +++ b/lambda-managed-instances-cdk-python/README.md @@ -0,0 +1,209 @@ +# Lambda Managed Instances with AWS CDK Python + +This pattern demonstrates how to create and deploy AWS Lambda Managed Instances using AWS CDK in Python. Lambda Managed Instances allow you to run Lambda functions on dedicated EC2 instances for workloads that require more control over the underlying infrastructure. + +Learn more about this pattern at Serverless Land Patterns: [Lambda Managed Instances](https://serverlessland.com/patterns/lambda-managed-instances) + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Language + +Python + +## Framework + +CDK + +## Services From/To + +AWS Lambda Managed Instances + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) (AWS CDK >= 2.2.0) Installed + +## Architecture + +This CDK stack creates: + +1. **IAM Roles**: + - Lambda execution role with basic execution permissions + - Capacity provider operator role for managing EC2 instances + +2. **VPC Resources**: + - New VPC with CIDR 10.0.0.0/16 + - Private subnet with NAT Gateway for outbound internet access + - Security group for Lambda Managed Instances + +3. **Lambda Capacity Provider**: + - Manages EC2 instances (x86_64 architecture) + - Maximum 30 vCPUs scaling configuration + +4. **Lambda Function**: + - Python 3.13 runtime + - 2048 MB memory allocation + - 512 MB ephemeral storage + - Configured to use the managed instances capacity provider + +## Deployment Instructions + +1. Clone this repository and navigate to the pattern directory: + ```bash + cd lambda-managed-instances-cdk-python + ``` + +2. Create a Python virtual environment: + ```bash + python3 -m venv .venv + ``` + +3. Activate the virtual environment: + ```bash + source .venv/bin/activate + ``` + + If you are in Windows platform, you would activate the virtualenv like this: + + ``` + % .venv\Scripts\activate.bat + ``` + +4. Install python modules: + ```bash + python3 -m pip install -r requirements.txt + ``` + +5. From the command line, use CDK to synthesize the CloudFormation template and check for errors: + ```bash + cdk synth + ``` + +6. From the command line, use CDK to deploy the stack: + ```bash + cdk deploy + ``` + + Expected result: + + ```bash + LambdaManagedInstancesPythonStack + + Outputs: + LambdaManagedInstancesPythonStack.FunctionName = LambdaManagedInstancesPythonStack-MyLambdaFunction67CCA873-XXXXXXXXXXXXX + LambdaManagedInstancesPythonStack.FunctionArn = arn:aws:lambda:us-east-1:xxxxxxxxxxxxx:function:my-managed-instance-function + ``` + +7. Note the outputs from the CDK deployment process. These contain the resource names and ARNs which are used for testing. + +## How it works + +1. **Infrastructure Setup**: The CDK creates all necessary infrastructure including VPC, subnets, security groups, and IAM roles. + +2. **Capacity Provider**: A Lambda capacity provider is created that manages EC2 instances in your VPC. This provider can scale up to 30 vCPUs based on demand. + +3. **Lambda Function**: The Lambda function is configured to use the managed instances capacity provider instead of the standard serverless execution environment. + +4. **Function Execution**: When invoked, the Lambda function runs on dedicated EC2 instances managed by the capacity provider, providing more control over the execution environment. + +### Testing + +Use the [AWS CLI](https://aws.amazon.com/cli/) to invoke the Lambda function and observe the execution on managed instances: + +1. Invoke the Lambda function: + ```bash + aws lambda invoke \ + --function-name my-managed-instance-function \ + --payload '{"test": "data"}' \ + response.json + ``` + +2. Check the response: + ```bash + cat response.json + ``` + + Expected result: + ```json + { + "statusCode": 200, + "body": "{\"message\":\"Hello from Lambda Managed Instances!\",\"event\":{\"test\":\"data\"}}" + } + ``` + +3. Retrieve the logs from the Lambda function: + + List the log streams for that log group: + ```bash + aws logs describe-log-streams --log-group-name '/aws/lambda/my-managed-instance-function' --query logStreams[*].logStreamName + ``` + + Expected result: + ```bash + [ + "2025/01/15/[$LATEST]6922e90439514d8195e455360917eaa9" + ] + ``` + + Get the log events for that stream: + ```bash + aws logs get-log-events --log-group-name '/aws/lambda/my-managed-instance-function' --log-stream-name '2025/01/15/[$LATEST]6922e90439514d8195e455360917eaa9' + ``` + + Expected result: + ```json + { + "events": [ + { + "timestamp": 1639828317813, + "message": "START RequestId: bd3f036b-3bf1-5300-8b05-595cf662119c Version: $LATEST\n", + "ingestionTime": 1639828322765 + }, + { + "timestamp": 1639828317815, + "message": "Hello from Lambda Managed Instances!\n", + "ingestionTime": 1639828322765 + }, + { + "timestamp": 1639828317815, + "message": "END RequestId: bd3f036b-3bf1-5300-8b05-595cf662119c\n", + "ingestionTime": 1639828322765 + } + ] + } + ``` + +## Cleanup + +1. Delete the stack: + ```bash + cdk destroy + ``` + +## Tutorial + +See [this useful workshop](https://cdkworkshop.com/30-python.html) on working with the AWS CDK for Python projects. + +## Useful commands + + * `cdk ls` list all stacks in the app + * `cdk synth` emits the synthesized CloudFormation template + * `cdk deploy` deploy this stack to your default AWS account/region + * `cdk diff` compare deployed stack with current state + * `cdk docs` open CDK documentation + +## Notes + +- Lambda Managed Instances require VPC configuration and have different networking requirements compared to standard Lambda functions. +- The capacity provider manages EC2 instances automatically, scaling based on function invocation demand. +- This pattern is suitable for workloads that need more control over the execution environment or have specific networking requirements. + + +Enjoy! + +---- +Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/lambda-managed-instances-cdk-python/app.py b/lambda-managed-instances-cdk-python/app.py new file mode 100644 index 000000000..ffa614fce --- /dev/null +++ b/lambda-managed-instances-cdk-python/app.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +import os +import aws_cdk as cdk +from lambda_managed_instances_cdk_python.lambda_managed_instances_stack import LambdaManagedInstancesStack + +app = cdk.App() +LambdaManagedInstancesStack(app, "LambdaManagedInstancesPythonStack", + # If you don't specify 'env', this stack will be environment-agnostic. + # Account/Region-dependent features and context lookups will not work, + # but a single synthesized template can be deployed anywhere. + + # Uncomment the next line to specialize this stack for the AWS Account + # and Region that are implied by the current CLI configuration. + + # env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')), + + # Uncomment the next line if you know exactly what Account and Region you + # want to deploy the stack to. */ + + # env=cdk.Environment(account='123456789012', region='us-east-1'), + + # For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html + ) + +app.synth() \ No newline at end of file diff --git a/lambda-managed-instances-cdk-python/cdk.json b/lambda-managed-instances-cdk-python/cdk.json new file mode 100644 index 000000000..2c23c2eaa --- /dev/null +++ b/lambda-managed-instances-cdk-python/cdk.json @@ -0,0 +1,62 @@ +{ + "app": "python3 app.py", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "requirements*.txt", + "source.bat", + "**/__pycache__", + "**/*.pyc" + ] + }, + "context": { + "@aws-cdk/core:bootstrapQualifier": "simple", + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableLogging": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForSourceAction": true + } +} \ No newline at end of file diff --git a/lambda-managed-instances-cdk-python/example-pattern.json b/lambda-managed-instances-cdk-python/example-pattern.json new file mode 100644 index 000000000..8616bc19b --- /dev/null +++ b/lambda-managed-instances-cdk-python/example-pattern.json @@ -0,0 +1,59 @@ +{ + "title": "Step Functions to Athena", + "description": "Create a Step Functions workflow to query Amazon Athena.", + "language": "Python", + "level": "200", + "framework": "AWS CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to use an AWS Step Functions state machine to query Athena and get the results. This pattern is leveraging the native integration between these 2 services which means only JSON-based, structured language is used to define the implementation.", + "With Amazon Athena you can get up to 1000 results per invocation of the GetQueryResults method and this is the reason why the Step Function has a loop to get more results. The results are sent to a Map which can be configured to handle (the DoSomething state) the items in parallel or one by one by modifying the max_concurrency parameter.", + "This pattern deploys one Step Functions, two S3 Buckets, one Glue table and one Glue database." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sfn-athena-cdk-python", + "templateURL": "serverless-patterns/sfn-athena-cdk-python", + "projectFolder": "sfn-athena-cdk-python", + "templateFile": "sfn_athena_cdk_python_stack.py" + } + }, + "resources": { + "bullets": [ + { + "text": "Call Athena with Step Functions", + "link": "https://docs.aws.amazon.com/step-functions/latest/dg/connect-athena.html" + }, + { + "text": "Amazon Athena - Serverless Interactive Query Service", + "link": "https://aws.amazon.com/athena/" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk delete." + ] + }, + "authors": [ + { + "name": "Your name", + "image": "link-to-your-photo.jpg", + "bio": "Your bio.", + "linkedin": "linked-in-ID", + "twitter": "twitter-handle" + } + ] +} diff --git a/lambda-managed-instances-cdk-python/lambda_managed_instances_cdk_python/__init__.py b/lambda-managed-instances-cdk-python/lambda_managed_instances_cdk_python/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/lambda-managed-instances-cdk-python/lambda_managed_instances_cdk_python/lambda_managed_instances_stack.py b/lambda-managed-instances-cdk-python/lambda_managed_instances_cdk_python/lambda_managed_instances_stack.py new file mode 100644 index 000000000..8a8ac3fa3 --- /dev/null +++ b/lambda-managed-instances-cdk-python/lambda_managed_instances_cdk_python/lambda_managed_instances_stack.py @@ -0,0 +1,159 @@ +from aws_cdk import ( + Stack, + CfnOutput, + aws_ec2 as ec2, + aws_iam as iam, + aws_lambda as _lambda, +) +from constructs import Construct + + +class LambdaManagedInstancesStack(Stack): + + def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: + super().__init__(scope, construct_id, **kwargs) + + # Step 1: Create the required IAM roles (following instructions exactly) + + # Lambda execution role + lambda_execution_role = iam.Role( + self, "LambdaExecutionRole", + role_name="MyLambdaExecutionRolePython", + assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"), + managed_policies=[ + iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaBasicExecutionRole") + ] + ) + + # Capacity provider operator role + capacity_provider_operator_role = iam.Role( + self, "CapacityProviderOperatorRole", + role_name="MyCapacityProviderOperatorRolePython", + assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"), + managed_policies=[ + iam.ManagedPolicy.from_aws_managed_policy_name("AWSLambdaManagedEC2ResourceOperator") + ] + ) + + # Step 2: Set up VPC resources (following instructions exactly) + + # Create VPC with CIDR 10.0.0.0/16 + vpc = ec2.Vpc( + self, "LambdaManagedInstancesVpc", + ip_addresses=ec2.IpAddresses.cidr("10.0.0.0/16"), + max_azs=1, # Instructions show single subnet + subnet_configuration=[ + ec2.SubnetConfiguration( + cidr_mask=24, + name="lambda-subnet", + subnet_type=ec2.SubnetType.PUBLIC, # Instructions create in public for simplicity + ) + ], + nat_gateways=0 # No NAT needed for public subnet + ) + + # Create security group (following instructions exactly) + security_group = ec2.SecurityGroup( + self, "LambdaManagedInstancesSecurityGroup", + vpc=vpc, + description="Security group for Lambda Managed Instances", + security_group_name="my-capacity-provider-sg-python" + ) + + # Step 3: Create capacity provider (using native CDK L1 construct) + capacity_provider = _lambda.CfnCapacityProvider( + self, "CapacityProvider", + capacity_provider_name="my-capacity-provider-python", + vpc_config={ + "subnetIds": [vpc.public_subnets[0].subnet_id], + "securityGroupIds": [security_group.security_group_id] + }, + permissions_config={ + "capacityProviderOperatorRoleArn": capacity_provider_operator_role.role_arn + }, + instance_requirements={ + "architectures": ["x86_64"] + }, + capacity_provider_scaling_config={ + "maxVCpuCount": 30 + } + ) + + # Step 4: Create Lambda function with managed instances + managed_instance_function = _lambda.CfnFunction( + self, "ManagedInstanceFunction", + function_name="my-managed-instance-function-python", + runtime="python3.13", # Using python3.13 as requested + handler="index.handler", + code={ + "zipFile": """ +import json + +def handler(event, context): + print('Event:', json.dumps(event, indent=2)) + + return { + 'statusCode': 200, + 'body': json.dumps({ + 'message': 'Hello from Lambda Managed Instances!', + 'event': event + }) + } +""" + }, + role=lambda_execution_role.role_arn, + architectures=["x86_64"], + memory_size=2048, + ephemeral_storage={ + "size": 512 + }, + capacity_provider_config={ + "lambdaManagedInstancesCapacityProviderConfig": { + "capacityProviderArn": capacity_provider.capacity_provider_ref.capacity_provider_arn + } + } + ) + + # Step 5: Publish function version (following instructions exactly) + function_version = _lambda.CfnVersion( + self, "ManagedInstanceFunctionVersion", + function_name=managed_instance_function.ref, + description="Version 1 of Lambda Managed Instance function" + ) + + # Outputs (matching the instructions) + CfnOutput( + self, "VpcId", + value=vpc.vpc_id, + description="VPC ID for Lambda Managed Instances" + ) + + CfnOutput( + self, "SubnetId", + value=vpc.public_subnets[0].subnet_id, + description="Subnet ID for Lambda Managed Instances" + ) + + CfnOutput( + self, "SecurityGroupId", + value=security_group.security_group_id, + description="Security Group ID for Lambda Managed Instances" + ) + + CfnOutput( + self, "LambdaFunctionName", + value=managed_instance_function.ref, + description="Lambda function name" + ) + + CfnOutput( + self, "LambdaFunctionArn", + value=managed_instance_function.attr_arn, + description="Lambda function ARN" + ) + + CfnOutput( + self, "CapacityProviderOperatorRoleArn", + value=capacity_provider_operator_role.role_arn, + description="Capacity Provider Operator Role ARN" + ) \ No newline at end of file diff --git a/lambda-managed-instances-cdk-python/requirements.txt b/lambda-managed-instances-cdk-python/requirements.txt new file mode 100644 index 000000000..e271e6a8b --- /dev/null +++ b/lambda-managed-instances-cdk-python/requirements.txt @@ -0,0 +1,2 @@ +aws-cdk-lib==2.232.2 +constructs>=10.0.0,<11.0.0 \ No newline at end of file diff --git a/lambda-managed-instances-cdk-python/src/app.js b/lambda-managed-instances-cdk-python/src/app.js new file mode 100644 index 000000000..cb3c4d9c1 --- /dev/null +++ b/lambda-managed-instances-cdk-python/src/app.js @@ -0,0 +1,10 @@ +/*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: MIT-0 + */ + +'use strict' + +exports.handler = async (event) => { + // Lambda handler code + console.log(JSON.stringify(event, 0, null)) +} \ No newline at end of file diff --git a/lambda-managed-instances-cdk-python/template.yaml b/lambda-managed-instances-cdk-python/template.yaml new file mode 100644 index 000000000..269f82e41 --- /dev/null +++ b/lambda-managed-instances-cdk-python/template.yaml @@ -0,0 +1,16 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Serverless patterns - Service to Service description + +# Comment on each global +Globals: + + +# Comment each resource section to explain usage +Resources: + + +# List all common outputs for usage +Outputs: + +