diff --git a/lambda-managed-instances-bedrock-cdk/.gitignore b/lambda-managed-instances-bedrock-cdk/.gitignore new file mode 100644 index 000000000..7bddfb4c1 --- /dev/null +++ b/lambda-managed-instances-bedrock-cdk/.gitignore @@ -0,0 +1,6 @@ +*.js +!src/*.js +*.d.ts +node_modules +cdk.out +package-lock.json diff --git a/lambda-managed-instances-bedrock-cdk/.npmignore b/lambda-managed-instances-bedrock-cdk/.npmignore new file mode 100644 index 000000000..72b28b5da --- /dev/null +++ b/lambda-managed-instances-bedrock-cdk/.npmignore @@ -0,0 +1,3 @@ +*.ts +!*.d.ts +cdk.out diff --git a/lambda-managed-instances-bedrock-cdk/README.md b/lambda-managed-instances-bedrock-cdk/README.md new file mode 100644 index 000000000..e6788191e --- /dev/null +++ b/lambda-managed-instances-bedrock-cdk/README.md @@ -0,0 +1,76 @@ +# Lambda Managed Instances with Amazon Bedrock + +This pattern deploys a Lambda function on [Managed Instances](https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances.html) (EC2-backed compute) that invokes Amazon Bedrock (Claude) for text generation. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/lambda-managed-instances-bedrock-cdk + +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 +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Node and NPM](https://nodejs.org/en/download/) installed +* [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) installed + +## How it works + +Lambda Managed Instances combine EC2 flexibility with serverless simplicity: + +- **EC2-backed compute**: Your function runs on dedicated EC2 instances in your VPC +- **Serverless management**: AWS handles provisioning, scaling, OS patching, and load balancing +- **Cost optimization**: Use Compute Savings Plans and Reserved Instances for Lambda workloads +- **Specialized hardware**: Access Graviton, GPU, and network-optimized instance types + +This pattern adds Bedrock integration to demonstrate a real-world use case beyond hello-world. + +## Architecture + +``` +User → Lambda Function (on Managed Instances / EC2) + ├── VPC with private subnets + ├── CapacityProvider (ARM64) + └── Bedrock InvokeModel → Claude response +``` + +## Deployment Instructions + +1. Clone the repository and navigate to the pattern directory: + ```bash + git clone https://github.com/aws-samples/serverless-patterns + cd serverless-patterns/lambda-managed-instances-bedrock-cdk + ``` + +2. Install dependencies: + ```bash + npm install + ``` + +3. Deploy the stack: + ```bash + cdk deploy + ``` + +## Testing + +Invoke the function: + +```bash +aws lambda invoke \ + --function-name managed-instances-bedrock-cdk \ + --payload '{"prompt":"Explain Lambda Managed Instances in 3 sentences"}' \ + --cli-binary-format raw-in-base64-out \ + output.json && cat output.json | python3 -m json.tool +``` + +## Cleanup + +```bash +cdk destroy +``` + +---- +Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/lambda-managed-instances-bedrock-cdk/bin/app.ts b/lambda-managed-instances-bedrock-cdk/bin/app.ts new file mode 100644 index 000000000..4fe6b19a8 --- /dev/null +++ b/lambda-managed-instances-bedrock-cdk/bin/app.ts @@ -0,0 +1,7 @@ +#!/usr/bin/env node +import "source-map-support/register"; +import * as cdk from "aws-cdk-lib"; +import { LambdaManagedInstancesBedrockStack } from "../lib/lambda-managed-instances-bedrock-stack"; + +const app = new cdk.App(); +new LambdaManagedInstancesBedrockStack(app, "LambdaManagedInstancesBedrockStack"); diff --git a/lambda-managed-instances-bedrock-cdk/cdk.json b/lambda-managed-instances-bedrock-cdk/cdk.json new file mode 100644 index 000000000..27fe6d2ec --- /dev/null +++ b/lambda-managed-instances-bedrock-cdk/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "npx ts-node bin/app.ts" +} diff --git a/lambda-managed-instances-bedrock-cdk/example-pattern.json b/lambda-managed-instances-bedrock-cdk/example-pattern.json new file mode 100644 index 000000000..fe7116537 --- /dev/null +++ b/lambda-managed-instances-bedrock-cdk/example-pattern.json @@ -0,0 +1,51 @@ +{ + "title": "Lambda Managed Instances with Amazon Bedrock", + "description": "Run Amazon Bedrock model invocations on Lambda Managed Instances (EC2-backed compute) using AWS CDK, combining serverless simplicity with EC2 flexibility.", + "language": "TypeScript", + "level": "200", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern deploys a Lambda function on Managed Instances (EC2-backed compute) that invokes Amazon Bedrock (Claude) for text generation.", + "Lambda Managed Instances provide EC2 flexibility (Graviton, GPU, specialized hardware) with serverless operational simplicity — AWS handles provisioning, scaling, and maintenance.", + "The function runs on dedicated EC2 capacity in your VPC via a CapacityProvider, enabling access to EC2 pricing models (Savings Plans, Reserved Instances) while maintaining the Lambda programming model." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-managed-instances-bedrock-cdk", + "templateURL": "serverless-patterns/lambda-managed-instances-bedrock-cdk", + "projectFolder": "lambda-managed-instances-bedrock-cdk", + "templateFile": "lib/lambda-managed-instances-bedrock-stack.ts" + } + }, + "resources": { + "bullets": [ + { "text": "Lambda Managed Instances documentation", "link": "https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances.html" }, + { "text": "Lambda Managed Instances launch blog", "link": "https://aws.amazon.com/blogs/aws/introducing-aws-lambda-managed-instances-serverless-simplicity-with-ec2-flexibility/" }, + { "text": "Amazon Bedrock documentation", "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/" } + ] + }, + "deploy": { + "text": ["cdk deploy"] + }, + "testing": { + "text": ["See the README for testing instructions."] + }, + "cleanup": { + "text": ["cdk destroy"] + }, + "authors": [ + { + "name": "Nithin Chandran R", + "bio": "Technical Account Manager at AWS", + "linkedin": "nithin-chandran-r" + } + ], + "patternArch": { + "icon1": { "x": 20, "y": 50, "service": "lambda", "label": "Lambda (Managed Instances)" }, + "icon2": { "x": 80, "y": 50, "service": "bedrock", "label": "Amazon Bedrock" }, + "line1": { "from": "icon1", "to": "icon2" } + } +} diff --git a/lambda-managed-instances-bedrock-cdk/lib/lambda-managed-instances-bedrock-stack.ts b/lambda-managed-instances-bedrock-cdk/lib/lambda-managed-instances-bedrock-stack.ts new file mode 100644 index 000000000..67a82aae8 --- /dev/null +++ b/lambda-managed-instances-bedrock-cdk/lib/lambda-managed-instances-bedrock-stack.ts @@ -0,0 +1,74 @@ +import * as cdk from "aws-cdk-lib"; +import * as lambda from "aws-cdk-lib/aws-lambda"; +import * as iam from "aws-cdk-lib/aws-iam"; +import * as ec2 from "aws-cdk-lib/aws-ec2"; +import * as logs from "aws-cdk-lib/aws-logs"; +import { Construct } from "constructs"; + +export class LambdaManagedInstancesBedrockStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const modelId = new cdk.CfnParameter(this, "BedrockModelId", { + type: "String", + default: "us.anthropic.claude-sonnet-4-20250514-v1:0", + description: "Bedrock inference profile model ID", + }); + + const functionName = "managed-instances-bedrock-cdk"; + + const logGroup = new logs.LogGroup(this, "LogGroup", { + logGroupName: `/aws/lambda/${functionName}`, + retention: logs.RetentionDays.TWO_WEEKS, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + const fn = new lambda.Function(this, "BedrockFn", { + runtime: lambda.Runtime.NODEJS_24_X, + handler: "index.handler", + code: lambda.Code.fromAsset("src"), + timeout: cdk.Duration.minutes(5), + memorySize: 2048, // Managed Instances require >= 2048 MB + architecture: lambda.Architecture.ARM_64, + functionName, + description: "Bedrock invocation on Lambda Managed Instances (EC2-backed)", + loggingFormat: lambda.LoggingFormat.JSON, + logGroup, + environment: { MODEL_ID: modelId.valueAsString }, + }); + + fn.addToRolePolicy( + new iam.PolicyStatement({ + actions: ["bedrock:InvokeModel"], + resources: [ + `arn:aws:bedrock:${this.region}:${this.account}:inference-profile/${modelId.valueAsString}`, + "arn:aws:bedrock:*::foundation-model/*", + ], + }) + ); + + // Lambda Managed Instances: EC2-backed compute with serverless management + const vpc = new ec2.Vpc(this, "ManagedInstancesVpc", { + maxAzs: 2, + natGateways: 1, + }); + + const securityGroup = new ec2.SecurityGroup(this, "SecurityGroup", { + vpc, + description: "Lambda Managed Instances security group", + }); + + const capacityProvider = new lambda.CapacityProvider(this, "CapacityProvider", { + capacityProviderName: "bedrock-capacity-provider", + subnets: vpc.privateSubnets, + securityGroups: [securityGroup], + architectures: [lambda.Architecture.ARM_64], + }); + + capacityProvider.addFunction(fn); + + new cdk.CfnOutput(this, "FunctionName", { value: fn.functionName }); + new cdk.CfnOutput(this, "FunctionArn", { value: fn.functionArn }); + new cdk.CfnOutput(this, "LogGroupName", { value: logGroup.logGroupName }); + } +} diff --git a/lambda-managed-instances-bedrock-cdk/package.json b/lambda-managed-instances-bedrock-cdk/package.json new file mode 100644 index 000000000..ce27be108 --- /dev/null +++ b/lambda-managed-instances-bedrock-cdk/package.json @@ -0,0 +1,15 @@ +{ + "name": "lambda-managed-instances-bedrock-cdk", + "version": "1.0.0", + "bin": { "app": "bin/app.ts" }, + "scripts": { "build": "tsc", "cdk": "cdk" }, + "dependencies": { + "aws-cdk-lib": "2.236.0", + "constructs": "10.4.2" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "ts-node": "^10.9.0", + "typescript": "~5.7.0" + } +} diff --git a/lambda-managed-instances-bedrock-cdk/src/index.js b/lambda-managed-instances-bedrock-cdk/src/index.js new file mode 100644 index 000000000..002981a46 --- /dev/null +++ b/lambda-managed-instances-bedrock-cdk/src/index.js @@ -0,0 +1,33 @@ +const { BedrockRuntimeClient, InvokeModelCommand } = require("@aws-sdk/client-bedrock-runtime"); + +const client = new BedrockRuntimeClient(); +const MODEL_ID = process.env.MODEL_ID; + +exports.handler = async (event) => { + const prompt = event.prompt || "What are the benefits of Lambda Managed Instances?"; + + const res = await client.send( + new InvokeModelCommand({ + modelId: MODEL_ID, + contentType: "application/json", + accept: "application/json", + body: JSON.stringify({ + anthropic_version: "bedrock-2023-05-31", + max_tokens: 1024, + messages: [{ role: "user", content: prompt }], + }), + }) + ); + + const body = JSON.parse(new TextDecoder().decode(res.body)); + + return { + statusCode: 200, + body: JSON.stringify({ + prompt, + response: body.content[0].text, + model: MODEL_ID, + usage: body.usage, + }), + }; +}; diff --git a/lambda-managed-instances-bedrock-cdk/tsconfig.json b/lambda-managed-instances-bedrock-cdk/tsconfig.json new file mode 100644 index 000000000..5a9a7d6dd --- /dev/null +++ b/lambda-managed-instances-bedrock-cdk/tsconfig.json @@ -0,0 +1,22 @@ +{ + "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 + }, + "exclude": ["cdk.out"] +}