Child 5 of 6 — parent #740/#741. Deploys safely. Pure refactor — synthesized template must be byte-identical.
Makes the Bedrock geo configurable without changing it. Prerequisite for child 6.
Problem
Both grant sites hardcode the US geo, so no non-US or global deployment is possible without editing constructs:
cdk/src/stacks/agent.ts:549 — geoRegion: bedrock.CrossRegionInferenceProfileRegion.US
cdk/src/constructs/ecs-agent-cluster.ts:585 — string-concatenates `us.${modelId}` into the profile ARN
Scope
1. New context key bedrockGeoRegion, defaulting to us. Resolve in cdk/src/constructs/bedrock-models.ts alongside resolveBedrockModelIds, following its established shape: read via node.tryGetContext, fall back to a documented constant, and throw at synth on an unknown value so a typo fails loudly rather than granting an invalid ARN.
Accept the geos @aws-cdk/aws-bedrock-alpha actually models — CrossRegionInferenceProfileRegion is an enum of global, us, eu, apac, jp, au, us-gov. Map the string to the enum in agent.ts; use it in place of the us. literal in ecs-agent-cluster.ts.
2. Thread it into both grant sites. CrossRegionInferenceProfile.fromConfig builds inferenceProfileId as `${geoRegion}.${model.modelId}`, so this is a prefix substitution, not an ARN-shape change. Confirmed empirically — us. and global. profile ARNs are identical in shape (both regional + account-qualified).
Note the foundation-model half of the grant is already geo-agnostic: grantInvoke on a cross-region profile calls grantInvokeAllRegions, which emits region: '*'. Only the profile half is US-pinned. Verified: the global profile's members are arn:aws:bedrock:::foundation-model/anthropic.claude-opus-5 (empty region = all Regions) plus the regional form — both already covered by the region: '*' grant.
3. Derive ANTHROPIC_DEFAULT_HAIKU_MODEL from the same key. Currently hardcoded us.-prefixed at cdk/src/stacks/agent.ts:393. If the deployment moves geo and this doesn't, the main and auxiliary models route differently. Verified the global counterpart exists and is invocable: global.anthropic.claude-haiku-4-5-20251001-v1:0 (ACTIVE), and a smoke test with it set alongside global Opus 5 passed. Derive the prefix; do not leave a second hardcode.
4. Generalize the drift guard. cdk/test/constructs/bedrock-models.test.ts:83 asserts the agent default matches /^us\./ and strips only a us. prefix. Widen to the enum's geos — e.g. /^(global|us|eu|apac|jp|au|us-gov)\./ — and strip whichever matched. Do not weaken to .*: the assertion's purpose is catching a bare ID where a profile ID is required, and a bare ID is genuinely un-invokable (ValidationException: on-demand throughput isn't supported). Keep that teeth.
5. Fix the latent prefix-guard hole. resolveBedrockModelIds rejects us|eu|apac-prefixed entries (bedrock-models.ts:84) but not global., us-gov., jp., or au.. So -c bedrockModels='[\"global.anthropic.claude-opus-5\"]' silently produces an invalid us.global.anthropic.claude-opus-5 ARN instead of failing at synth. Add the missing geos. Keep the contract clear in the error message: bedrockModels is bare-IDs-only; geo comes from bedrockGeoRegion.
Why this is safe to deploy alone
With the default us, the synthesized template is unchanged. Assert exactly that in a test — synth with no context override and confirm the Bedrock IAM resources match the pre-change template. That assertion is the safety proof, not a nicety.
Acceptance criteria
Notes
Blocks #746. Independent of #742/#743/#744/#745 — touches plumbing, not model values.
Child 5 of 6 — parent #740/#741. Deploys safely. Pure refactor — synthesized template must be byte-identical.
Makes the Bedrock geo configurable without changing it. Prerequisite for child 6.
Problem
Both grant sites hardcode the US geo, so no non-US or global deployment is possible without editing constructs:
cdk/src/stacks/agent.ts:549—geoRegion: bedrock.CrossRegionInferenceProfileRegion.UScdk/src/constructs/ecs-agent-cluster.ts:585— string-concatenates`us.${modelId}`into the profile ARNScope
1. New context key
bedrockGeoRegion, defaulting tous. Resolve incdk/src/constructs/bedrock-models.tsalongsideresolveBedrockModelIds, following its established shape: read vianode.tryGetContext, fall back to a documented constant, and throw at synth on an unknown value so a typo fails loudly rather than granting an invalid ARN.Accept the geos
@aws-cdk/aws-bedrock-alphaactually models —CrossRegionInferenceProfileRegionis an enum ofglobal,us,eu,apac,jp,au,us-gov. Map the string to the enum inagent.ts; use it in place of theus.literal inecs-agent-cluster.ts.2. Thread it into both grant sites.
CrossRegionInferenceProfile.fromConfigbuildsinferenceProfileIdas`${geoRegion}.${model.modelId}`, so this is a prefix substitution, not an ARN-shape change. Confirmed empirically —us.andglobal.profile ARNs are identical in shape (both regional + account-qualified).Note the foundation-model half of the grant is already geo-agnostic:
grantInvokeon a cross-region profile callsgrantInvokeAllRegions, which emitsregion: '*'. Only the profile half is US-pinned. Verified: the global profile's members arearn:aws:bedrock:::foundation-model/anthropic.claude-opus-5(empty region = all Regions) plus the regional form — both already covered by theregion: '*'grant.3. Derive
ANTHROPIC_DEFAULT_HAIKU_MODELfrom the same key. Currently hardcodedus.-prefixed atcdk/src/stacks/agent.ts:393. If the deployment moves geo and this doesn't, the main and auxiliary models route differently. Verified the global counterpart exists and is invocable:global.anthropic.claude-haiku-4-5-20251001-v1:0(ACTIVE), and a smoke test with it set alongside global Opus 5 passed. Derive the prefix; do not leave a second hardcode.4. Generalize the drift guard.
cdk/test/constructs/bedrock-models.test.ts:83asserts the agent default matches/^us\./and strips only aus.prefix. Widen to the enum's geos — e.g./^(global|us|eu|apac|jp|au|us-gov)\./— and strip whichever matched. Do not weaken to.*: the assertion's purpose is catching a bare ID where a profile ID is required, and a bare ID is genuinely un-invokable (ValidationException: on-demand throughput isn't supported). Keep that teeth.5. Fix the latent prefix-guard hole.
resolveBedrockModelIdsrejectsus|eu|apac-prefixed entries (bedrock-models.ts:84) but notglobal.,us-gov.,jp., orau.. So-c bedrockModels='[\"global.anthropic.claude-opus-5\"]'silently produces an invalidus.global.anthropic.claude-opus-5ARN instead of failing at synth. Add the missing geos. Keep the contract clear in the error message:bedrockModelsis bare-IDs-only; geo comes frombedrockGeoRegion.Why this is safe to deploy alone
With the default
us, the synthesized template is unchanged. Assert exactly that in a test — synth with no context override and confirm the Bedrock IAM resources match the pre-change template. That assertion is the safety proof, not a nicety.Acceptance criteria
bedrockGeoRegionresolved inbedrock-models.ts, defaults tous, throws at synth on an unknown geostacks/agent.tsandconstructs/ecs-agent-cluster.ts; nous.literal remains in either grant pathANTHROPIC_DEFAULT_HAIKU_MODELprefix derived from the keyresolveBedrockModelIdsrejectsglobal./us-gov./jp./au.prefixes-c bedrockGeoRegion=globalyieldsinference-profile/global.anthropic.…ARNs and aglobal.-prefixed haiku env varcdk/test/stacks/agent.test.ts:150haiku assertion updated to derive its expectation rather than hardcodeus.mise //cdk:eslint+mise run buildgreencdk diffon default context shows no changesNotes
Blocks #746. Independent of #742/#743/#744/#745 — touches plumbing, not model values.