From aa271e1066dcd54d50c88ffc523c189dce6d0289 Mon Sep 17 00:00:00 2001 From: Vidit Shah Date: Sun, 18 Oct 2026 11:14:30 +0530 Subject: [PATCH 1/2] fix(cdk): honor Bedrock geography in MicroVM grants Resolve the configured geography so the fallback compute-role grants match the inference profiles delivered to the agent. Cover global, eu, explicit us, and omitted context in synthesis regressions. Fixes #955 Co-Authored-By: Codex --- cdk/src/constructs/lambda-microvm-compute.ts | 9 ++--- .../constructs/lambda-microvm-compute.test.ts | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/cdk/src/constructs/lambda-microvm-compute.ts b/cdk/src/constructs/lambda-microvm-compute.ts index e429b0728..ea5e0d2ba 100644 --- a/cdk/src/constructs/lambda-microvm-compute.ts +++ b/cdk/src/constructs/lambda-microvm-compute.ts @@ -40,7 +40,7 @@ import { Construct } from 'constructs'; // costs nothing and cannot drift. import { AgentMemory } from './agent-memory'; import { AgentSessionRole } from './agent-session-role'; -import { resolveBedrockModelIds } from './bedrock-models'; +import { inferenceProfileId, resolveBedrockGeoRegion, resolveBedrockModelIds } from './bedrock-models'; import sharedConstants from '../../../contracts/constants.json'; import { LAMBDA_MICROVM_SUPPORTED_REGIONS, isLambdaMicrovmRegionSupported } from '../handlers/shared/microvm-regions'; @@ -1190,14 +1190,15 @@ export class LambdaMicrovmCompute extends Construct { // Bedrock model invocation — scoped to explicit foundation-model and // cross-Region inference-profile ARNs (parity with the AgentCore runtime and // the ECS task role), NEVER `Resource: '*'`. The model set comes from the - // shared, context-overridable list (`constructs/bedrock-models.ts`) so no - // backend can drift from the others. + // shared, context-overridable list (`constructs/bedrock-models.ts`). Resolve + // the same geography as the agent's model IDs so grants and calls cannot drift. // // Required on the COMPUTE role even though the SessionRole carries a // session-tagged Bedrock grant for cost attribution (#215): that attribution // is designed to FAIL OPEN — Claude Code's credential helper falls back to // ambient compute-role credentials when the assume-role fails — so without // this grant the fallback path AccessDenies and the task dies at turn 0. + const bedrockGeoRegion = resolveBedrockGeoRegion(this.node); const bedrockResources: string[] = []; for (const modelId of resolveBedrockModelIds(this.node)) { bedrockResources.push( @@ -1212,7 +1213,7 @@ export class LambdaMicrovmCompute extends Construct { stack.formatArn({ service: 'bedrock', resource: 'inference-profile', - resourceName: `us.${modelId}`, + resourceName: inferenceProfileId(bedrockGeoRegion, modelId), arnFormat: ArnFormat.SLASH_RESOURCE_NAME, }), ); diff --git a/cdk/test/constructs/lambda-microvm-compute.test.ts b/cdk/test/constructs/lambda-microvm-compute.test.ts index 133816282..54ddf8f35 100644 --- a/cdk/test/constructs/lambda-microvm-compute.test.ts +++ b/cdk/test/constructs/lambda-microvm-compute.test.ts @@ -156,6 +156,41 @@ function build(options: BuildOptions = {}): Built { return { stack, construct, template: Template.fromStack(stack) }; } +describe.each(['global', 'eu', 'us', undefined])('LambdaMicrovmCompute — bedrockGeoRegion=%s', (geo) => { + let template: Template; + + beforeAll(() => { + template = build({ context: geo === undefined ? {} : { bedrockGeoRegion: geo } }).template; + }); + + test('execution role grants only the selected inference profiles and bare foundation models', () => { + const policies = Object.entries(template.findResources('AWS::IAM::Policy')) + .filter(([id]) => id.includes('LambdaMicrovmComputeExecutionRole')); + const statements = policies.flatMap(([, policy]) => policy.Properties.PolicyDocument.Statement) + .filter((statement) => { + const actions = Array.isArray(statement.Action) ? statement.Action : [statement.Action]; + return actions.includes('bedrock:InvokeModel'); + }); + expect(statements).toHaveLength(1); + expect(statements[0].Action).toEqual([ + 'bedrock:InvokeModel', + 'bedrock:InvokeModelWithResponseStream', + ]); + const expectedGeo = geo ?? 'us'; + const resources = statements[0].Resource; + expect(resources).toHaveLength(DEFAULT_BEDROCK_MODEL_IDS.length * 2); + expect(resources).not.toContain('*'); + const rendered = JSON.stringify(resources); + for (const modelId of DEFAULT_BEDROCK_MODEL_IDS) { + expect(rendered).toContain(`:bedrock:*::foundation-model/${modelId}`); + expect(rendered).toContain(`:bedrock:us-east-1:123456789012:inference-profile/${expectedGeo}.${modelId}`); + } + if (expectedGeo !== 'us') { + expect(rendered).not.toContain('inference-profile/us.'); + } + }); +}); + describe('LambdaMicrovmCompute — image provisioned from a managed base image', () => { let built: Built; let template: Template; From df5fb39c6699084da9744b6627ad05a3e6854c21 Mon Sep 17 00:00:00 2001 From: Vidit Shah Date: Sun, 18 Oct 2026 11:48:47 +0530 Subject: [PATCH 2/2] docs(cdk): document all three Bedrock grant sites Address review feedback on #960. Refs #955. Co-Authored-By: Codex --- cdk/src/constructs/bedrock-models.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cdk/src/constructs/bedrock-models.ts b/cdk/src/constructs/bedrock-models.ts index 22f33e8a4..80d524073 100644 --- a/cdk/src/constructs/bedrock-models.ts +++ b/cdk/src/constructs/bedrock-models.ts @@ -103,10 +103,11 @@ const GEO_PREFIX_RE = new RegExp(`^(?:${GEO_ALTERNATION})\\.`); * redeploy, to move the deployment's inference profiles to another geography — * no construct edits needed. * - * Both grant sites derive their inference-profile ARNs from this one value (the + * All three grant sites derive their inference-profile ARNs from this one value (the * AgentCore runtime in `stacks/agent.ts` via - * `CrossRegionInferenceProfile.fromConfig`, the ECS task role in - * `constructs/ecs-agent-cluster.ts` via the `.` ARN resource + * `CrossRegionInferenceProfile.fromConfig`, and the ECS task role in + * `constructs/ecs-agent-cluster.ts` and MicroVM execution role in + * `constructs/lambda-microvm-compute.ts` via the `.` ARN resource * name), and the agent's auxiliary-model env var (`ANTHROPIC_DEFAULT_HAIKU_MODEL`) * takes the same prefix via {@link inferenceProfileId} — so the main and * auxiliary models can never route through different geographies. @@ -138,7 +139,7 @@ export function resolveBedrockGeoRegion(node: Node): CrossRegionInferenceProfile * a model the runtime may invoke — no construct edits needed. * * **Use the bare foundation-model ID (`anthropic.claude-…`), NOT a - * geo-prefixed inference-profile ID.** Both grant sites derive the + * geo-prefixed inference-profile ID.** All three grant sites derive the * inference-profile ARN by prefixing the geography from * {@link resolveBedrockGeoRegion} (`bedrockGeoRegion` — `global` in the shipped cdk.json, `us` absent any context), so passing * `us.anthropic.…` here would produce an invalid `us.us.anthropic.…` ARN. The