import * as cdk from "aws-cdk-lib"; import * as ec2 from "aws-cdk-lib/aws-ec2"; import { Construct } from "constructs"; interface VpcStackProps extends cdk.StackProps { /** * Environment name (staging, prod) */ environment: string; /** * Maximum number of AZs to use (default: 2) */ maxAzs?: number; /** * Number of NAT Gateways (default: 1 for cost optimization) * Use 2 for high availability in production */ natGateways?: number; } export class VpcStack extends cdk.Stack { /** * The VPC created by this stack */ public readonly vpc: ec2.Vpc; /** * Security group for Lambda functions */ public readonly lambdaSecurityGroup: ec2.SecurityGroup; /** * S3 Gateway endpoint (free) */ public readonly s3Endpoint: ec2.GatewayVpcEndpoint; constructor(scope: Construct, id: string, props: VpcStackProps) { super(scope, id, props); const { environment, maxAzs = 2, natGateways = 1 } = props; // Create VPC with public and private subnets this.vpc = new ec2.Vpc(this, "VillageShareVpc", { vpcName: `village-share-vpc-${environment}`, ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/16"), maxAzs, natGateways, subnetConfiguration: [ { name: "Public", subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24, mapPublicIpOnLaunch: false, }, { name: "Private", subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, cidrMask: 24, }, { name: "Isolated", subnetType: ec2.SubnetType.PRIVATE_ISOLATED, cidrMask: 24, }, ], // Enable DNS support for VPC endpoints enableDnsHostnames: true, enableDnsSupport: true, }); // Security group for Lambda functions this.lambdaSecurityGroup = new ec2.SecurityGroup( this, "LambdaSecurityGroup", { vpc: this.vpc, securityGroupName: `lambda-sg-${environment}`, description: "Security group for Lambda functions in VPC", allowAllOutbound: true, // Lambda needs outbound for AWS services } ); // Security group for VPC endpoints const vpcEndpointSecurityGroup = new ec2.SecurityGroup( this, "VpcEndpointSecurityGroup", { vpc: this.vpc, securityGroupName: `vpc-endpoint-sg-${environment}`, description: "Security group for VPC Interface Endpoints", allowAllOutbound: false, } ); // Allow HTTPS traffic from Lambda security group to VPC endpoints vpcEndpointSecurityGroup.addIngressRule( this.lambdaSecurityGroup, ec2.Port.tcp(443), "Allow HTTPS from Lambda functions" ); // S3 Gateway Endpoint (FREE - no NAT charges for S3 traffic) this.s3Endpoint = this.vpc.addGatewayEndpoint("S3Endpoint", { service: ec2.GatewayVpcEndpointAwsService.S3, subnets: [{ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }], }); // SES Interface Endpoint (for sending emails without NAT) this.vpc.addInterfaceEndpoint("SesEndpoint", { service: ec2.InterfaceVpcEndpointAwsService.SES, subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, securityGroups: [vpcEndpointSecurityGroup], privateDnsEnabled: true, }); // SQS Interface Endpoint (for DLQ access) this.vpc.addInterfaceEndpoint("SqsEndpoint", { service: ec2.InterfaceVpcEndpointAwsService.SQS, subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, securityGroups: [vpcEndpointSecurityGroup], privateDnsEnabled: true, }); // CloudWatch Logs Interface Endpoint this.vpc.addInterfaceEndpoint("CloudWatchLogsEndpoint", { service: ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS, subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, securityGroups: [vpcEndpointSecurityGroup], privateDnsEnabled: true, }); // Scheduler Interface Endpoint (for EventBridge Scheduler) // Note: EventBridge Scheduler uses the scheduler.{region}.amazonaws.com endpoint this.vpc.addInterfaceEndpoint("SchedulerEndpoint", { service: new ec2.InterfaceVpcEndpointService( `com.amazonaws.${cdk.Stack.of(this).region}.scheduler` ), subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, securityGroups: [vpcEndpointSecurityGroup], privateDnsEnabled: true, }); // Add tags to subnets for easy identification cdk.Tags.of(this.vpc).add("Environment", environment); cdk.Tags.of(this.vpc).add("Project", "village-share"); // Outputs new cdk.CfnOutput(this, "VpcId", { value: this.vpc.vpcId, description: "VPC ID", exportName: `VpcId-${environment}`, }); new cdk.CfnOutput(this, "VpcCidr", { value: this.vpc.vpcCidrBlock, description: "VPC CIDR block", exportName: `VpcCidr-${environment}`, }); new cdk.CfnOutput(this, "PrivateSubnetIds", { value: this.vpc .selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }) .subnetIds.join(","), description: "Private subnet IDs", exportName: `PrivateSubnetIds-${environment}`, }); new cdk.CfnOutput(this, "LambdaSecurityGroupId", { value: this.lambdaSecurityGroup.securityGroupId, description: "Security group ID for Lambda functions", exportName: `LambdaSecurityGroupId-${environment}`, }); } }