VPC endpoints in AWS CDK EC2 enable private connectivity to AWS services without requiring internet gateways, NAT devices, VPN connections, or AWS Direct Connect.
The abstract base class for all VPC endpoints:
abstract class VpcEndpoint extends Resource {
constructor(scope: Construct, id: string, props?: ResourceProps);
abstract readonly vpcEndpointId: string;
addToPolicy(statement: iam.PolicyStatement): void;
}
interface IVpcEndpoint extends IResource {
readonly vpcEndpointId: string;
}
enum VpcEndpointType {
INTERFACE = 'Interface',
GATEWAY = 'Gateway'
}Gateway endpoints provide private access to S3 and DynamoDB:
class GatewayVpcEndpoint extends VpcEndpoint implements IGatewayVpcEndpoint {
constructor(scope: Construct, id: string, props: GatewayVpcEndpointProps);
readonly vpcEndpointId: string;
readonly policyDocument?: iam.PolicyDocument;
static fromGatewayVpcEndpointId(scope: Construct, id: string, gatewayVpcEndpointId: string): IGatewayVpcEndpoint;
addToPolicy(statement: iam.PolicyStatement): void;
}
interface IGatewayVpcEndpoint extends IVpcEndpoint {
// Gateway-specific interface methods
}
interface GatewayVpcEndpointProps {
readonly service: IGatewayVpcEndpointService;
readonly vpc: IVpc;
readonly subnets?: SubnetSelection[];
readonly policyDocument?: iam.PolicyDocument;
}Interface endpoints provide private access to most other AWS services:
class InterfaceVpcEndpoint extends VpcEndpoint implements IInterfaceVpcEndpoint {
constructor(scope: Construct, id: string, props: InterfaceVpcEndpointProps);
readonly vpcEndpointId: string;
readonly vpcEndpointNetworkInterfaceIds: string[];
readonly vpcEndpointDnsEntries: string[];
readonly connections: Connections;
static fromInterfaceVpcEndpointAttributes(scope: Construct, id: string, attrs: InterfaceVpcEndpointAttributes): IInterfaceVpcEndpoint;
addToPolicy(statement: iam.PolicyStatement): void;
}
interface IInterfaceVpcEndpoint extends IVpcEndpoint, IConnectable {
readonly vpcEndpointNetworkInterfaceIds: string[];
readonly vpcEndpointDnsEntries: string[];
}
interface InterfaceVpcEndpointProps {
readonly service: IInterfaceVpcEndpointService;
readonly vpc: IVpc;
readonly subnets?: SubnetSelection;
readonly securityGroups?: ISecurityGroup[];
readonly privateDnsEnabled?: boolean;
readonly policyDocument?: iam.PolicyDocument;
readonly lookupSupportedAzs?: boolean;
}
interface InterfaceVpcEndpointAttributes {
readonly vpcEndpointId: string;
readonly port: number;
readonly securityGroupId?: string;
}Predefined services available for gateway endpoints:
class GatewayVpcEndpointAwsService implements IGatewayVpcEndpointService {
static readonly DYNAMODB: GatewayVpcEndpointAwsService;
static readonly S3: GatewayVpcEndpointAwsService;
readonly name: string;
}
interface IGatewayVpcEndpointService {
readonly name: string;
}Comprehensive list of AWS services available for interface endpoints:
class InterfaceVpcEndpointAwsService implements IInterfaceVpcEndpointService {
// Compute Services
static readonly EC2: InterfaceVpcEndpointAwsService;
static readonly EC2_MESSAGES: InterfaceVpcEndpointAwsService;
static readonly ECS: InterfaceVpcEndpointAwsService;
static readonly ECS_AGENT: InterfaceVpcEndpointAwsService;
static readonly ECS_TELEMETRY: InterfaceVpcEndpointAwsService;
static readonly LAMBDA: InterfaceVpcEndpointAwsService;
// Storage Services
static readonly S3: InterfaceVpcEndpointAwsService;
static readonly EBS: InterfaceVpcEndpointAwsService;
static readonly EFS: InterfaceVpcEndpointAwsService;
static readonly FSX: InterfaceVpcEndpointAwsService;
// Database Services
static readonly RDS: InterfaceVpcEndpointAwsService;
static readonly REDSHIFT: InterfaceVpcEndpointAwsService;
static readonly REDSHIFT_DATA: InterfaceVpcEndpointAwsService;
// Messaging Services
static readonly SNS: InterfaceVpcEndpointAwsService;
static readonly SQS: InterfaceVpcEndpointAwsService;
// Management Services
static readonly CLOUDFORMATION: InterfaceVpcEndpointAwsService;
static readonly CLOUDTRAIL: InterfaceVpcEndpointAwsService;
static readonly CLOUDWATCH: InterfaceVpcEndpointAwsService;
static readonly CLOUDWATCH_EVENTS: InterfaceVpcEndpointAwsService;
static readonly CLOUDWATCH_LOGS: InterfaceVpcEndpointAwsService;
static readonly CONFIG: InterfaceVpcEndpointAwsService;
// Security Services
static readonly KMS: InterfaceVpcEndpointAwsService;
static readonly SECRETS_MANAGER: InterfaceVpcEndpointAwsService;
static readonly STS: InterfaceVpcEndpointAwsService;
// Developer Services
static readonly CODECOMMIT: InterfaceVpcEndpointAwsService;
static readonly CODECOMMIT_GIT: InterfaceVpcEndpointAwsService;
static readonly CODEBUILD: InterfaceVpcEndpointAwsService;
static readonly CODEBUILD_FIPS: InterfaceVpcEndpointAwsService;
static readonly CODEPIPELINE: InterfaceVpcEndpointAwsService;
// Systems Manager
static readonly SSM: InterfaceVpcEndpointAwsService;
static readonly SSM_MESSAGES: InterfaceVpcEndpointAwsService;
// And many more services...
static of(service: string, prefix?: string): InterfaceVpcEndpointAwsService;
readonly name: string;
readonly port: number;
}
interface IInterfaceVpcEndpointService {
readonly name: string;
readonly port: number;
}Options for adding VPC endpoints to existing VPCs:
interface GatewayVpcEndpointOptions {
readonly service: IGatewayVpcEndpointService;
readonly subnets?: SubnetSelection[];
readonly policyDocument?: iam.PolicyDocument;
}
interface InterfaceVpcEndpointOptions {
readonly service: IInterfaceVpcEndpointService;
readonly subnets?: SubnetSelection;
readonly securityGroups?: ISecurityGroup[];
readonly privateDnsEnabled?: boolean;
readonly policyDocument?: iam.PolicyDocument;
readonly lookupSupportedAzs?: boolean;
}import * as ec2 from "@aws-cdk/aws-ec2";
import * as iam from "@aws-cdk/aws-iam";
const vpc = new ec2.Vpc(this, "MyVpc", {
maxAzs: 2
});
// S3 Gateway Endpoint
const s3Endpoint = vpc.addGatewayEndpoint("S3Endpoint", {
service: ec2.GatewayVpcEndpointAwsService.S3
});
// DynamoDB Gateway Endpoint
const dynamoEndpoint = vpc.addGatewayEndpoint("DynamoEndpoint", {
service: ec2.GatewayVpcEndpointAwsService.DYNAMODB
});
// Gateway endpoint with custom policy
const restrictedS3Endpoint = vpc.addGatewayEndpoint("RestrictedS3Endpoint", {
service: ec2.GatewayVpcEndpointAwsService.S3,
policyDocument: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.AnyPrincipal()],
actions: ["s3:GetObject"],
resources: ["arn:aws:s3:::my-bucket/*"]
})
]
})
});// EC2 Interface Endpoint
const ec2Endpoint = vpc.addInterfaceEndpoint("EC2Endpoint", {
service: ec2.InterfaceVpcEndpointAwsService.EC2,
privateDnsEnabled: true
});
// Lambda Interface Endpoint
const lambdaEndpoint = vpc.addInterfaceEndpoint("LambdaEndpoint", {
service: ec2.InterfaceVpcEndpointAwsService.LAMBDA,
subnets: {
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT
}
});
// S3 Interface Endpoint (for S3 API calls)
const s3InterfaceEndpoint = vpc.addInterfaceEndpoint("S3InterfaceEndpoint", {
service: ec2.InterfaceVpcEndpointAwsService.S3,
privateDnsEnabled: true
});// Create security group for VPC endpoints
const endpointSg = new ec2.SecurityGroup(this, "VpcEndpointSG", {
vpc,
description: "Security group for VPC endpoints",
allowAllOutbound: false
});
// Allow HTTPS traffic from VPC
endpointSg.addIngressRule(
ec2.Peer.ipv4(vpc.vpcCidrBlock),
ec2.Port.tcp(443),
"Allow HTTPS from VPC"
);
// Interface endpoint with custom security group
const secureEndpoint = vpc.addInterfaceEndpoint("SecureEndpoint", {
service: ec2.InterfaceVpcEndpointAwsService.SSM,
securityGroups: [endpointSg],
privateDnsEnabled: true
});const services = [
{ name: "EC2", service: ec2.InterfaceVpcEndpointAwsService.EC2 },
{ name: "EC2Messages", service: ec2.InterfaceVpcEndpointAwsService.EC2_MESSAGES },
{ name: "SSM", service: ec2.InterfaceVpcEndpointAwsService.SSM },
{ name: "SSMMessages", service: ec2.InterfaceVpcEndpointAwsService.SSM_MESSAGES },
{ name: "S3", service: ec2.InterfaceVpcEndpointAwsService.S3 }
];
services.forEach(({ name, service }) => {
vpc.addInterfaceEndpoint(`${name}Endpoint`, {
service,
privateDnsEnabled: true,
subnets: {
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT
}
});
});// Restrictive policy for S3 access
const s3Policy = new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.AnyPrincipal()],
actions: [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
resources: [
"arn:aws:s3:::my-allowed-bucket",
"arn:aws:s3:::my-allowed-bucket/*"
],
conditions: {
StringEquals: {
"aws:PrincipalVpc": vpc.vpcId
}
}
})
]
});
const restrictedS3Gateway = vpc.addGatewayEndpoint("RestrictedS3", {
service: ec2.GatewayVpcEndpointAwsService.S3,
policyDocument: s3Policy
});
// Time-based access policy
const timeBasedPolicy = new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.AnyPrincipal()],
actions: ["lambda:*"],
resources: ["*"],
conditions: {
DateGreaterThan: {
"aws:CurrentTime": "2024-01-01T00:00:00Z"
},
DateLessThan: {
"aws:CurrentTime": "2024-12-31T23:59:59Z"
}
}
})
]
});
const timedLambdaEndpoint = vpc.addInterfaceEndpoint("TimedLambdaEndpoint", {
service: ec2.InterfaceVpcEndpointAwsService.LAMBDA,
policyDocument: timeBasedPolicy
});const vpc = new ec2.Vpc(this, "MultiTierVpc", {
subnetConfiguration: [
{
cidrMask: 24,
name: "Web",
subnetType: ec2.SubnetType.PUBLIC
},
{
cidrMask: 24,
name: "App",
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT
},
{
cidrMask: 28,
name: "Database",
subnetType: ec2.SubnetType.PRIVATE_ISOLATED
}
]
});
// S3 endpoint for all private subnets
vpc.addGatewayEndpoint("S3Endpoint", {
service: ec2.GatewayVpcEndpointAwsService.S3,
subnets: [
{ subnetGroupName: "App" },
{ subnetGroupName: "Database" }
]
});
// RDS endpoint only for database subnets
vpc.addInterfaceEndpoint("RDSEndpoint", {
service: ec2.InterfaceVpcEndpointAwsService.RDS,
subnets: {
subnetGroupName: "Database"
}
});// Custom service endpoint
const customService = ec2.InterfaceVpcEndpointAwsService.of(
"my-custom-service",
"com.amazonaws.vpce"
);
const customEndpoint = vpc.addInterfaceEndpoint("CustomEndpoint", {
service: customService,
privateDnsEnabled: false
});
// Third-party service endpoint
const thirdPartyService = ec2.InterfaceVpcEndpointAwsService.of(
"partner-service",
"com.amazonaws.us-east-1"
);// Service in different region
const crossRegionService = ec2.InterfaceVpcEndpointAwsService.of(
"s3",
"com.amazonaws.us-west-2"
);
const crossRegionEndpoint = vpc.addInterfaceEndpoint("CrossRegionS3", {
service: crossRegionService,
privateDnsEnabled: false,
lookupSupportedAzs: true
});const vpc = new ec2.Vpc(this, "EndpointVpc");
// Add required endpoints for SSM
const ssmEndpoint = vpc.addInterfaceEndpoint("SSMEndpoint", {
service: ec2.InterfaceVpcEndpointAwsService.SSM,
privateDnsEnabled: true
});
const ssmMessagesEndpoint = vpc.addInterfaceEndpoint("SSMMessagesEndpoint", {
service: ec2.InterfaceVpcEndpointAwsService.SSM_MESSAGES,
privateDnsEnabled: true
});
const ec2MessagesEndpoint = vpc.addInterfaceEndpoint("EC2MessagesEndpoint", {
service: ec2.InterfaceVpcEndpointAwsService.EC2_MESSAGES,
privateDnsEnabled: true
});
// Instance in private subnet can now use SSM without internet access
const privateInstance = new ec2.Instance(this, "PrivateInstance", {
vpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
machineImage: ec2.MachineImage.latestAmazonLinux(),
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_ISOLATED
}
});
// Allow instance to use VPC endpoints
privateInstance.connections.allowTo(
ssmEndpoint,
ec2.Port.tcp(443),
"SSM endpoint access"
);import * as cloudwatch from "@aws-cdk/aws-cloudwatch";
const s3Endpoint = vpc.addGatewayEndpoint("S3Endpoint", {
service: ec2.GatewayVpcEndpointAwsService.S3
});
// Create CloudWatch alarm for VPC endpoint
const endpointAlarm = new cloudwatch.Alarm(this, "EndpointAlarm", {
metric: new cloudwatch.Metric({
namespace: "AWS/VPC",
metricName: "PacketDropCount",
dimensionsMap: {
VpcId: vpc.vpcId,
VpcEndpointId: s3Endpoint.vpcEndpointId
}
}),
threshold: 100,
evaluationPeriods: 2
});