diff --git a/packages/@aws-cdk/aws-ec2/lib/nat.ts b/packages/@aws-cdk/aws-ec2/lib/nat.ts index 75cd31142c0b6..e991e732f38b0 100644 --- a/packages/@aws-cdk/aws-ec2/lib/nat.ts +++ b/packages/@aws-cdk/aws-ec2/lib/nat.ts @@ -1,4 +1,5 @@ import * as iam from '@aws-cdk/aws-iam'; +import { Fn, Token } from '@aws-cdk/core'; import { Connections, IConnectable } from './connections'; import { Instance } from './instance'; import { InstanceType } from './instance-types'; @@ -59,8 +60,8 @@ export abstract class NatProvider { * * @see https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html */ - public static gateway(): NatProvider { - return new NatGatewayProvider(); + public static gateway(props: NatGatewayProps = {}): NatProvider { + return new NatGatewayProvider(props); } /** @@ -122,6 +123,19 @@ export interface ConfigureNatOptions { readonly privateSubnets: PrivateSubnet[]; } +/** + * Properties for a NAT gateway + * + */ +export interface NatGatewayProps { + /** + * EIP allocation IDs for the NAT gateways + * + * @default - No fixed EIPs allocated for the NAT gateways + */ + readonly eipAllocationIds?: string[]; +} + /** * Properties for a NAT instance * @@ -203,11 +217,20 @@ export interface NatInstanceProps { class NatGatewayProvider extends NatProvider { private gateways: PrefSet = new PrefSet(); + constructor(private readonly props: NatGatewayProps = {}) { + super(); + } + public configureNat(options: ConfigureNatOptions) { // Create the NAT gateways + let i = 0; for (const sub of options.natSubnets) { const gateway = sub.addNatGateway(); + if (this.props.eipAllocationIds) { + gateway.allocationId = pickN(i, this.props.eipAllocationIds); + } this.gateways.add(sub.availabilityZone, gateway.ref); + i++; } // Add routes to them in the private subnets @@ -377,4 +400,17 @@ function isOutboundAllowed(direction: NatTrafficDirection) { function isInboundAllowed(direction: NatTrafficDirection) { return direction === NatTrafficDirection.INBOUND_AND_OUTBOUND; +} + +/** + * Token-aware pick index function + */ +function pickN(i: number, xs: string[]) { + if (Token.isUnresolved(xs)) { return Fn.select(i, xs); } + + if (i >= xs.length) { + throw new Error(`Cannot get element ${i} from ${xs}`); + } + + return xs[i]; } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts index 9a5f4d04ce5f0..172bec102ccd0 100644 --- a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts @@ -855,6 +855,24 @@ nodeunitShim({ test.done(); }, + + 'NAT gateway provider with EIP allocations'(test: Test) { + const stack = new Stack(); + const natGatewayProvider = NatProvider.gateway({ + eipAllocationIds: ['a', 'b', 'c', 'd'], + }); + new Vpc(stack, 'VpcNetwork', { natGatewayProvider }); + + cdkExpect(stack).to(haveResource('AWS::EC2::NatGateway', { + AllocationId: 'a', + })); + cdkExpect(stack).to(haveResource('AWS::EC2::NatGateway', { + AllocationId: 'b', + })); + + test.done(); + }, + 'Can add an IPv6 route'(test: Test) { // GIVEN const stack = getTestStack();