From e997591d830940890a3dbf3f10ba16ed1393df51 Mon Sep 17 00:00:00 2001 From: Nithin Chandran R Date: Fri, 17 Apr 2026 08:51:22 +0000 Subject: [PATCH 1/3] New serverless pattern - lambda-durable-bedrock-cdk --- lambda-durable-bedrock-cdk/.gitignore | 6 ++ lambda-durable-bedrock-cdk/README.md | 97 +++++++++++++++++++ lambda-durable-bedrock-cdk/bin/app.ts | 7 ++ lambda-durable-bedrock-cdk/cdk.json | 11 +++ .../example-pattern.json | 61 ++++++++++++ .../lib/lambda-durable-bedrock-stack.ts | 66 +++++++++++++ lambda-durable-bedrock-cdk/package.json | 20 ++++ lambda-durable-bedrock-cdk/src/index.js | 60 ++++++++++++ lambda-durable-bedrock-cdk/tsconfig.json | 20 ++++ 9 files changed, 348 insertions(+) create mode 100644 lambda-durable-bedrock-cdk/.gitignore create mode 100644 lambda-durable-bedrock-cdk/README.md create mode 100644 lambda-durable-bedrock-cdk/bin/app.ts create mode 100644 lambda-durable-bedrock-cdk/cdk.json create mode 100644 lambda-durable-bedrock-cdk/example-pattern.json create mode 100644 lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts create mode 100644 lambda-durable-bedrock-cdk/package.json create mode 100644 lambda-durable-bedrock-cdk/src/index.js create mode 100644 lambda-durable-bedrock-cdk/tsconfig.json diff --git a/lambda-durable-bedrock-cdk/.gitignore b/lambda-durable-bedrock-cdk/.gitignore new file mode 100644 index 000000000..2303e9b95 --- /dev/null +++ b/lambda-durable-bedrock-cdk/.gitignore @@ -0,0 +1,6 @@ +node_modules +cdk.out +*.js +!src/**/*.js +*.d.ts +package-lock.json diff --git a/lambda-durable-bedrock-cdk/README.md b/lambda-durable-bedrock-cdk/README.md new file mode 100644 index 000000000..d43b4f213 --- /dev/null +++ b/lambda-durable-bedrock-cdk/README.md @@ -0,0 +1,97 @@ +# Lambda Durable Functions with Amazon Bedrock + +This pattern deploys a Lambda durable function that orchestrates a multi-step AI content pipeline using Amazon Bedrock. Each step is automatically checkpointed, so the workflow resumes from the last completed step after any interruption — without re-invoking Bedrock. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/lambda-durable-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.js 18+](https://nodejs.org/en/download/) installed +* [AWS CDK v2](https://docs.aws.amazon.com/cdk/v2/guide/getting-started.html) installed +* [Amazon Bedrock model access](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html) enabled for Anthropic Claude Sonnet in your target region + +## Architecture + +``` +┌─────────────┐ ┌──────────────────────────────────────────────────┐ +│ Invoke │────▶│ Lambda Durable Function │ +│ (CLI/SDK) │ │ │ +└─────────────┘ │ Step 1: Generate Outline ──▶ Bedrock (Claude) │ + │ ✓ checkpoint │ + │ Wait: 5s (simulate review) │ + │ ✓ checkpoint │ + │ Step 2: Expand Draft ──▶ Bedrock (Claude) │ + │ ✓ checkpoint │ + │ Step 3: Summarize ──▶ Bedrock (Claude) │ + │ ✓ checkpoint │ + └──────────────────────────────────────────────────┘ +``` + +## How it works + +1. The Lambda durable function receives a topic as input. +2. **Step 1** calls Amazon Bedrock (Claude Sonnet) to generate a blog outline from the topic. The result is checkpointed. +3. The function **waits** 5 seconds (simulating an editorial review pause). During the wait, no compute charges are incurred. +4. **Step 2** calls Bedrock to expand the outline into a full blog draft. Checkpointed. +5. **Step 3** calls Bedrock to generate a concise summary. Checkpointed. +6. If the function is interrupted at any point, it replays from the last checkpoint — completed Bedrock calls are not re-executed. + +## Deployment Instructions + +1. Clone the repository: + ```bash + git clone https://github.com/aws-samples/serverless-patterns + cd serverless-patterns/lambda-durable-bedrock-cdk + ``` + +2. Install dependencies: + ```bash + npm install + ``` + +3. Deploy the stack: + ```bash + cdk deploy + ``` + +4. Note the Lambda function name from the stack outputs. + +## Testing + +1. Invoke the durable function (use the published version from the output): + ```bash + aws lambda invoke \ + --function-name \ + --qualifier 1 \ + --payload '{"topic": "Serverless AI workflows with Lambda durable functions"}' \ + --cli-binary-format raw-in-base64-out \ + output.json + ``` + +2. Since the function includes a wait, the initial invocation returns quickly. Check the durable execution status: + ```bash + aws lambda list-durable-executions \ + --function-name \ + --qualifier 1 + ``` + +3. Once the execution completes, view the result: + ```bash + cat output.json | jq . + ``` + +## Cleanup + +```bash +cdk destroy +``` + +---- +Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/lambda-durable-bedrock-cdk/bin/app.ts b/lambda-durable-bedrock-cdk/bin/app.ts new file mode 100644 index 000000000..e35ff8486 --- /dev/null +++ b/lambda-durable-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 { LambdaDurableBedrockStack } from "../lib/lambda-durable-bedrock-stack"; + +const app = new cdk.App(); +new LambdaDurableBedrockStack(app, "LambdaDurableBedrockStack"); diff --git a/lambda-durable-bedrock-cdk/cdk.json b/lambda-durable-bedrock-cdk/cdk.json new file mode 100644 index 000000000..822400f59 --- /dev/null +++ b/lambda-durable-bedrock-cdk/cdk.json @@ -0,0 +1,11 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/app.ts", + "watch": { + "include": ["**"], + "exclude": ["README.md", "cdk*.json", "**/*.d.ts", "**/*.js", "node_modules", "src"] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true + } +} diff --git a/lambda-durable-bedrock-cdk/example-pattern.json b/lambda-durable-bedrock-cdk/example-pattern.json new file mode 100644 index 000000000..3cc32ce55 --- /dev/null +++ b/lambda-durable-bedrock-cdk/example-pattern.json @@ -0,0 +1,61 @@ +{ + "title": "Lambda Durable Functions with Amazon Bedrock", + "description": "Use AWS Lambda durable functions to orchestrate a multi-step AI content pipeline with Amazon Bedrock, featuring automatic checkpointing and failure recovery.", + "language": "TypeScript", + "level": "300", + "framework": "AWS CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern deploys a Lambda durable function that orchestrates a multi-step AI content pipeline using Amazon Bedrock. The function uses the Durable Execution SDK to automatically checkpoint progress at each step.", + "The workflow: (1) generates a blog outline from a topic using Claude, (2) waits 5 seconds to simulate editorial review, (3) expands the outline into a full draft, (4) generates a summary of the draft. Each step is checkpointed, so if the function is interrupted, it resumes from the last completed step without re-invoking Bedrock.", + "This pattern demonstrates how durable functions eliminate the need for Step Functions in tightly-coupled AI workflows while providing built-in resilience." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-durable-bedrock-cdk", + "templateURL": "serverless-patterns/lambda-durable-bedrock-cdk", + "projectFolder": "lambda-durable-bedrock-cdk", + "templateFile": "lib/lambda-durable-bedrock-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "Lambda Durable Functions Documentation", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html" + }, + { + "text": "Build multi-step applications and AI workflows with Lambda durable functions", + "link": "https://aws.amazon.com/blogs/aws/build-multi-step-applications-and-ai-workflows-with-aws-lambda-durable-functions/" + }, + { + "text": "Amazon Bedrock", + "link": "https://aws.amazon.com/bedrock/" + } + ] + }, + "deploy": { + "text": [ + "cdk deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk destroy." + ] + }, + "authors": [ + { + "name": "Nithin Chandran R", + "bio": "Technical Account Manager at AWS", + "linkedin": "nithin-chandran-r" + } + ] +} \ No newline at end of file diff --git a/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts b/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts new file mode 100644 index 000000000..5392067c1 --- /dev/null +++ b/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts @@ -0,0 +1,66 @@ +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 logs from "aws-cdk-lib/aws-logs"; +import { Construct } from "constructs"; + +export class LambdaDurableBedrockStack 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", + description: "Bedrock model ID (inference profile) to use", + }); + + // Lambda function with durable execution enabled + const fn = new lambda.Function(this, "DurableBedrockFn", { + runtime: lambda.Runtime.NODEJS_20_X, + handler: "index.handler", + code: lambda.Code.fromAsset("src"), + timeout: cdk.Duration.minutes(15), + memorySize: 256, + environment: { + MODEL_ID: modelId.valueAsString, + }, + logRetention: logs.RetentionDays.ONE_WEEK, + }); + + // Enable durable execution via CfnFunction escape hatch + const cfnFn = fn.node.defaultChild as lambda.CfnFunction; + cfnFn.addOverride("Properties.DurableExecution", { + Enabled: true, + }); + + // Bedrock InvokeModel permission + fn.addToRolePolicy( + new iam.PolicyStatement({ + actions: ["bedrock:InvokeModel"], + resources: ["*"], + }) + ); + + // Durable execution permissions + fn.addToRolePolicy( + new iam.PolicyStatement({ + actions: [ + "lambda:CheckpointDurableExecution", + "lambda:GetDurableExecutionState", + ], + resources: [fn.functionArn, `${fn.functionArn}:*`], + }) + ); + + // Publish version (durable functions require qualified invocation) + // Use CfnVersion to avoid CDK validation of DurableExecution property + const version = new lambda.CfnVersion(this, "DurableVersion", { + functionName: fn.functionName, + }); + version.addDependency(cfnFn); + + new cdk.CfnOutput(this, "FunctionName", { value: fn.functionName }); + new cdk.CfnOutput(this, "FunctionArn", { value: fn.functionArn }); + new cdk.CfnOutput(this, "VersionArn", { value: version.attrFunctionArn }); + } +} diff --git a/lambda-durable-bedrock-cdk/package.json b/lambda-durable-bedrock-cdk/package.json new file mode 100644 index 000000000..2b3e56193 --- /dev/null +++ b/lambda-durable-bedrock-cdk/package.json @@ -0,0 +1,20 @@ +{ + "name": "lambda-durable-bedrock-cdk", + "version": "1.0.0", + "bin": { + "app": "bin/app.ts" + }, + "scripts": { + "build": "tsc", + "cdk": "cdk" + }, + "dependencies": { + "aws-cdk-lib": "2.180.0", + "constructs": "10.4.2" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "ts-node": "^10.9.0", + "typescript": "~5.7.0" + } +} diff --git a/lambda-durable-bedrock-cdk/src/index.js b/lambda-durable-bedrock-cdk/src/index.js new file mode 100644 index 000000000..fe8993a11 --- /dev/null +++ b/lambda-durable-bedrock-cdk/src/index.js @@ -0,0 +1,60 @@ +const { + withDurableExecution, +} = require("@aws/durable-execution-sdk-js"); +const { + BedrockRuntimeClient, + InvokeModelCommand, +} = require("@aws-sdk/client-bedrock-runtime"); + +const client = new BedrockRuntimeClient(); +const MODEL_ID = process.env.MODEL_ID; + +async function callBedrock(prompt) { + 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 body.content[0].text; +} + +exports.handler = withDurableExecution(async (event, context) => { + const topic = event.topic || "Serverless computing"; + + // Step 1: Generate blog outline + const outline = await context.step(async (stepCtx) => { + stepCtx.logger.info(`Generating outline for: ${topic}`); + return callBedrock( + `Create a concise blog post outline (5 sections max) about: ${topic}. Return only the outline.` + ); + }); + + // Wait — simulate editorial review pause + await context.wait({ seconds: 5 }); + + // Step 2: Expand outline into draft + const draft = await context.step(async (stepCtx) => { + stepCtx.logger.info("Expanding outline into draft"); + return callBedrock( + `Expand this outline into a short blog draft (under 500 words):\n\n${outline}` + ); + }); + + // Step 3: Generate summary + const summary = await context.step(async (stepCtx) => { + stepCtx.logger.info("Generating summary"); + return callBedrock( + `Summarize this blog post in 2-3 sentences:\n\n${draft}` + ); + }); + + return { topic, outline, draft, summary }; +}); diff --git a/lambda-durable-bedrock-cdk/tsconfig.json b/lambda-durable-bedrock-cdk/tsconfig.json new file mode 100644 index 000000000..15e54de36 --- /dev/null +++ b/lambda-durable-bedrock-cdk/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["es2020"], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "outDir": "build", + "rootDir": ".", + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "esModuleInterop": true + }, + "exclude": ["node_modules", "build", "src"] +} From c9a0a0ecb5b5c0c00a01593f708de1ac19ca2d77 Mon Sep 17 00:00:00 2001 From: Nithin Chandran R Date: Fri, 17 Apr 2026 09:46:00 +0000 Subject: [PATCH 2/3] fix: deployment-tested corrections for lambda-durable-bedrock-cdk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix Bedrock model ID default (add -v1:0 suffix for inference profile) - Fix DurableExecution → DurableConfig with ExecutionTimeout - Override runtime to nodejs24.x (required for durable functions) - Remove logRetention to avoid circular dependency with CfnVersion - Use wildcard resource for durable execution IAM permissions Tested: CDK synth verified, deployed to AWS account --- .../lib/lambda-durable-bedrock-stack.ts | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts b/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts index 5392067c1..c148fa949 100644 --- a/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts +++ b/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts @@ -10,7 +10,7 @@ export class LambdaDurableBedrockStack extends cdk.Stack { const modelId = new cdk.CfnParameter(this, "BedrockModelId", { type: "String", - default: "us.anthropic.claude-sonnet-4-20250514", + default: "us.anthropic.claude-sonnet-4-20250514-v1:0", description: "Bedrock model ID (inference profile) to use", }); @@ -24,13 +24,14 @@ export class LambdaDurableBedrockStack extends cdk.Stack { environment: { MODEL_ID: modelId.valueAsString, }, - logRetention: logs.RetentionDays.ONE_WEEK, }); // Enable durable execution via CfnFunction escape hatch const cfnFn = fn.node.defaultChild as lambda.CfnFunction; - cfnFn.addOverride("Properties.DurableExecution", { - Enabled: true, + cfnFn.addOverride("Properties.Runtime", "nodejs24.x"); + cfnFn.addOverride("Properties.DurableConfig", { + ExecutionTimeout: 900, // 15 minutes max durable execution + RetentionPeriodInDays: 14, }); // Bedrock InvokeModel permission @@ -41,26 +42,18 @@ export class LambdaDurableBedrockStack extends cdk.Stack { }) ); - // Durable execution permissions + // Durable execution permissions (wildcard to avoid circular dep) fn.addToRolePolicy( new iam.PolicyStatement({ actions: [ "lambda:CheckpointDurableExecution", "lambda:GetDurableExecutionState", ], - resources: [fn.functionArn, `${fn.functionArn}:*`], + resources: ["*"], }) ); - // Publish version (durable functions require qualified invocation) - // Use CfnVersion to avoid CDK validation of DurableExecution property - const version = new lambda.CfnVersion(this, "DurableVersion", { - functionName: fn.functionName, - }); - version.addDependency(cfnFn); - new cdk.CfnOutput(this, "FunctionName", { value: fn.functionName }); new cdk.CfnOutput(this, "FunctionArn", { value: fn.functionArn }); - new cdk.CfnOutput(this, "VersionArn", { value: version.attrFunctionArn }); } } From 7a68a28e45b592328bff5aea566c48d343cfda68 Mon Sep 17 00:00:00 2001 From: Nithin Chandran R Date: Tue, 21 Apr 2026 06:30:10 +0000 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20address=20PR=20review=20=E2=80=94=20?= =?UTF-8?q?tighten=20IAM,=20use=20managed=20policy,=20fix=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Scope Bedrock InvokeModel to inference-profile and foundation-model ARNs instead of wildcard - Replace inline durable execution policy with AWSLambdaBasicDurableExecutionRolePolicy managed policy - Publish a Lambda version via CfnVersion for qualified invocation - Add FunctionVersion output for testing instructions - Fix README title, clarify testing with explicit placeholder replacement - Trim example-pattern.json description to 128 chars --- lambda-durable-bedrock-cdk/README.md | 29 +++++++++++---- .../example-pattern.json | 4 +-- .../lib/lambda-durable-bedrock-stack.ts | 35 ++++++++++++------- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/lambda-durable-bedrock-cdk/README.md b/lambda-durable-bedrock-cdk/README.md index d43b4f213..5f49c8726 100644 --- a/lambda-durable-bedrock-cdk/README.md +++ b/lambda-durable-bedrock-cdk/README.md @@ -1,4 +1,4 @@ -# Lambda Durable Functions with Amazon Bedrock +# AWS Lambda durable functions with Amazon Bedrock This pattern deploys a Lambda durable function that orchestrates a multi-step AI content pipeline using Amazon Bedrock. Each step is automatically checkpointed, so the workflow resumes from the last completed step after any interruption — without re-invoking Bedrock. @@ -59,28 +59,45 @@ Important: this application uses various AWS services and there are costs associ cdk deploy ``` -4. Note the Lambda function name from the stack outputs. +4. Note the outputs printed after deployment. You will need `FunctionName` and `FunctionVersion` for testing. + + Example output: + ``` + Outputs: + LambdaDurableBedrockStack.FunctionName = LambdaDurableBedrockStack-DurableBedrockFn3CE0D50D-AbCdEfGh + LambdaDurableBedrockStack.FunctionVersion = 1 + ``` ## Testing -1. Invoke the durable function (use the published version from the output): +1. Invoke the durable function. Replace `` with the `FunctionName` value from the deploy output, and `` with the `FunctionVersion` value: ```bash aws lambda invoke \ --function-name \ + --qualifier \ + --payload '{"topic": "Serverless AI workflows with Lambda durable functions"}' \ + --cli-binary-format raw-in-base64-out \ + output.json + ``` + + For example, if your deploy output showed `FunctionName = MyStack-DurableBedrockFn-AbCdEfGh` and `FunctionVersion = 1`: + ```bash + aws lambda invoke \ + --function-name MyStack-DurableBedrockFn-AbCdEfGh \ --qualifier 1 \ --payload '{"topic": "Serverless AI workflows with Lambda durable functions"}' \ --cli-binary-format raw-in-base64-out \ output.json ``` -2. Since the function includes a wait, the initial invocation returns quickly. Check the durable execution status: +2. The function includes a durable wait, so the initial invocation returns quickly with a `"PENDING"` status. Check the durable execution status using the same `` and ``: ```bash aws lambda list-durable-executions \ --function-name \ - --qualifier 1 + --qualifier ``` -3. Once the execution completes, view the result: +3. Once the execution status shows `"SUCCEEDED"`, view the result: ```bash cat output.json | jq . ``` diff --git a/lambda-durable-bedrock-cdk/example-pattern.json b/lambda-durable-bedrock-cdk/example-pattern.json index 3cc32ce55..10a6080b6 100644 --- a/lambda-durable-bedrock-cdk/example-pattern.json +++ b/lambda-durable-bedrock-cdk/example-pattern.json @@ -1,6 +1,6 @@ { - "title": "Lambda Durable Functions with Amazon Bedrock", - "description": "Use AWS Lambda durable functions to orchestrate a multi-step AI content pipeline with Amazon Bedrock, featuring automatic checkpointing and failure recovery.", + "title": "AWS Lambda durable functions with Amazon Bedrock", + "description": "AWS Lambda durable functions with Amazon Bedrock for a multi-step AI pipeline with automatic checkpointing and failure recovery.", "language": "TypeScript", "level": "300", "framework": "AWS CDK", diff --git a/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts b/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts index c148fa949..bd32d93a1 100644 --- a/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts +++ b/lambda-durable-bedrock-cdk/lib/lambda-durable-bedrock-stack.ts @@ -1,7 +1,6 @@ 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 logs from "aws-cdk-lib/aws-logs"; import { Construct } from "constructs"; export class LambdaDurableBedrockStack extends cdk.Stack { @@ -34,26 +33,38 @@ export class LambdaDurableBedrockStack extends cdk.Stack { RetentionPeriodInDays: 14, }); - // Bedrock InvokeModel permission + // Bedrock InvokeModel — scoped to the specific inference profile and its + // underlying foundation model rather than a wildcard resource. fn.addToRolePolicy( new iam.PolicyStatement({ actions: ["bedrock:InvokeModel"], - resources: ["*"], + resources: [ + `arn:aws:bedrock:${this.region}:${this.account}:inference-profile/${modelId.valueAsString}`, + "arn:aws:bedrock:*::foundation-model/*", + ], }) ); - // Durable execution permissions (wildcard to avoid circular dep) - fn.addToRolePolicy( - new iam.PolicyStatement({ - actions: [ - "lambda:CheckpointDurableExecution", - "lambda:GetDurableExecutionState", - ], - resources: ["*"], - }) + // Durable execution + CloudWatch Logs permissions via AWS managed policy + // https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicDurableExecutionRolePolicy.html + fn.role!.addManagedPolicy( + iam.ManagedPolicy.fromAwsManagedPolicyName( + "service-role/AWSLambdaBasicDurableExecutionRolePolicy" + ) ); + // Publish a version via L1 — fn.currentVersion doesn't recognise the + // DurableConfig escape-hatch property on CDK 2.180. + const cfnVersion = new lambda.CfnVersion(this, "DurableBedrockFnVersion", { + functionName: fn.functionName, + description: "Durable execution version", + }); + new cdk.CfnOutput(this, "FunctionName", { value: fn.functionName }); new cdk.CfnOutput(this, "FunctionArn", { value: fn.functionArn }); + new cdk.CfnOutput(this, "FunctionVersion", { + value: cfnVersion.attrVersion, + description: "Published version number — use as --qualifier value", + }); } }