177 lines
5.3 KiB
TypeScript
177 lines
5.3 KiB
TypeScript
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}`,
|
|
});
|
|
}
|
|
}
|