Skip to content

New pattern - lambda-managed-instances-bedrock-cdk#3076

Open
NithinChandranR-AWS wants to merge 2 commits into
aws-samples:mainfrom
NithinChandranR-AWS:NithinChandranR-AWS-feature-lambda-managed-instances-bedrock-cdk
Open

New pattern - lambda-managed-instances-bedrock-cdk#3076
NithinChandranR-AWS wants to merge 2 commits into
aws-samples:mainfrom
NithinChandranR-AWS:NithinChandranR-AWS-feature-lambda-managed-instances-bedrock-cdk

Conversation

@NithinChandranR-AWS
Copy link
Copy Markdown
Contributor

New Serverless Pattern: Lambda Managed Instances + Bedrock (CDK)

Description

Deploys a Lambda function on Managed Instances (EC2-backed compute) that invokes Amazon Bedrock (Claude) for text generation. Demonstrates a real-world Bedrock use case beyond the existing hello-world Managed Instances pattern.

Architecture

User → Lambda Function (on Managed Instances / EC2)
         ├── VPC with private subnets
         ├── CapacityProvider (ARM64 / Graviton)
         └── Bedrock InvokeModel → Claude response

Key Features

  • EC2-backed Lambda: Runs on dedicated EC2 instances with Graviton ARM64 architecture
  • VPC integration: Private subnets with NAT Gateway for Bedrock API access
  • Least-privilege IAM: Bedrock permissions scoped to inference profile ARN
  • Production-ready: 2048 MB memory (Managed Instances minimum), nodejs24.x runtime, CloudWatch logging

Bugs Found & Fixed During Deploy

  1. Managed Instances require memorySize >= 2048 MB (not the default 128 MB)
  2. Managed Instances require nodejs24.x runtime (not nodejs22.x)
  3. Function architecture: ARM_64 must match CapacityProvider architecture

Deployed & Tested ✅

Deployed to a live AWS account and tested end-to-end. Bedrock (Claude) returned a valid response via Lambda on Managed Instances.

Files

  • lib/lambda-managed-instances-bedrock-stack.ts — CDK stack
  • src/index.js — Lambda handler (Bedrock invocation)
  • example-pattern.json — Pattern metadata
  • README.md — Full documentation with architecture, deploy, and test instructions

Deploy Lambda on EC2-backed Managed Instances with Bedrock (Claude)
integration. Uses CapacityProvider L2 construct with ARM64 architecture,
VPC with private subnets, and least-privilege IAM scoped to inference
profile ARN.

Differentiates from existing hello-world pattern by demonstrating a
real-world Bedrock invocation use case on Managed Instances.
@NithinChandranR-AWS
Copy link
Copy Markdown
Contributor Author

Hi @biswanathmukherjee 👋 Friendly nudge — this pattern is ready for review. Deployed and tested end-to-end on a live AWS account. Would appreciate a look when you have time. Thank you!

@NithinChandranR-AWS
Copy link
Copy Markdown
Contributor Author

Hi @biswanathmukherjee 👋 This shows Lambda Managed Instances (April 2026 GA) — a brand new compute mode where Lambda runs on customer-chosen EC2 instance types (Graviton). First real-world pattern beyond hello-world. Unique service integration.

@NithinChandranR-AWS
Copy link
Copy Markdown
Contributor Author

Hi @bfreiberg 👋 — friendly nudge on this pattern. It's been deployed and tested end-to-end on a live AWS account. Happy to address any feedback. Thank you!

Comment on lines +42 to +49
actions: ["bedrock:InvokeModel"],
resources: [
`arn:aws:bedrock:${this.region}:${this.account}:inference-profile/${modelId.valueAsString}`,
"arn:aws:bedrock:*::foundation-model/*",
],
})
);

Copy link
Copy Markdown
Contributor

@parikhudit parikhudit May 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bedrock IAM resource list is too broad. arn:aws:bedrock:*::foundation-model/* permits InvokeModel against every foundation model in every region.
For the default cross-region inference profile us.anthropic.claude-sonnet-4-20250514-v1:0, scope to the destination regions only.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point -- scoped it down to the three US regions that the cross-region inference profile routes to (us-east-1, us-east-2, us-west-2) instead of the wildcard.

capacityProvider.addFunction(fn);

new cdk.CfnOutput(this, "FunctionName", { value: fn.functionName });
new cdk.CfnOutput(this, "FunctionArn", { value: fn.functionArn });
Copy link
Copy Markdown
Contributor

@parikhudit parikhudit May 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function will not become ACTIVE on the capacity provider until a version is published. Per https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances-getting-started.html step 5: "To run your function on Lambda Managed Instances, you must publish a version."

Without this, cdk deploy may succeed but aws lambda invoke could return the in-place $LATEST and will not execute on Managed Instances.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right -- added fn.currentVersion + a prod alias. Without a published version the function stays on $LATEST and won't actually run on the capacity provider.

});

const capacityProvider = new lambda.CapacityProvider(this, "CapacityProvider", {
capacityProviderName: "bedrock-capacity-provider",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

capacityProviderName: "bedrock-capacity-provider" is hardcoded. Capacity-provider names must be unique per account/region (validateCapacityProviderName in the CDK source). The same stack deployed twice in one account will fail. Drop the explicit name (CDK will generate one) or prefix with the stack name.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense -- dropped the hardcoded name, letting CDK generate a unique one.

Comment on lines +51 to +55
const vpc = new ec2.Vpc(this, "ManagedInstancesVpc", {
maxAzs: 2,
natGateways: 1,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The VPC creates a NAT gateway and Bedrock traffic egresses through it. Adding interface endpoints removes the NAT requirement and keeps Bedrock traffic on the AWS network.
If endpoints are intentionally omitted to keep the example minimal, please call out the NAT trade-off in the README.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping it minimal intentionally -- added a note in the README calling out the NAT trade-off and recommending a Bedrock VPC endpoint for production.

const { BedrockRuntimeClient, InvokeModelCommand } = require("@aws-sdk/client-bedrock-runtime");

const client = new BedrockRuntimeClient();
const MODEL_ID = process.env.MODEL_ID;
Copy link
Copy Markdown
Contributor

@parikhudit parikhudit May 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If MODEL_ID is unset the handler would proceed with undefined and the SDK call would fail with an opaque ValidationException. Should fail fast at module load?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed -- added a throw at module load so it fails immediately with a clear message instead of a cryptic SDK error.

exports.handler = async (event) => {
const prompt = event.prompt || "What are the benefits of Lambda Managed Instances?";

const res = await client.send(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing exception handling in case of failed SDK calls

Comment on lines +28 to +35
## Architecture

```
User → Lambda Function (on Managed Instances / EC2)
├── VPC with private subnets
├── CapacityProvider (ARM64)
└── Bedrock InvokeModel → Claude response
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Folder structure is not architecture

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair -- replaced with a proper flow diagram showing Client → Lambda (Managed Instances) → Bedrock.

Comment on lines +59 to +65
```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
```
Copy link
Copy Markdown
Contributor

@parikhudit parikhudit May 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the stack publishes a version (recommended in another comment), this invoke should target the published version

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the invoke command to target the prod alias with --qualifier prod.

- Narrowed foundation-model/* wildcard to specific us-east-1/us-east-2/
  us-west-2 regions for the default Claude Sonnet inference profile
- Added published version + alias (required for Managed Instances
  execution per docs step 5)
@NithinChandranR-AWS NithinChandranR-AWS force-pushed the NithinChandranR-AWS-feature-lambda-managed-instances-bedrock-cdk branch from 30b480a to 021c5ee Compare May 26, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants