Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ec2): access a vpc's internet gateway #7939

Merged
merged 10 commits into from
Jul 19, 2020
45 changes: 45 additions & 0 deletions packages/@aws-cdk/aws-ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,51 @@ DatabaseSubnet1 |`ISOLATED`|`10.0.6.0/28` |#1|Only routes within the VPC
DatabaseSubnet2 |`ISOLATED`|`10.0.6.16/28`|#2|Only routes within the VPC
DatabaseSubnet3 |`ISOLATED`|`10.0.6.32/28`|#3|Only routes within the VPC

### Accessing the Internet Gateway

If you need access to the internet gateway, you can get it's ID like so:
shearn89 marked this conversation as resolved.
Show resolved Hide resolved

```ts
const igwId = vpc.internetGatewayId;
```

For a VPC with only `ISOLATED` subnets, this value will be undefined.

This is only supported for VPC's created in the stack - currently you're
unable to get the ID for imported VPC's. To do that you'd have to specifically
look up the Internet Gateway by name, which would require knowing the name
beforehand.

This can be useful for configuring routing using a combination of gateways:
for more information see [Routing](#routing) below.

#### Routing

It's possible to add routes to any subnets using the `addRoute()` method. If for
example you want an isolated subnet to have a static route via the default
Internet Gateway created for the public subnet - perhaps for routing a VPN
connection - you can do so like this:

```ts
const vpc = ec2.Vpc(this, "VPC", {
subnetConfiguration: [{
subnetType: SubnetType.PUBLIC,
name: 'Public',
},{
subnetType: SubnetType.ISOLATED,
name: 'Isolated',
}]
})
(vpc.isolatedSubnets[0] as Subnet).addRoute("StaticRoute", {
routerId: vpc.internetGatewayId,
routerType: RouterType.GATEWAY,
destinationCidrBlock: "8.8.8.8/32",
})
```

*Note that we cast to `Subnet` here because the list of subnets only returns an
`ISubnet`.*

### Reserving subnet IP space

There are situations where the IP space for a subnet or number of subnets
Expand Down
10 changes: 10 additions & 0 deletions packages/@aws-cdk/aws-ec2/lib/vpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export interface IVpc extends IResource {
* Identifier for the VPN gateway
*/
readonly vpnGatewayId?: string;

/**
* Dependable that can be depended upon to force internet connectivity established on the VPC
*/
Expand Down Expand Up @@ -1098,6 +1099,12 @@ export class Vpc extends VpcBase {
*/
public readonly availabilityZones: string[];

/**
* Internet Gateway for the VPC. Note that in case the VPC is configured only
* with ISOLATED subnets, this attribute will be `undefined`.
*/
public readonly internetGatewayId?: string;

public readonly internetConnectivityEstablished: IDependable;

/**
Expand Down Expand Up @@ -1184,6 +1191,9 @@ export class Vpc extends VpcBase {
if (allowOutbound) {
const igw = new CfnInternetGateway(this, 'IGW', {
});

this.internetGatewayId = igw.ref;

this._internetConnectivityEstablished.add(igw);
const att = new CfnVPCGatewayAttachment(this, 'VPCGW', {
internetGatewayId: igw.ref,
Expand Down
168 changes: 168 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/integ.vpc-gateway.expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
{
"Resources": {
"MyVpcF9F0CA6F": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16",
"EnableDnsHostnames": true,
"EnableDnsSupport": true,
"InstanceTenancy": "default",
"Tags": [
{
"Key": "Name",
"Value": "aws-cdk-ec2-vpc-gateway/MyVpc"
}
]
}
},
"MyVpcPublicSubnet1SubnetF6608456": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"CidrBlock": "10.0.0.0/17",
"VpcId": {
"Ref": "MyVpcF9F0CA6F"
},
"AvailabilityZone": "test-region-1a",
"MapPublicIpOnLaunch": true,
"Tags": [
{
"Key": "aws-cdk:subnet-name",
"Value": "Public"
},
{
"Key": "aws-cdk:subnet-type",
"Value": "Public"
},
{
"Key": "Name",
"Value": "aws-cdk-ec2-vpc-gateway/MyVpc/PublicSubnet1"
}
]
}
},
"MyVpcPublicSubnet1RouteTableC46AB2F4": {
"Type": "AWS::EC2::RouteTable",
"Properties": {
"VpcId": {
"Ref": "MyVpcF9F0CA6F"
},
"Tags": [
{
"Key": "Name",
"Value": "aws-cdk-ec2-vpc-gateway/MyVpc/PublicSubnet1"
}
]
}
},
"MyVpcPublicSubnet1RouteTableAssociation2ECEE1CB": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"RouteTableId": {
"Ref": "MyVpcPublicSubnet1RouteTableC46AB2F4"
},
"SubnetId": {
"Ref": "MyVpcPublicSubnet1SubnetF6608456"
}
}
},
"MyVpcPublicSubnet1DefaultRoute95FDF9EB": {
"Type": "AWS::EC2::Route",
"Properties": {
"RouteTableId": {
"Ref": "MyVpcPublicSubnet1RouteTableC46AB2F4"
},
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "MyVpcIGW5C4A4F63"
}
},
"DependsOn": [
"MyVpcVPCGW488ACE0D"
]
},
"MyVpcIsolatedSubnet1Subnet2259FE9F": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"CidrBlock": "10.0.128.0/17",
"VpcId": {
"Ref": "MyVpcF9F0CA6F"
},
"AvailabilityZone": "test-region-1a",
"MapPublicIpOnLaunch": false,
"Tags": [
{
"Key": "aws-cdk:subnet-name",
"Value": "Isolated"
},
{
"Key": "aws-cdk:subnet-type",
"Value": "Isolated"
},
{
"Key": "Name",
"Value": "aws-cdk-ec2-vpc-gateway/MyVpc/IsolatedSubnet1"
}
]
}
},
"MyVpcIsolatedSubnet1RouteTable67AEA7B8": {
"Type": "AWS::EC2::RouteTable",
"Properties": {
"VpcId": {
"Ref": "MyVpcF9F0CA6F"
},
"Tags": [
{
"Key": "Name",
"Value": "aws-cdk-ec2-vpc-gateway/MyVpc/IsolatedSubnet1"
}
]
}
},
"MyVpcIsolatedSubnet1RouteTableAssociationCDAE5449": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"RouteTableId": {
"Ref": "MyVpcIsolatedSubnet1RouteTable67AEA7B8"
},
"SubnetId": {
"Ref": "MyVpcIsolatedSubnet1Subnet2259FE9F"
}
}
},
"MyVpcIsolatedSubnet1MyRouteCDD7D172": {
"Type": "AWS::EC2::Route",
"Properties": {
"RouteTableId": {
"Ref": "MyVpcIsolatedSubnet1RouteTable67AEA7B8"
},
"DestinationCidrBlock": "8.8.8.8/32",
"GatewayId": {
"Ref": "MyVpcIGW5C4A4F63"
}
}
},
"MyVpcIGW5C4A4F63": {
"Type": "AWS::EC2::InternetGateway",
"Properties": {
"Tags": [
{
"Key": "Name",
"Value": "aws-cdk-ec2-vpc-gateway/MyVpc"
}
]
}
},
"MyVpcVPCGW488ACE0D": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
"VpcId": {
"Ref": "MyVpcF9F0CA6F"
},
"InternetGatewayId": {
"Ref": "MyVpcIGW5C4A4F63"
}
}
}
}
}
27 changes: 27 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/integ.vpc-gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as cdk from '@aws-cdk/core';
import * as ec2 from '../lib';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'aws-cdk-ec2-vpc-gateway');

const vpc = new ec2.Vpc(stack, 'MyVpc', {
maxAzs: 1,
subnetConfiguration: [
{
subnetType: ec2.SubnetType.PUBLIC,
name: 'Public',
},
{
subnetType: ec2.SubnetType.ISOLATED,
name: 'Isolated',
},
],
});

(vpc.isolatedSubnets[0] as ec2.Subnet).addRoute('MyRoute', {
routerId: vpc.internetGatewayId!,
routerType: ec2.RouterType.GATEWAY,
destinationCidrBlock: '8.8.8.8/32',
});

app.synth();
52 changes: 51 additions & 1 deletion packages/@aws-cdk/aws-ec2/test/vpc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ nodeunitShim({
test.done();
},

'can refer to the internet gateway'(test: Test) {
const stack = getTestStack();
const vpc = new Vpc(stack, 'TheVPC');
test.deepEqual(stack.resolve(vpc.internetGatewayId), { Ref: 'TheVPCIGWFA25CC08' });
test.done();
},

'with only isolated subnets, the VPC should not contain an IGW or NAT Gateways'(test: Test) {
const stack = getTestStack();
new Vpc(stack, 'TheVPC', {
Expand Down Expand Up @@ -103,7 +110,8 @@ nodeunitShim({
},
],
});
expect(stack).to(countResources('AWS::EC2::InternetGateway', 1));
expect(stack).to(countResources('AWS::EC2::InternetGateway', 1))
;
expect(stack).notTo(haveResource('AWS::EC2::NatGateway'));
test.done();
},
Expand Down Expand Up @@ -158,6 +166,48 @@ nodeunitShim({
test.done();
},

'with isolated and public subnet, should be able to use the internet gateway to define routes'(test: Test) {
shearn89 marked this conversation as resolved.
Show resolved Hide resolved
const stack = getTestStack();
const vpc = new Vpc(stack, 'TheVPC', {
subnetConfiguration: [
{
subnetType: SubnetType.ISOLATED,
name: 'isolated',
},
{
subnetType: SubnetType.PUBLIC,
name: 'public',
},
],
});
(vpc.isolatedSubnets[0] as Subnet).addRoute('TheRoute', {
routerId: vpc.internetGatewayId!,
routerType: RouterType.GATEWAY,
destinationCidrBlock: '8.8.8.8/32',
});
expect(stack).to(haveResource('AWS::EC2::InternetGateway'));
expect(stack).to(haveResourceLike('AWS::EC2::Route', {
DestinationCidrBlock: '8.8.8.8/32',
GatewayId: { },
}));
test.done();
},

'with only isolated subnets the internet gateway should be undefined'(test: Test) {
const stack = getTestStack();
const vpc = new Vpc(stack, 'TheVPC', {
subnetConfiguration: [
{
subnetType: SubnetType.ISOLATED,
name: 'isolated',
},
],
});
test.equal(vpc.internetGatewayId, undefined);
expect(stack).notTo(haveResource('AWS::EC2::InternetGateway'));
test.done();
},

'with subnets and reserved subnets defined, VPC subnet count should not contain reserved subnets '(test: Test) {
const stack = getTestStack();
new Vpc(stack, 'TheVPC', {
Expand Down