Add dual-stack IPv6 VPC with egress optimization and VPC endpoints

- Add Amazon-provided IPv6 /56 CIDR block with auto-carved /64 per subnet
- Add Egress-Only Internet Gateway for free IPv6 outbound from private subnets
- Add IPv6 routes: public subnets via IGW, private subnets via EOIGW
- Add IPv6 NACL entries for subnet tier 2
- Add DynamoDB gateway endpoint (free, alongside existing S3)
- Add 6 interface endpoints: ECR, ECR Docker, CloudWatch Logs, STS,
  Secrets Manager, SSM with shared security group
- Add enableIpv6 prop (default true) and interfaceEndpoints config
- Update VPC stack with context params for all new features
- Include design doc and implementation plan

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 17:01:13 -08:00
parent 21f4fef6a3
commit 2e02b02023
4 changed files with 1042 additions and 2 deletions

View File

@@ -86,6 +86,15 @@ export class SpicyVpcStack extends cdk.Stack {
const publicSubnetTag = app.node.tryGetContext('publicSubnetTag') as string | undefined;
const privateSubnetATag = app.node.tryGetContext('privateSubnetATag') as string | undefined;
const privateSubnetBTag = app.node.tryGetContext('privateSubnetBTag') as string | undefined;
const enableIpv6 = app.node.tryGetContext('enableIpv6') as string | undefined;
// Interface endpoint overrides
const disableEcrEndpoint = app.node.tryGetContext('disableEcrEndpoint') as string | undefined;
const disableEcrDockerEndpoint = app.node.tryGetContext('disableEcrDockerEndpoint') as string | undefined;
const disableCloudwatchLogsEndpoint = app.node.tryGetContext('disableCloudwatchLogsEndpoint') as string | undefined;
const disableStsEndpoint = app.node.tryGetContext('disableStsEndpoint') as string | undefined;
const disableSecretsManagerEndpoint = app.node.tryGetContext('disableSecretsManagerEndpoint') as string | undefined;
const disableSsmEndpoint = app.node.tryGetContext('disableSsmEndpoint') as string | undefined;
// Parse subnet CIDRs from context
const subnetCidrs = SpicyVpcStack.parseSubnetCidrsFromContext(app);
@@ -102,6 +111,8 @@ export class SpicyVpcStack extends cdk.Stack {
privateSubnetATag?: string;
privateSubnetBTag?: string;
subnetCidrs?: SpicyVpcProps['subnetCidrs'];
enableIpv6?: boolean;
interfaceEndpoints?: SpicyVpcProps['interfaceEndpoints'];
} = {};
if (vpcCidr) vpcConfigBuilder.vpcCidr = vpcCidr;
@@ -122,6 +133,28 @@ export class SpicyVpcStack extends cdk.Stack {
if (subnetCidrs && Object.keys(subnetCidrs).length > 0) {
vpcConfigBuilder.subnetCidrs = subnetCidrs;
}
if (enableIpv6 !== undefined) {
vpcConfigBuilder.enableIpv6 = enableIpv6 !== 'false';
}
// Build interface endpoint config if any overrides provided
if (
disableEcrEndpoint ||
disableEcrDockerEndpoint ||
disableCloudwatchLogsEndpoint ||
disableStsEndpoint ||
disableSecretsManagerEndpoint ||
disableSsmEndpoint
) {
vpcConfigBuilder.interfaceEndpoints = {
ecr: disableEcrEndpoint !== 'true',
ecrDocker: disableEcrDockerEndpoint !== 'true',
cloudwatchLogs: disableCloudwatchLogsEndpoint !== 'true',
sts: disableStsEndpoint !== 'true',
secretsManager: disableSecretsManagerEndpoint !== 'true',
ssm: disableSsmEndpoint !== 'true',
};
}
return new SpicyVpcStack(scope, id, {
...stackProps,