Skip to content

Commit 00011f0

Browse files
author
bgagent
committed
fix(cdk): allow pinning AgentVpc to AgentCore-supported availability zones (#353)
AgentCore only supports a subset of physical availability zones per region. AZ names are aliased per-account to physical zone IDs, so the default maxAzs selection can land in a zone AgentCore does not support, causing the AWS::BedrockAgentCore::Runtime resource to fail with NotStabilized. Changes: - Add optional `availabilityZones` prop to AgentVpcProps — when provided it takes precedence over maxAzs so the VPC is pinned to specific AZ names. - Wire up the CDK context key `agentcore:availabilityZones` in agent.ts so affected accounts can set it in cdk.context.json or via -c flag without touching construct code. - Add tests for the new prop (explicit AZs override maxAzs, 3-zone case). Usage for affected accounts: cdk deploy -c agentcore:availabilityZones='["us-east-1b","us-east-1c"]' Or in cdk.context.json: { "agentcore:availabilityZones": ["us-east-1b", "us-east-1c"] } Closes #353
1 parent 61fa63c commit 00011f0

3 files changed

Lines changed: 86 additions & 2 deletions

File tree

cdk/src/constructs/agent-vpc.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,45 @@ const HTTPS_PORT = 443;
3232
export interface AgentVpcProps {
3333
/**
3434
* Maximum number of availability zones to use.
35+
*
36+
* Ignored when {@link availabilityZones} is provided (CDK does not allow
37+
* both `maxAzs` and an explicit zone list on the same VPC).
3538
* @default 2
3639
*/
3740
readonly maxAzs?: number;
3841

42+
/**
43+
* Explicit list of availability-zone *names* (e.g. `['us-east-1b', 'us-east-1c']`)
44+
* to place the VPC — and therefore the AgentCore Runtime ENIs — into.
45+
*
46+
* AgentCore only supports a subset of the physical availability zones in a
47+
* region, and AZ *names* are aliased per-account to physical zone IDs (so
48+
* `us-east-1a` is not the same physical zone across accounts). When CDK is
49+
* left to pick zones by name (the `maxAzs` default) it can land the Runtime
50+
* subnets in a zone AgentCore does not support, and the
51+
* `AWS::BedrockAgentCore::Runtime` resource fails to stabilize with
52+
* `NotStabilized` ("subnets are in unsupported availability zones"), rolling
53+
* back the whole stack.
54+
*
55+
* Pin this to AZ names whose physical zone IDs are AgentCore-supported to
56+
* make a fresh deploy deterministic regardless of the account's
57+
* name → zone-ID mapping. Discover the mapping with:
58+
*
59+
* ```sh
60+
* aws ec2 describe-availability-zones --region <region> \
61+
* --query 'AvailabilityZones[].[ZoneName,ZoneId]' --output text
62+
* ```
63+
*
64+
* then choose names whose zone IDs are in the AgentCore-supported set for
65+
* the region (for `us-east-1` at time of writing: `use1-az1`, `use1-az2`,
66+
* `use1-az4`). The error message returned by a failed Runtime creation also
67+
* lists the currently supported zone IDs.
68+
*
69+
* When provided, takes precedence over {@link maxAzs}.
70+
* @default - CDK selects the first `maxAzs` zones by name
71+
*/
72+
readonly availabilityZones?: string[];
73+
3974
/**
4075
* Number of NAT gateways to provision.
4176
* @default 1
@@ -71,8 +106,12 @@ export class AgentVpc extends Construct {
71106
const removalPolicy = props.removalPolicy ?? RemovalPolicy.DESTROY;
72107

73108
// --- VPC ---
109+
// When explicit AZs are provided (to target AgentCore-supported physical
110+
// zones), pass them directly and omit maxAzs — CDK does not allow both.
74111
this.vpc = new ec2.Vpc(this, 'Vpc', {
75-
maxAzs,
112+
...(props.availabilityZones
113+
? { availabilityZones: props.availabilityZones }
114+
: { maxAzs }),
76115
natGateways,
77116
restrictDefaultSecurityGroup: true,
78117
subnetConfiguration: [

cdk/src/stacks/agent.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,23 @@ export class AgentStack extends Stack {
199199
]);
200200

201201
// Network isolation — VPC with restricted egress
202-
const agentVpc = new AgentVpc(this, 'AgentVpc');
202+
// AgentCore only supports a subset of physical availability zones per
203+
// region (for us-east-1: use1-az1, use1-az2, use1-az4). AZ *names* are
204+
// aliased per-account, so the default maxAzs selection can land in an
205+
// unsupported zone and cause a deploy failure. Use the CDK context key
206+
// `agentcore:availabilityZones` to pin to account-specific AZ names whose
207+
// physical zone IDs are AgentCore-supported.
208+
//
209+
// Discover your mapping:
210+
// aws ec2 describe-availability-zones --region us-east-1 \
211+
// --query 'AvailabilityZones[].[ZoneName,ZoneId]' --output text
212+
//
213+
// Then set in cdk.context.json or via -c:
214+
// "agentcore:availabilityZones": ["us-east-1b", "us-east-1c"]
215+
const agentCoreAzs = this.node.tryGetContext('agentcore:availabilityZones') as string[] | undefined;
216+
const agentVpc = new AgentVpc(this, 'AgentVpc', {
217+
...(agentCoreAzs ? { availabilityZones: agentCoreAzs } : {}),
218+
});
203219

204220
// DNS Firewall — domain-level egress filtering (observation mode for initial deployment)
205221
const additionalDomains = [...new Set(blueprints.flatMap(b => b.egressAllowlist))];

cdk/test/constructs/agent-vpc.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,33 @@ describe('AgentVpc with custom props', () => {
149149

150150
template.resourceCountIs('AWS::EC2::NatGateway', 2);
151151
});
152+
153+
test('accepts explicit availabilityZones and ignores maxAzs', () => {
154+
const app = new App();
155+
const stack = new Stack(app, 'TestStack', {
156+
env: { account: '123456789012', region: 'us-east-1' },
157+
});
158+
new AgentVpc(stack, 'AgentVpc', {
159+
availabilityZones: ['us-east-1b', 'us-east-1c'],
160+
maxAzs: 3, // should be ignored when availabilityZones is provided
161+
});
162+
const template = Template.fromStack(stack);
163+
164+
// 2 explicit AZs × 2 subnet types = 4 subnets
165+
template.resourceCountIs('AWS::EC2::Subnet', 4);
166+
});
167+
168+
test('availabilityZones with 3 zones creates 6 subnets', () => {
169+
const app = new App();
170+
const stack = new Stack(app, 'TestStack', {
171+
env: { account: '123456789012', region: 'us-east-1' },
172+
});
173+
new AgentVpc(stack, 'AgentVpc', {
174+
availabilityZones: ['us-east-1b', 'us-east-1c', 'us-east-1d'],
175+
});
176+
const template = Template.fromStack(stack);
177+
178+
// 3 AZs × 2 subnet types = 6 subnets
179+
template.resourceCountIs('AWS::EC2::Subnet', 6);
180+
});
152181
});

0 commit comments

Comments
 (0)