Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion cdk/src/constructs/agent-vpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,45 @@ const HTTPS_PORT = 443;
export interface AgentVpcProps {
/**
* Maximum number of availability zones to use.
*
* Ignored when {@link availabilityZones} is provided (CDK does not allow
* both `maxAzs` and an explicit zone list on the same VPC).
* @default 2
*/
readonly maxAzs?: number;

/**
* Explicit list of availability-zone *names* (e.g. `['us-east-1b', 'us-east-1c']`)
* to place the VPC — and therefore the AgentCore Runtime ENIs — into.
*
* AgentCore only supports a subset of the physical availability zones in a
* region, and AZ *names* are aliased per-account to physical zone IDs (so
* `us-east-1a` is not the same physical zone across accounts). When CDK is
* left to pick zones by name (the `maxAzs` default) it can land the Runtime
* subnets in a zone AgentCore does not support, and the
* `AWS::BedrockAgentCore::Runtime` resource fails to stabilize with
* `NotStabilized` ("subnets are in unsupported availability zones"), rolling
* back the whole stack.
*
* Pin this to AZ names whose physical zone IDs are AgentCore-supported to
* make a fresh deploy deterministic regardless of the account's
* name → zone-ID mapping. Discover the mapping with:
*
* ```sh
* aws ec2 describe-availability-zones --region <region> \
* --query 'AvailabilityZones[].[ZoneName,ZoneId]' --output text
* ```
*
* then choose names whose zone IDs are in the AgentCore-supported set for
* the region (for `us-east-1` at time of writing: `use1-az1`, `use1-az2`,
* `use1-az4`). The error message returned by a failed Runtime creation also
* lists the currently supported zone IDs.
*
* When provided, takes precedence over {@link maxAzs}.
* @default - CDK selects the first `maxAzs` zones by name
*/
readonly availabilityZones?: string[];

/**
* Number of NAT gateways to provision.
* @default 1
Expand Down Expand Up @@ -71,8 +106,12 @@ export class AgentVpc extends Construct {
const removalPolicy = props.removalPolicy ?? RemovalPolicy.DESTROY;

// --- VPC ---
// When explicit AZs are provided (to target AgentCore-supported physical
// zones), pass them directly and omit maxAzs — CDK does not allow both.
this.vpc = new ec2.Vpc(this, 'Vpc', {
maxAzs,
...(props.availabilityZones
? { availabilityZones: props.availabilityZones }
: { maxAzs }),
natGateways,
restrictDefaultSecurityGroup: true,
subnetConfiguration: [
Expand Down
18 changes: 17 additions & 1 deletion cdk/src/stacks/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,23 @@ export class AgentStack extends Stack {
]);

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

// DNS Firewall — domain-level egress filtering (observation mode for initial deployment)
const additionalDomains = [...new Set(blueprints.flatMap(b => b.egressAllowlist))];
Expand Down
29 changes: 29 additions & 0 deletions cdk/test/constructs/agent-vpc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,33 @@ describe('AgentVpc with custom props', () => {

template.resourceCountIs('AWS::EC2::NatGateway', 2);
});

test('accepts explicit availabilityZones and ignores maxAzs', () => {
const app = new App();
const stack = new Stack(app, 'TestStack', {
env: { account: '123456789012', region: 'us-east-1' },
});
new AgentVpc(stack, 'AgentVpc', {
availabilityZones: ['us-east-1b', 'us-east-1c'],
maxAzs: 3, // should be ignored when availabilityZones is provided
});
const template = Template.fromStack(stack);

// 2 explicit AZs × 2 subnet types = 4 subnets
template.resourceCountIs('AWS::EC2::Subnet', 4);
});

test('availabilityZones with 3 zones creates 6 subnets', () => {
const app = new App();
const stack = new Stack(app, 'TestStack', {
env: { account: '123456789012', region: 'us-east-1' },
});
new AgentVpc(stack, 'AgentVpc', {
availabilityZones: ['us-east-1b', 'us-east-1c', 'us-east-1d'],
});
const template = Template.fromStack(stack);

// 3 AZs × 2 subnet types = 6 subnets
template.resourceCountIs('AWS::EC2::Subnet', 6);
});
});