From af44791ab55188bad67bb4130f9139335708a2e5 Mon Sep 17 00:00:00 2001 From: paulhcsun <47882901+paulhcsun@users.noreply.github.com> Date: Mon, 3 Feb 2025 02:19:16 -0800 Subject: [PATCH] feat(ec2-alpha): add Transit Gateway L2 (#32956) ### Issue # (if applicable) Closes https://github.com/aws/aws-cdk/issues/17528 ### Description of changes Create new Transit Gateway L2 with the following constructs: * `TransitGateway` * `TransitGatewayRouteTable` * `TransitGatewayRoute` * `TransitGatewayVpcAttachment` * `TransitGatewayRouteTableAssociation` * `TransitGatewayRouteTablePropagation` #### Important Design Decision As described in the README, the CDK disables the creation of the default route table by EC2 and instead the CDK will create a "custom" default route table in its place. This is primarily because there is no way to obtain the route table ID of the EC2 created default route table without a custom resource. The CDK will disable the `defaultRouteTablePropagation` and `defaultRouteTableAssociation` properties on the L1 (when both are disabled, EC2 does not create the default route table), but the properties are still exposed on the CDK TransitGateway interface to allow it to be toggled for the CDK created default route table. The automatic association/propagation is being mimicked by CDK implementation and not relying on the actual EC2 feature. ### Describe any new or updated permissions being added n/a ### Description of how you validated changes Unit + Integration tests to verify that the deployed resources behave as expected. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- packages/@aws-cdk/aws-ec2-alpha/README.md | 188 +++++++ packages/@aws-cdk/aws-ec2-alpha/awslint.json | 6 +- packages/@aws-cdk/aws-ec2-alpha/lib/index.ts | 8 + .../lib/transit-gateway-association.ts | 20 + .../lib/transit-gateway-attachment.ts | 19 + ...transit-gateway-route-table-association.ts | 56 ++ ...transit-gateway-route-table-propagation.ts | 62 +++ .../lib/transit-gateway-route-table.ts | 122 +++++ .../lib/transit-gateway-route.ts | 126 +++++ .../lib/transit-gateway-vpc-attachment.ts | 200 +++++++ .../aws-ec2-alpha/lib/transit-gateway.ts | 254 +++++++++ packages/@aws-cdk/aws-ec2-alpha/lib/util.ts | 23 + .../aws-ec2-alpha/rosetta/default.ts-fixture | 2 +- ...way-disable-automatic-settings.assets.json | 19 + ...y-disable-automatic-settings.template.json | 223 ++++++++ .../integ.transit-gateway.js.snapshot/cdk.out | 1 + .../integ.json | 12 + ...efaultTestDeployAssertCF40BD53.assets.json | 19 + ...aultTestDeployAssertCF40BD53.template.json | 36 ++ .../manifest.json | 179 +++++++ .../tree.json | 491 ++++++++++++++++++ .../test/integ.transit-gateway.ts | 57 ++ ...it-gateway-route-table-association.test.ts | 60 +++ ...it-gateway-route-table-propagation.test.ts | 60 +++ .../test/transit-gateway-route-table.test.ts | 177 +++++++ .../test/transit-gateway-route.test.ts | 125 +++++ .../transit-gateway-vpc-attachment.test.ts | 195 +++++++ .../test/transit-gateway.test.ts | 331 ++++++++++++ 28 files changed, 3069 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-association.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-attachment.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table-association.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table-propagation.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-vpc-attachment.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.assets.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.template.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.template.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table-association.test.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table-propagation.test.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table.test.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route.test.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-vpc-attachment.test.ts create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway.test.ts diff --git a/packages/@aws-cdk/aws-ec2-alpha/README.md b/packages/@aws-cdk/aws-ec2-alpha/README.md index 6d0ed12468837..4fc4e6d26cbb2 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/README.md +++ b/packages/@aws-cdk/aws-ec2-alpha/README.md @@ -717,3 +717,191 @@ const vpc = new VpcV2(this, 'VPC-integ-test-tag', { // Add custom tags if needed Tags.of(vpc).add('Environment', 'Production'); ``` + +## Transit Gateway + +The AWS Transit Gateway construct library allows you to create and configure Transit Gateway resources using AWS CDK. + +See [AWS Transit Gateway Docs](docs.aws.amazon.com/vpc/latest/tgw/what-is-transit-gateway.html) for more info. + +### Overview + +The Transit Gateway construct (`TransitGateway`) is the main entry point for creating and managing your Transit Gateway infrastructure. It provides methods to create route tables, attach VPCs, and configure cross-account access. + +The Transit Gateway construct library provides four main constructs: + +- `TransitGateway`: The central hub for your network connections +- `TransitGatewayRouteTable`: Manages routing between attached networks +- `TransitGatewayVpcAttachment`: Connects VPCs to the Transit Gateway +- `TransitGatewayRoute`: Defines routing rules within your Transit Gateway + +### Basic Usage + +To create a minimal deployable `TransitGateway`: + +```ts +const transitGateway = new TransitGateway(this, 'MyTransitGateway'); +``` + +### Default Transit Gateway Route Table + +By default, `TransitGateway` is created with a default `TransitGatewayRouteTable`, for which automatic Associations and automatic Propagations are enabled. + +> Note: When you create a default Transit Gateway in AWS Console, a default Transit Gateway Route Table is automatically created by AWS. However, when using the CDK Transit Gateway L2 construct, the underlying L1 construct is configured with `defaultRouteTableAssociation` and `defaultRouteTablePropagation` explicitly disabled. This ensures that AWS does not create the default route table, allowing the CDK to define a custom default route table instead. +> +> As a result, in the AWS Console, the **Default association route table** and **Default propagation route table** settings will appear as disabled. Despite this, the CDK still provides automatic association and propagation functionality through its internal implementation, which can be controlled using the `defaultRouteTableAssociation` and `defaultRouteTablePropagation` properties within the CDK. + + +You can disable the automatic Association/Propagation on the default `TransitGatewayRouteTable` via the `TransitGateway` properties. This will still create a default route table for you: + +```ts +const transitGateway = new TransitGateway(this, 'MyTransitGateway', { + defaultRouteTableAssociation: false, + defaultRouteTablePropagation: false, +}); +``` + +### Transit Gateway Route Table Management + +Add additional Transit Gateway Route Tables using the `addRouteTable()` method: + +```ts +const transitGateway = new TransitGateway(this, 'MyTransitGateway'); + +const routeTable = transitGateway.addRouteTable('CustomRouteTable'); +``` + +### Attaching VPCs to the Transit Gateway + +Currently only VPC to Transit Gateway attachments are supported. + +Create an attachment from a VPC to the Transit Gateway using the `attachVpc()` method: + +```ts +const myVpc = new VpcV2(this, 'Vpc'); +const subnet1 = new SubnetV2(this, 'Subnet', { + vpc: myVpc, + availabilityZone: 'eu-west-2a', + ipv4CidrBlock: new IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PUBLIC +}); + +const subnet2 = new SubnetV2(this, 'Subnet', { + vpc: myVpc, + availabilityZone: 'eu-west-2a', + ipv4CidrBlock: new IpCidr('10.0.1.0/24'), + subnetType: SubnetType.PUBLIC +}); + +const transitGateway = new TransitGateway(this, 'MyTransitGateway'); + +// Create a basic attachment +const attachment = transitGateway.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [subnet1, subnet2] +}); + +// Create an attachment with optional parameters +const attachmentWithOptions = transitGateway.attachVpc('VpcAttachmentWithOptions', { + vpc: myVpc, + subnets: [subnet1], + vpcAttachmentOptions: { + dnsSupport: true, + applianceModeSupport: true, + ipv6Support: true, + securityGroupReferencingSupport: true, + } +}); +``` + +If you want to automatically associate and propagate routes with transit gateway route tables, you can pass the `associationRouteTable` and `propagationRouteTables` parameters. This will automatically create the necessary associations and propagations based on the provided route tables. + +```ts +const myVpc = new VpcV2(this, 'Vpc'); +const subnet1 = new SubnetV2(this, 'Subnet', { + vpc: myVpc, + availabilityZone: 'eu-west-2a', + ipv4CidrBlock: new IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PUBLIC +}); + +const subnet2 = new SubnetV2(this, 'Subnet', { + vpc: myVpc, + availabilityZone: 'eu-west-2a', + ipv4CidrBlock: new IpCidr('10.0.1.0/24'), + subnetType: SubnetType.PUBLIC +}); + +const transitGateway = new TransitGateway(this, 'MyTransitGateway'); +const associationRouteTable = transitGateway.addRouteTable('AssociationRouteTable'); +const propagationRouteTable1 = transitGateway.addRouteTable('PropagationRouteTable1'); +const propagationRouteTable2 = transitGateway.addRouteTable('PropagationRouteTable2'); + +// Create an attachment with automatically created association + propagations +const attachmentWithRoutes = transitGateway.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [subnet1, subnet2], + associationRouteTable: associationRouteTable, + propagationRouteTables: [propagationRouteTable1, propagationRouteTable2], +}); +``` + +In this example, the `associationRouteTable` is set to `associationRouteTable`, and `propagationRouteTables` is set to an array containing `propagationRouteTable1` and `propagationRouteTable2`. This triggers the automatic creation of route table associations and route propagations between the Transit Gateway and the specified route tables. + +### Adding static routes to the route table + +Add static routes using either the `addRoute()` method to add an active route or `addBlackholeRoute()` to add a blackhole route: + +```ts +const transitGateway = new TransitGateway(this, 'MyTransitGateway'); +const routeTable = transitGateway.addRouteTable('CustomRouteTable'); + +const myVpc = new VpcV2(this, 'Vpc'); +const subnet = new SubnetV2(this, 'Subnet', { + vpc: myVpc, + availabilityZone: 'eu-west-2a', + ipv4CidrBlock: new IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PUBLIC +}); + +const attachment = transitGateway.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [subnet] +}); + +// Add a static route to direct traffic +routeTable.addRoute('StaticRoute', attachment, '10.0.0.0/16'); + +// Block unwanted traffic with a blackhole route +routeTable.addBlackholeRoute('BlackholeRoute', '172.16.0.0/16'); +``` + +### Route Table Associations and Propagations + +Configure route table associations and enable route propagation: + +```ts +const transitGateway = new TransitGateway(this, 'MyTransitGateway'); +const routeTable = transitGateway.addRouteTable('CustomRouteTable'); +const myVpc = new VpcV2(this, 'Vpc'); +const subnet = new SubnetV2(this, 'Subnet', { + vpc: myVpc, + availabilityZone: 'eu-west-2a', + ipv4CidrBlock: new IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PUBLIC +}); +const attachment = transitGateway.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [subnet] +}); + +// Associate an attachment with a route table +routeTable.addAssociation('Association', attachment); + +// Enable route propagation for an attachment +routeTable.enablePropagation('Propagation', attachment); +``` + +**Associations** — The linking of a Transit Gateway attachment to a specific route table, which determines which routes that attachment will use for routing decisions. + +**Propagation** — The automatic advertisement of routes from an attachment to a route table, allowing the route table to learn about available network destinations. diff --git a/packages/@aws-cdk/aws-ec2-alpha/awslint.json b/packages/@aws-cdk/aws-ec2-alpha/awslint.json index ec8b3e125eeea..23608305b3668 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/awslint.json +++ b/packages/@aws-cdk/aws-ec2-alpha/awslint.json @@ -1,6 +1,10 @@ { "exclude": [ "attribute-tag:@aws-cdk/aws-ec2-alpha.RouteTable.routeTableId", - "from-method:@aws-cdk/aws-ec2-alpha.Route" + "from-method:@aws-cdk/aws-ec2-alpha.Route", + "from-method:@aws-cdk/aws-ec2-alpha.TransitGateway", + "from-method:@aws-cdk/aws-ec2-alpha.TransitGatewayRouteTableAssociation", + "from-method:@aws-cdk/aws-ec2-alpha.TransitGatewayRouteTablePropagation", + "from-method:@aws-cdk/aws-ec2-alpha.TransitGatewayVpcAttachment" ] } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/index.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/index.ts index 26f148f30b2aa..6f51175b8d6a8 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/index.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/index.ts @@ -8,3 +8,11 @@ export * from './ipam'; export * from './vpc-v2-base'; export * from './subnet-v2'; export * from './route'; +export * from './transit-gateway'; +export * from './transit-gateway-route'; +export * from './transit-gateway-route-table'; +export * from './transit-gateway-attachment'; +export * from './transit-gateway-vpc-attachment'; +export * from './transit-gateway-association'; +export * from './transit-gateway-route-table-association'; +export * from './transit-gateway-route-table-propagation'; diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-association.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-association.ts new file mode 100644 index 0000000000000..4c0f6a222aef3 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-association.ts @@ -0,0 +1,20 @@ +import { IResource, Resource } from 'aws-cdk-lib/core'; + +/** + * Represents a Transit Gateway Route Table Association. + */ +export interface ITransitGatewayAssociation extends IResource { + /** + * The ID of the transit gateway route table association. + * @attribute + */ + readonly transitGatewayAssociationId: string; +} + +/** + * A Transit Gateway Association. + * @internal + */ +export abstract class TransitGatewayAssociationBase extends Resource implements ITransitGatewayAssociation { + public abstract readonly transitGatewayAssociationId: string; +} diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-attachment.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-attachment.ts new file mode 100644 index 0000000000000..0f793f998333c --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-attachment.ts @@ -0,0 +1,19 @@ +import { IResource, Resource } from 'aws-cdk-lib/core'; +/** + * Represents a Transit Gateway Attachment. + */ +export interface ITransitGatewayAttachment extends IResource { + /** + * The ID of the transit gateway attachment. + * @attribute + */ + readonly transitGatewayAttachmentId: string; +} + +/** + * A Transit Gateway Attachment. + * @internal + */ +export abstract class TransitGatewayAttachmentBase extends Resource implements ITransitGatewayAttachment { + public abstract readonly transitGatewayAttachmentId: string; +} diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table-association.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table-association.ts new file mode 100644 index 0000000000000..6365907de4504 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table-association.ts @@ -0,0 +1,56 @@ +import { ITransitGatewayAttachment } from './transit-gateway-attachment'; +import { ITransitGatewayRouteTable } from './transit-gateway-route-table'; +import { CfnTransitGatewayRouteTableAssociation } from 'aws-cdk-lib/aws-ec2'; +import { Construct } from 'constructs'; +import { ITransitGatewayAssociation, TransitGatewayAssociationBase } from './transit-gateway-association'; + +/** + * Represents a Transit Gateway Route Table Association. + */ +export interface ITransitGatewayRouteTableAssociation extends ITransitGatewayAssociation {} + +/** + * Common properties for a Transit Gateway Route Table Association. + */ +export interface TransitGatewayRouteTableAssociationProps { + /** + * The ID of the transit gateway route table association. + */ + readonly transitGatewayVpcAttachment: ITransitGatewayAttachment; + + /** + * The ID of the transit gateway route table association. + */ + readonly transitGatewayRouteTable: ITransitGatewayRouteTable; + + /** + * Physical name of this association. + * + * @default - Assigned by CloudFormation. + */ + readonly transitGatewayRouteTableAssociationName?: string; +} + +/** + * Create a Transit Gateway Route Table Association. + * + * @resource AWS::EC2::TransitGatewayRouteTableAssociation + */ +export class TransitGatewayRouteTableAssociation extends TransitGatewayAssociationBase { + /** + * The ID of the transit gateway route table association. + */ + public readonly transitGatewayAssociationId: string; + + constructor(scope: Construct, id: string, props: TransitGatewayRouteTableAssociationProps) { + super(scope, id); + + const resource = new CfnTransitGatewayRouteTableAssociation(this, id, { + transitGatewayAttachmentId: props.transitGatewayVpcAttachment.transitGatewayAttachmentId, + transitGatewayRouteTableId: props.transitGatewayRouteTable.routeTableId, + }); + this.node.defaultChild = resource; + + this.transitGatewayAssociationId = resource.ref; + } +} diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table-propagation.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table-propagation.ts new file mode 100644 index 0000000000000..1e795a408c81d --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table-propagation.ts @@ -0,0 +1,62 @@ +import { IResource, Resource } from 'aws-cdk-lib/core'; +import { CfnTransitGatewayRouteTablePropagation } from 'aws-cdk-lib/aws-ec2'; +import { Construct } from 'constructs'; +import { ITransitGatewayAttachment } from './transit-gateway-attachment'; +import { ITransitGatewayRouteTable } from './transit-gateway-route-table'; + +/** + * Represents a Transit Gateway Route Table Propagation. + */ +export interface ITransitGatewayRouteTablePropagation extends IResource { + /** + * The ID of the transit gateway route table propagation. + * @attribute + */ + readonly transitGatewayRouteTablePropagationId: string; +} + +/** + * Common properties for a Transit Gateway Route Table Propagation. + */ +export interface TransitGatewayRouteTablePropagationProps { + /** + * The ID of the transit gateway route table propagation. + */ + readonly transitGatewayVpcAttachment: ITransitGatewayAttachment; + + /** + * The ID of the transit gateway route table propagation. + */ + readonly transitGatewayRouteTable: ITransitGatewayRouteTable; + + /** + * Physical name of this propagation. + * + * @default - Assigned by CloudFormation. + */ + readonly transitGatewayRouteTablePropagationName?: string; +} + +/** + * Create a Transit Gateway Route Table Propagation. + * + * @resource AWS::EC2::TransitGatewayRouteTablePropagation + */ +export class TransitGatewayRouteTablePropagation extends Resource implements ITransitGatewayRouteTablePropagation { + /** + * The ID of the transit gateway route table propagation. + */ + public readonly transitGatewayRouteTablePropagationId: string; + + constructor(scope: Construct, id: string, props: TransitGatewayRouteTablePropagationProps) { + super(scope, id); + + const resource = new CfnTransitGatewayRouteTablePropagation(this, id, { + transitGatewayAttachmentId: props.transitGatewayVpcAttachment.transitGatewayAttachmentId, + transitGatewayRouteTableId: props.transitGatewayRouteTable.routeTableId, + }); + this.node.defaultChild = resource; + + this.transitGatewayRouteTablePropagationId = resource.ref; + } +} diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table.ts new file mode 100644 index 0000000000000..5b2df56c14a85 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route-table.ts @@ -0,0 +1,122 @@ +import { IResource, Resource } from 'aws-cdk-lib/core'; +import { ITransitGateway } from './transit-gateway'; +import { Construct } from 'constructs'; +import { CfnTransitGatewayRouteTable, IRouteTable } from 'aws-cdk-lib/aws-ec2'; +import { ITransitGatewayAttachment } from './transit-gateway-attachment'; +import { TransitGatewayRoute, TransitGatewayBlackholeRoute, ITransitGatewayRoute } from './transit-gateway-route'; +import { ITransitGatewayRouteTableAssociation, TransitGatewayRouteTableAssociation } from './transit-gateway-route-table-association'; +import { ITransitGatewayRouteTablePropagation, TransitGatewayRouteTablePropagation } from './transit-gateway-route-table-propagation'; + +/** + * Represents a Transit Gateway Route Table. + */ +export interface ITransitGatewayRouteTable extends IResource, IRouteTable { + /** + * Add an active route to this route table. + * + * @returns ITransitGatewayRoute + */ + addRoute(id: string, transitGatewayAttachment: ITransitGatewayAttachment, destinationCidr: string): ITransitGatewayRoute; + + /** + * Add a blackhole route to this route table. + * + * @returns ITransitGatewayRoute + */ + addBlackholeRoute(id: string, destinationCidr: string): ITransitGatewayRoute; + + /** + * Associate the provided Attachments with this route table. + * + * @returns ITransitGatewayRouteTableAssociation + */ + addAssociation(id: string, transitGatewayAttachment: ITransitGatewayAttachment): ITransitGatewayRouteTableAssociation; + + /** + * Enable propagation from the provided Attachments to this route table. + * + * @returns ITransitGatewayRouteTablePropagation + */ + enablePropagation(id: string, transitGatewayAttachment: ITransitGatewayAttachment): ITransitGatewayRouteTablePropagation; +} + +/** + * Common properties for creating a Transit Gateway Route Table resource. + */ +export interface TransitGatewayRouteTableProps { + /** + * The Transit Gateway that this route table belongs to. + */ + readonly transitGateway: ITransitGateway; + + /** + * Physical name of this Transit Gateway Route Table. + * + * @default - Assigned by CloudFormation. + */ + readonly transitGatewayRouteTableName?: string; +} + +/** + * A Transit Gateway Route Table. + * @internal + */ +abstract class TransitGatewayRouteTableBase extends Resource implements ITransitGatewayRouteTable { + public abstract readonly routeTableId: string; + public abstract readonly transitGateway: ITransitGateway; + + public addRoute(id: string, transitGatewayAttachment: ITransitGatewayAttachment, destinationCidr: string): ITransitGatewayRoute { + return new TransitGatewayRoute(this, id, { + transitGatewayRouteTable: this, + transitGatewayAttachment, + destinationCidrBlock: destinationCidr, + }); + } + + public addBlackholeRoute(id: string, destinationCidr: string): ITransitGatewayRoute { + return new TransitGatewayBlackholeRoute(this, id, { + transitGatewayRouteTable: this, + destinationCidrBlock: destinationCidr, + }); + } + + public addAssociation(id: string, transitGatewayAttachment: ITransitGatewayAttachment): ITransitGatewayRouteTableAssociation { + return new TransitGatewayRouteTableAssociation(this, id, { + transitGatewayVpcAttachment: transitGatewayAttachment, + transitGatewayRouteTable: this, + }); + } + + public enablePropagation(id: string, transitGatewayAttachment: ITransitGatewayAttachment): ITransitGatewayRouteTablePropagation { + return new TransitGatewayRouteTablePropagation(this, id, { + transitGatewayVpcAttachment: transitGatewayAttachment, + transitGatewayRouteTable: this, + }); + } +} + +/** + * Creates a Transit Gateway route table. + * + * @resource AWS::EC2::TransitGatewayRouteTable + */ +export class TransitGatewayRouteTable extends TransitGatewayRouteTableBase { + public readonly routeTableId: string; + /** + * The Transit Gateway. + */ + public readonly transitGateway: ITransitGateway; + + constructor(scope: Construct, id: string, props: TransitGatewayRouteTableProps) { + super(scope, id); + + const resource = new CfnTransitGatewayRouteTable(this, id, { + transitGatewayId: props.transitGateway.transitGatewayId, + }); + + this.node.defaultChild = resource; + + this.routeTableId = resource.attrTransitGatewayRouteTableId; + this.transitGateway = props.transitGateway; + } +} diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route.ts new file mode 100644 index 0000000000000..0178b99ac55e6 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-route.ts @@ -0,0 +1,126 @@ +import { IResource, Resource } from 'aws-cdk-lib/core'; +import { Construct } from 'constructs'; +import { CfnTransitGatewayRoute } from 'aws-cdk-lib/aws-ec2'; +import { ITransitGatewayRouteTable } from './transit-gateway-route-table'; +import { ITransitGatewayAttachment } from './transit-gateway-attachment'; + +/** + * Represents a Transit Gateway Route. + */ +export interface ITransitGatewayRoute extends IResource { + /** + * The destination CIDR block for this route. + * + * Destination Cidr cannot overlap for static routes but is allowed for propagated routes. + * When overlapping occurs, static routes take precedence over propagated routes. + */ + readonly destinationCidrBlock: string; + + /** + * The transit gateway route table this route belongs to. + */ + readonly routeTable: ITransitGatewayRouteTable; +} + +/** + * Common properties for a Transit Gateway Route. + */ +export interface BaseTransitGatewayRouteProps { + /** + * The destination CIDR block for this route. + * + * Destination Cidr cannot overlap for static routes but is allowed for propagated routes. + * When overlapping occurs, static routes take precedence over propagated routes. + */ + readonly destinationCidrBlock: string; + + /** + * The transit gateway route table you want to install this route into. + */ + readonly transitGatewayRouteTable: ITransitGatewayRouteTable; + + /** + * Physical name of this Transit Gateway Route. + * + * @default - Assigned by CloudFormation. + */ + readonly transitGatewayRouteName?: string; +} + +/** + * Common properties for a Transit Gateway Route. + */ +export interface TransitGatewayRouteProps extends BaseTransitGatewayRouteProps { + /** + * The transit gateway attachment to route the traffic to. + */ + readonly transitGatewayAttachment: ITransitGatewayAttachment; +} + +/** + * Properties for a Transit Gateway Blackhole Route. + */ +export interface TransitGatewayBlackholeRouteProps extends BaseTransitGatewayRouteProps {} + +/** + * A Transit Gateway Route. + * @internal + */ +abstract class TransitGatewayRouteBase extends Resource implements ITransitGatewayRoute { + public abstract routeTable: ITransitGatewayRouteTable; + public abstract destinationCidrBlock: string; +} + +/** + * Create a Transit Gateway Active Route. + * + * @resource AWS::EC2::TransitGatewayRoute + */ +export class TransitGatewayRoute extends TransitGatewayRouteBase { + public readonly routeTable: ITransitGatewayRouteTable; + public readonly destinationCidrBlock: string; + + /** + * The AWS CloudFormation resource representing the Transit Gateway Route. + */ + public readonly resource: CfnTransitGatewayRoute; + + constructor(scope: Construct, id: string, props: TransitGatewayRouteProps) { + super(scope, id); + + this.resource = new CfnTransitGatewayRoute(this, 'TransitGatewayRoute', { + blackhole: false, + destinationCidrBlock: props.destinationCidrBlock, + transitGatewayRouteTableId: props.transitGatewayRouteTable.routeTableId, + transitGatewayAttachmentId: props.transitGatewayAttachment?.transitGatewayAttachmentId, + }); + + this.node.defaultChild = this.resource; + this.destinationCidrBlock = this.resource.destinationCidrBlock; + this.routeTable = props.transitGatewayRouteTable; + } +} + +/** + * Create a Transit Gateway Blackhole Route. + * + * @resource AWS::EC2::TransitGatewayRoute + */ +export class TransitGatewayBlackholeRoute extends TransitGatewayRouteBase { + public readonly routeTable: ITransitGatewayRouteTable; + public readonly destinationCidrBlock: string; + + constructor(scope: Construct, id: string, props: TransitGatewayBlackholeRouteProps) { + super(scope, id); + + const resource = new CfnTransitGatewayRoute(this, id, { + blackhole: true, + destinationCidrBlock: props.destinationCidrBlock, + transitGatewayRouteTableId: props.transitGatewayRouteTable.routeTableId, + }); + + this.node.defaultChild = resource; + this.destinationCidrBlock = resource.destinationCidrBlock; + this.routeTable = props.transitGatewayRouteTable; + } +} diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-vpc-attachment.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-vpc-attachment.ts new file mode 100644 index 0000000000000..95f4682a7efa1 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway-vpc-attachment.ts @@ -0,0 +1,200 @@ +import { ITransitGateway } from './transit-gateway'; +import { CfnTransitGatewayAttachment, ISubnet, IVpc } from 'aws-cdk-lib/aws-ec2'; +import { Construct } from 'constructs'; +import { TransitGatewayRouteTableAssociation } from './transit-gateway-route-table-association'; +import { TransitGatewayRouteTablePropagation } from './transit-gateway-route-table-propagation'; +import { ITransitGatewayAttachment, TransitGatewayAttachmentBase } from './transit-gateway-attachment'; +import { getFeatureStatus } from './util'; +import { ITransitGatewayRouteTable } from './transit-gateway-route-table'; +import { Annotations } from 'aws-cdk-lib'; + +/** + * Options for Transit Gateway VPC Attachment. + */ +export interface ITransitGatewayVpcAttachmentOptions { + /** + * Enable or disable appliance mode support. + * + * @default - disable (false) + */ + readonly applianceModeSupport?: boolean; + + /** + * Enable or disable DNS support. + * + * @default - disable (false) + */ + readonly dnsSupport?: boolean; + + /** + * Enable or disable IPv6 support. + * + * @default - disable (false) + */ + readonly ipv6Support?: boolean; + + /** + * Enables you to reference a security group across VPCs attached to a transit gateway. + * + * @default - disable (false) + */ + readonly securityGroupReferencingSupport?: boolean; +} + +/** + * Represents a Transit Gateway VPC Attachment. + */ +export interface ITransitGatewayVpcAttachment extends ITransitGatewayAttachment { + /** + * Add additional subnets to this attachment. + */ + addSubnets(subnets: ISubnet[]): void; + + /** + * Remove subnets from this attachment. + */ + removeSubnets(subnets: ISubnet[]): void; +} + +/** + * Base class for Transit Gateway VPC Attachment. + */ +interface BaseTransitGatewayVpcAttachmentProps { + /** + * A list of one or more subnets to place the attachment in. + * It is recommended to specify more subnets for better availability. + */ + readonly subnets: ISubnet[]; + + /** + * A VPC attachment(s) will get assigned to. + */ + readonly vpc: IVpc; + + /** + * The VPC attachment options. + * + * @default - All options are disabled. + */ + readonly vpcAttachmentOptions?: ITransitGatewayVpcAttachmentOptions; + + /** + * Physical name of this Transit Gateway VPC Attachment. + * + * @default - Assigned by CloudFormation. + */ + readonly transitGatewayAttachmentName?: string; +} + +/** + * Common properties for creating a Transit Gateway VPC Attachment resource. + */ +export interface TransitGatewayVpcAttachmentProps extends BaseTransitGatewayVpcAttachmentProps { + /** + * The transit gateway this attachment gets assigned to. + */ + readonly transitGateway: ITransitGateway; +} + +/** + * Options for creating an Attachment via the attachVpc() method. + */ +export interface AttachVpcOptions extends BaseTransitGatewayVpcAttachmentProps { + /** + * An optional route table to associate with this VPC attachment. + * + * @default - No associations will be created unless it is for the default route table and automatic association is enabled. + */ + readonly associationRouteTable?: ITransitGatewayRouteTable; + + /** + * A list of optional route tables to propagate routes to. + * + * @default - No propagations will be created unless it is for the default route table and automatic propagation is enabled. + */ + readonly propagationRouteTables?: ITransitGatewayRouteTable[]; +} + +/** + * Creates a Transit Gateway VPC Attachment. + * + * @resource AWS::EC2::TransitGatewayAttachment + */ +export class TransitGatewayVpcAttachment extends TransitGatewayAttachmentBase implements ITransitGatewayVpcAttachment { + public readonly transitGatewayAttachmentId: string; + private readonly subnets: ISubnet[] = []; + + /** + * The AWS CloudFormation resource representing the Transit Gateway Attachment. + */ + private readonly _resource: CfnTransitGatewayAttachment; + + constructor(scope: Construct, id: string, props: TransitGatewayVpcAttachmentProps) { + super(scope, id); + + this._resource = new CfnTransitGatewayAttachment(this, id, { + subnetIds: props.subnets.map((subnet) => subnet.subnetId), + transitGatewayId: props.transitGateway.transitGatewayId, + vpcId: props.vpc.vpcId, + options: props.vpcAttachmentOptions ? { + ApplianceModeSupport: getFeatureStatus(props.vpcAttachmentOptions?.applianceModeSupport), + DnsSupport: getFeatureStatus(props.vpcAttachmentOptions?.dnsSupport), + Ipv6Support: getFeatureStatus(props.vpcAttachmentOptions?.ipv6Support), + SecurityGroupReferencingSupport: getFeatureStatus(props.vpcAttachmentOptions?.securityGroupReferencingSupport), + } : undefined, + }); + this.node.defaultChild = this._resource; + + this.transitGatewayAttachmentId = this._resource.attrId; + this.subnets = props.subnets; + + if (props.vpcAttachmentOptions?.dnsSupport && !props.transitGateway.dnsSupport) { + Annotations.of(this).addWarningV2('@aws-cdk/aws-ec2:transitGatewayDnsSupportMismatch', '\'DnsSupport\' is enabled for the VPC Attachment but disabled on the TransitGateway. The feature will not work unless \'DnsSupport\' is enabled on both.'); + } + + if (props.vpcAttachmentOptions?.securityGroupReferencingSupport && !props.transitGateway.securityGroupReferencingSupport) { + Annotations.of(this).addWarningV2('@aws-cdk/aws-ec2:transitGatewaySecurityGroupReferencingSupportMismatch', '\'SecurityGroupReferencingSupport\' is enabled for the VPC Attachment but disabled on the TransitGateway. The feature will not work unless \'SecurityGroupReferencingSupport\' is enabled on both.'); + } + + if (props.transitGateway.defaultRouteTableAssociation) { + new TransitGatewayRouteTableAssociation(this, id + 'Association', { + transitGatewayVpcAttachment: this, + transitGatewayRouteTable: props.transitGateway.defaultRouteTable, + }); + } + + if (props.transitGateway.defaultRouteTablePropagation) { + new TransitGatewayRouteTablePropagation(this, id + 'Propagation', { + transitGatewayVpcAttachment: this, + transitGatewayRouteTable: props.transitGateway.defaultRouteTable, + }); + } + } + + /** + * Add additional subnets to this attachment. + */ + addSubnets(subnets: ISubnet[]): void { + for (const subnet of subnets) { + if (this.subnets.some(existing => existing.subnetId === subnet.subnetId)) { + throw new Error(`Subnet with ID ${subnet.subnetId} is already added to the Attachment ${this.transitGatewayAttachmentId}.`); + } + this.subnets.push(subnet); + } + this._resource.subnetIds = this.subnets.map(subnet => subnet.subnetId); + } + + /** + * Remove additional subnets to this attachment. + */ + removeSubnets(subnets: ISubnet[]): void { + for (const subnet of subnets) { + const index = this.subnets.findIndex(existing => existing.subnetId === subnet.subnetId); + if (index === -1) { + throw new Error(`Subnet with ID ${subnet.subnetId} does not exist in the Attachment ${this.transitGatewayAttachmentId}.`); + } + this.subnets.splice(index, 1); + } + this._resource.subnetIds = this.subnets.map(subnet => subnet.subnetId); + } +} diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway.ts new file mode 100644 index 0000000000000..b02c267681fa8 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/transit-gateway.ts @@ -0,0 +1,254 @@ +import { CfnTransitGateway, RouterType } from 'aws-cdk-lib/aws-ec2'; +import * as cdk from 'aws-cdk-lib/core'; +import { Construct } from 'constructs'; +import { ITransitGatewayRouteTable, TransitGatewayRouteTable } from './transit-gateway-route-table'; +import { TransitGatewayVpcAttachment, AttachVpcOptions, ITransitGatewayVpcAttachment } from './transit-gateway-vpc-attachment'; +import { IRouteTarget } from './route'; +import { TransitGatewayRouteTableAssociation } from './transit-gateway-route-table-association'; +import { TransitGatewayRouteTablePropagation } from './transit-gateway-route-table-propagation'; +import { getFeatureStatus, TransitGatewayFeatureStatus } from './util'; + +/** + * Represents a Transit Gateway. + */ +export interface ITransitGateway extends cdk.IResource, IRouteTarget { + /** + * The unique identifier of the Transit Gateway. + * + * This ID is automatically assigned by AWS upon creation of the Transit Gateway + * and is used to reference it in various configurations and operations. + * @attribute + */ + readonly transitGatewayId: string; + + /** + * The Amazon Resource Name (ARN) of the Transit Gateway. + * + * The ARN uniquely identifies the Transit Gateway across AWS and is commonly + * used for permissions and resource tracking. + * @attribute + */ + readonly transitGatewayArn: string; + + /** + * The default route table associated with the Transit Gateway. + * + * This route table is created by the CDK and is used to manage the routes + * for attachments that do not have an explicitly defined route table association. + */ + readonly defaultRouteTable: ITransitGatewayRouteTable; + + /** + * Indicates whether new attachments are automatically associated with the default route table. + * + * If set to `true`, any VPC or VPN attachment will be automatically associated with + * the default route table unless otherwise specified. + */ + readonly defaultRouteTableAssociation: boolean; + + /** + * Indicates whether route propagation to the default route table is enabled. + * + * When set to `true`, routes from attachments will be automatically propagated + * to the default route table unless propagation is explicitly disabled. + */ + readonly defaultRouteTablePropagation: boolean; + + /** + * Whether or not DNS support is enabled on the Transit Gateway. + */ + readonly dnsSupport: boolean; + + /** + * Whether or not security group referencing support is enabled on the Transit Gateway. + */ + readonly securityGroupReferencingSupport: boolean; +} + +/** + * Common properties for creating a Transit Gateway resource. + */ +export interface TransitGatewayProps { + /** + * Physical name of this Transit Gateway. + * + * @default - Assigned by CloudFormation. + */ + readonly transitGatewayName?: string; + + /** + * A private Autonomous System Number (ASN) for the Amazon side of a BGP session. The range is 64512 to 65534 for 16-bit ASNs. + * + * @default - undefined, 64512 is assigned by CloudFormation. + */ + readonly amazonSideAsn?: number; + + /** + * Enable or disable automatic acceptance of cross-account attachment requests. + * + * @default - disable (false) + */ + readonly autoAcceptSharedAttachments?: boolean; + + /** + * Enable or disable automatic association with the default association route table. + * + * @default - enable (true) + */ + readonly defaultRouteTableAssociation?: boolean; + + /** + * Enable or disable automatic propagation of routes to the default propagation route table. + * + * @default - enable (true) + */ + readonly defaultRouteTablePropagation?: boolean; + + /** + * The description of the transit gateway. + * + * @default - no description + */ + readonly description?: string; + + /** + * Enable or disable DNS support. + * + * If dnsSupport is enabled on a VPC Attachment, this also needs to be enabled for the feature to work. + * Otherwise the resources will still deploy but the feature will not work. + * + * @default - enable (true) + */ + readonly dnsSupport?: boolean; + + /** + * Enable or disable security group referencing support + * + * If securityGroupReferencingSupport is enabled on a VPC Attachment, this also needs to be enabled for the feature to work. + * Otherwise the resources will still deploy but the feature will not work. + * + * @default - disable (false) + */ + readonly securityGroupReferencingSupport?: boolean; + + /** + * The transit gateway CIDR blocks. + * + * @default - none + */ + readonly transitGatewayCidrBlocks?: string[]; +} + +/** + * A Transit Gateway. + * @internal + */ +abstract class TransitGatewayBase extends cdk.Resource implements ITransitGateway, IRouteTarget { + public abstract readonly routerType: RouterType; + public abstract readonly routerTargetId: string; + public abstract readonly transitGatewayId: string; + public abstract readonly transitGatewayArn: string; + public abstract readonly defaultRouteTable: ITransitGatewayRouteTable; + public abstract readonly defaultRouteTableAssociation: boolean; + public abstract readonly defaultRouteTablePropagation: boolean; + public abstract readonly dnsSupport: boolean; + public abstract readonly securityGroupReferencingSupport: boolean; + + /** + * Adds a new route table to the Transit Gateway. + * + * @returns The created Transit Gateway route table. + */ + public addRouteTable(id: string): ITransitGatewayRouteTable { + return new TransitGatewayRouteTable(this, id, { + transitGateway: this, + }); + } + + /** + * Attaches a VPC to the Transit Gateway. + * + * @returns The created Transit Gateway VPC attachment. + */ + public attachVpc(id: string, options: AttachVpcOptions): ITransitGatewayVpcAttachment { + const attachment = new TransitGatewayVpcAttachment(this, id, { + transitGateway: this, + vpc: options.vpc, + subnets: options.subnets, + vpcAttachmentOptions: options.vpcAttachmentOptions ?? undefined, + }); + + // If `associationRouteTable` is provided, skip creating the Association only if `associationRouteTable` is the default route table and + // automatic association (`defaultRouteTableAssociation`) is enabled, as the TransitGatewayRouteTableAttachment's constructor will handle it in that case. + if (options.associationRouteTable && !(this.defaultRouteTable === options.associationRouteTable && this.defaultRouteTableAssociation)) { + new TransitGatewayRouteTableAssociation(this, `${id}-Association-${this.node.addr}`, { + transitGatewayVpcAttachment: attachment, + transitGatewayRouteTable: options.associationRouteTable, + }); + } + + if (options.propagationRouteTables) { + options.propagationRouteTables.forEach((propagationRouteTable, index) => { + // If `propagationRouteTable` is provided, skip creating the Propagation only if `propagationRouteTable` is the default route table and + // automatic propagation (`defaultRouteTablePropagation`) is enabled, as the TransitGatewayRouteTableAttachment's constructor will handle it in that case. + if (!(this.defaultRouteTable === propagationRouteTable && this.defaultRouteTablePropagation)) { + new TransitGatewayRouteTablePropagation(this, `${id}-Propagation-${index}`, { + transitGatewayVpcAttachment: attachment, + transitGatewayRouteTable: propagationRouteTable, + }); + } + }); + } + + return attachment; + } +} + +/** + * Creates a Transit Gateway. + * + * @resource AWS::EC2::TransitGateway + */ +export class TransitGateway extends TransitGatewayBase { + public readonly routerType: RouterType; + public readonly routerTargetId: string; + public readonly transitGatewayId: string; + public readonly transitGatewayArn: string; + public readonly defaultRouteTable: ITransitGatewayRouteTable; + public readonly defaultRouteTableAssociation: boolean; + public readonly defaultRouteTablePropagation: boolean; + public readonly dnsSupport: boolean; + public readonly securityGroupReferencingSupport: boolean; + + constructor(scope: Construct, id: string, props: TransitGatewayProps = {}) { + super(scope, id); + + const resource = new CfnTransitGateway(this, id, { + amazonSideAsn: props.amazonSideAsn ?? undefined, + autoAcceptSharedAttachments: getFeatureStatus(props.autoAcceptSharedAttachments), + // Default Association/Propagation will always be false when creating the L1 to prevent EC2 from creating the default route table. + // Instead, CDK will create a custom default route table and use the properties to mimic the automatic assocation/propagation behaviour. + defaultRouteTableAssociation: TransitGatewayFeatureStatus.DISABLE, + defaultRouteTablePropagation: TransitGatewayFeatureStatus.DISABLE, + transitGatewayCidrBlocks: props.transitGatewayCidrBlocks, + description: props.description, + dnsSupport: getFeatureStatus(props.dnsSupport), + securityGroupReferencingSupport: getFeatureStatus(props.securityGroupReferencingSupport), + }); + + this.node.defaultChild = resource; + this.transitGatewayId = resource.attrId; + this.routerTargetId = resource.attrId; + this.routerType = RouterType.TRANSIT_GATEWAY; + this.transitGatewayArn = resource.attrTransitGatewayArn; + this.dnsSupport = props.dnsSupport ?? true; + this.securityGroupReferencingSupport = props.securityGroupReferencingSupport ?? false; + + this.defaultRouteTable = new TransitGatewayRouteTable(this, 'DefaultRouteTable', { + transitGateway: this, + }); + + this.defaultRouteTableAssociation = props.defaultRouteTableAssociation ?? true; + this.defaultRouteTablePropagation = props.defaultRouteTablePropagation ?? true; + } +} diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts index 4ebf5e22036c5..f39e7de4e84bf 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts @@ -18,6 +18,29 @@ export function subnetId(name: string, i: number) { return `${name}Subnet${i + 1}`; } +/** + * The status of a Transit Gateway feature. + */ +export enum TransitGatewayFeatureStatus { + /** + * The feature is enabled. + */ + ENABLE = 'enable', + + /** + * The feature is disabled. + */ + DISABLE = 'disable', +} + +export function getFeatureStatus(status?: boolean): TransitGatewayFeatureStatus | undefined { + if (status === undefined) { + return undefined; + } else { + return status ? TransitGatewayFeatureStatus.ENABLE : TransitGatewayFeatureStatus.DISABLE; + } +} + /** * Return the union of table IDs from all selected subnets */ diff --git a/packages/@aws-cdk/aws-ec2-alpha/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-ec2-alpha/rosetta/default.ts-fixture index 5704158e8073c..3e29ae294b046 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-ec2-alpha/rosetta/default.ts-fixture @@ -1,7 +1,7 @@ // Fixture with packages imported, but nothing else import { Construct } from 'constructs'; import { Stack, App, Fn, Tags } from 'aws-cdk-lib'; -import { VpcV2, SubnetV2, IpAddresses, IpamPoolPublicIpSource, RouteTable, InternetGateway, Route, NatGateway, EgressOnlyInternetGateway, VPCPeeringConnection } from '@aws-cdk/aws-ec2-alpha'; +import { VpcV2, SubnetV2, IpAddresses, IpamPoolPublicIpSource, RouteTable, InternetGateway, Route, NatGateway, EgressOnlyInternetGateway, VPCPeeringConnection, TransitGateway, TransitGatewayRoute, TransitGatewayRouteTable, TransitGatewayVpcAttachment } from '@aws-cdk/aws-ec2-alpha'; import { Ipam, AwsServiceName, IpCidr, AddressFamily } from '@aws-cdk/aws-ec2-alpha'; import { NatConnectivityType } from '@aws-cdk/aws-ec2-alpha'; import { SubnetType, VpnConnectionType } from 'aws-cdk-lib/aws-ec2'; diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.assets.json new file mode 100644 index 0000000000000..d3c16ab80f40b --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.assets.json @@ -0,0 +1,19 @@ +{ + "version": "39.0.0", + "files": { + "abbff3a0484dd36d58296bffebd14d57a957f5202e481df8ef1ba150a399c149": { + "source": { + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "abbff3a0484dd36d58296bffebd14d57a957f5202e481df8ef1ba150a399c149.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.template.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.template.json new file mode 100644 index 0000000000000..5d42c0f4971ba --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.template.json @@ -0,0 +1,223 @@ +{ + "Resources": { + "SubnetTest3296A161": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.1.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/SubnetTest" + } + ] + } + }, + "SubnetTestSecondaryTest2AB12223": { + "Type": "AWS::EC2::VPCCidrBlock", + "Properties": { + "AmazonProvidedIpv6CidrBlock": true, + "VpcId": { + "Fn::GetAtt": [ + "SubnetTest3296A161", + "VpcId" + ] + } + } + }, + "testSubnet1Subnet72087287": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AssignIpv6AddressOnCreation": false, + "AvailabilityZone": "us-east-1a", + "CidrBlock": "10.1.0.0/20", + "VpcId": { + "Fn::GetAtt": [ + "SubnetTest3296A161", + "VpcId" + ] + } + }, + "DependsOn": [ + "SubnetTestSecondaryTest2AB12223" + ] + }, + "testSubnet1RouteTableB5FDDF81": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "DefaultCDKRouteTable" + } + ], + "VpcId": { + "Fn::GetAtt": [ + "SubnetTest3296A161", + "VpcId" + ] + } + }, + "DependsOn": [ + "SubnetTestSecondaryTest2AB12223" + ] + }, + "testSubnet1RouteTableAssociation1DA9E185": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Fn::GetAtt": [ + "testSubnet1RouteTableB5FDDF81", + "RouteTableId" + ] + }, + "SubnetId": { + "Ref": "testSubnet1Subnet72087287" + } + }, + "DependsOn": [ + "SubnetTestSecondaryTest2AB12223" + ] + }, + "TransitGateway11B93D57": { + "Type": "AWS::EC2::TransitGateway", + "Properties": { + "DefaultRouteTableAssociation": "disable", + "DefaultRouteTablePropagation": "disable" + } + }, + "TransitGatewayDefaultRouteTable608EC117": { + "Type": "AWS::EC2::TransitGatewayRouteTable", + "Properties": { + "TransitGatewayId": { + "Fn::GetAtt": [ + "TransitGateway11B93D57", + "Id" + ] + } + } + }, + "TransitGatewayDefaultRouteTabledefaultRTBRouteTransitGatewayRouteAB7CA8FF": { + "Type": "AWS::EC2::TransitGatewayRoute", + "Properties": { + "Blackhole": false, + "DestinationCidrBlock": "0.0.0.0/0", + "TransitGatewayAttachmentId": { + "Fn::GetAtt": [ + "TransitGatewayDefaultRtbAttachment6E19DB55", + "Id" + ] + }, + "TransitGatewayRouteTableId": { + "Fn::GetAtt": [ + "TransitGatewayDefaultRouteTable608EC117", + "TransitGatewayRouteTableId" + ] + } + } + }, + "TransitGatewayDefaultRtbAttachment6E19DB55": { + "Type": "AWS::EC2::TransitGatewayAttachment", + "Properties": { + "SubnetIds": [ + { + "Ref": "testSubnet1Subnet72087287" + } + ], + "TransitGatewayId": { + "Fn::GetAtt": [ + "TransitGateway11B93D57", + "Id" + ] + }, + "VpcId": { + "Fn::GetAtt": [ + "SubnetTest3296A161", + "VpcId" + ] + } + } + }, + "TransitGatewayRouteTable2047E2A04": { + "Type": "AWS::EC2::TransitGatewayRouteTable", + "Properties": { + "TransitGatewayId": { + "Fn::GetAtt": [ + "TransitGateway11B93D57", + "Id" + ] + } + } + }, + "TransitGatewayRouteTable2customRtbAssociation353F1A21": { + "Type": "AWS::EC2::TransitGatewayRouteTableAssociation", + "Properties": { + "TransitGatewayAttachmentId": { + "Fn::GetAtt": [ + "TransitGatewayDefaultRtbAttachment6E19DB55", + "Id" + ] + }, + "TransitGatewayRouteTableId": { + "Fn::GetAtt": [ + "TransitGatewayRouteTable2047E2A04", + "TransitGatewayRouteTableId" + ] + } + } + }, + "TransitGatewayRouteTable2customRtbPropagation45784C74": { + "Type": "AWS::EC2::TransitGatewayRouteTablePropagation", + "Properties": { + "TransitGatewayAttachmentId": { + "Fn::GetAtt": [ + "TransitGatewayDefaultRtbAttachment6E19DB55", + "Id" + ] + }, + "TransitGatewayRouteTableId": { + "Fn::GetAtt": [ + "TransitGatewayRouteTable2047E2A04", + "TransitGatewayRouteTableId" + ] + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/cdk.out new file mode 100644 index 0000000000000..91e1a8b9901d5 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"39.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integ.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integ.json new file mode 100644 index 0000000000000..5365b390e3274 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "39.0.0", + "testCases": { + "integtest-model/DefaultTest": { + "stacks": [ + "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings" + ], + "assertionStack": "integtest-model/DefaultTest/DeployAssert", + "assertionStackName": "integtestmodelDefaultTestDeployAssertCF40BD53" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json new file mode 100644 index 0000000000000..928ed61bb88d0 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json @@ -0,0 +1,19 @@ +{ + "version": "39.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "integtestmodelDefaultTestDeployAssertCF40BD53.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.template.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/manifest.json new file mode 100644 index 0000000000000..c6ed1632e2450 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/manifest.json @@ -0,0 +1,179 @@ +{ + "version": "39.0.0", + "artifacts": { + "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/abbff3a0484dd36d58296bffebd14d57a957f5202e481df8ef1ba150a399c149.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings.assets" + ], + "metadata": { + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/SubnetTest/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SubnetTest3296A161" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/SubnetTest/SecondaryTest/SecondaryTest": [ + { + "type": "aws:cdk:logicalId", + "data": "SubnetTestSecondaryTest2AB12223" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/testSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "testSubnet1Subnet72087287" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/testSubnet1/RouteTable/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "testSubnet1RouteTableB5FDDF81" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/testSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "testSubnet1RouteTableAssociation1DA9E185" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/TransitGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "TransitGateway11B93D57" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/DefaultRouteTable/DefaultRouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "TransitGatewayDefaultRouteTable608EC117" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/DefaultRouteTable/defaultRTBRoute/TransitGatewayRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "TransitGatewayDefaultRouteTabledefaultRTBRouteTransitGatewayRouteAB7CA8FF" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/DefaultRtbAttachment/DefaultRtbAttachment": [ + { + "type": "aws:cdk:logicalId", + "data": "TransitGatewayDefaultRtbAttachment6E19DB55" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/RouteTable2/RouteTable2": [ + { + "type": "aws:cdk:logicalId", + "data": "TransitGatewayRouteTable2047E2A04" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/RouteTable2/customRtbAssociation/customRtbAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "TransitGatewayRouteTable2customRtbAssociation353F1A21" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/RouteTable2/customRtbPropagation/customRtbPropagation": [ + { + "type": "aws:cdk:logicalId", + "data": "TransitGatewayRouteTable2customRtbPropagation45784C74" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings" + }, + "integtestmodelDefaultTestDeployAssertCF40BD53.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integtestmodelDefaultTestDeployAssertCF40BD53.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integtestmodelDefaultTestDeployAssertCF40BD53": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integtestmodelDefaultTestDeployAssertCF40BD53.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integtestmodelDefaultTestDeployAssertCF40BD53.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integtestmodelDefaultTestDeployAssertCF40BD53.assets" + ], + "metadata": { + "/integtest-model/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integtest-model/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integtest-model/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/tree.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/tree.json new file mode 100644 index 0000000000000..ba349ddb8e702 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.js.snapshot/tree.json @@ -0,0 +1,491 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings": { + "id": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings", + "children": { + "SubnetTest": { + "id": "SubnetTest", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/SubnetTest", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/SubnetTest/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.1.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/SubnetTest" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "SecondaryTest": { + "id": "SecondaryTest", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/SubnetTest/SecondaryTest", + "children": { + "SecondaryTest": { + "id": "SecondaryTest", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/SubnetTest/SecondaryTest/SecondaryTest", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCCidrBlock", + "aws:cdk:cloudformation:props": { + "amazonProvidedIpv6CidrBlock": true, + "vpcId": { + "Fn::GetAtt": [ + "SubnetTest3296A161", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCCidrBlock", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.VpcV2", + "version": "0.0.0" + } + }, + "testSubnet1": { + "id": "testSubnet1", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/testSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/testSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "assignIpv6AddressOnCreation": false, + "availabilityZone": "us-east-1a", + "cidrBlock": "10.1.0.0/20", + "vpcId": { + "Fn::GetAtt": [ + "SubnetTest3296A161", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/testSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/testSubnet1/RouteTable", + "children": { + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/testSubnet1/RouteTable/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "DefaultCDKRouteTable" + } + ], + "vpcId": { + "Fn::GetAtt": [ + "SubnetTest3296A161", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.RouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/testSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Fn::GetAtt": [ + "testSubnet1RouteTableB5FDDF81", + "RouteTableId" + ] + }, + "subnetId": { + "Ref": "testSubnet1Subnet72087287" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.SubnetV2", + "version": "0.0.0" + } + }, + "TransitGateway": { + "id": "TransitGateway", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway", + "children": { + "TransitGateway": { + "id": "TransitGateway", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/TransitGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::TransitGateway", + "aws:cdk:cloudformation:props": { + "defaultRouteTableAssociation": "disable", + "defaultRouteTablePropagation": "disable" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnTransitGateway", + "version": "0.0.0" + } + }, + "DefaultRouteTable": { + "id": "DefaultRouteTable", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/DefaultRouteTable", + "children": { + "DefaultRouteTable": { + "id": "DefaultRouteTable", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/DefaultRouteTable/DefaultRouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::TransitGatewayRouteTable", + "aws:cdk:cloudformation:props": { + "transitGatewayId": { + "Fn::GetAtt": [ + "TransitGateway11B93D57", + "Id" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnTransitGatewayRouteTable", + "version": "0.0.0" + } + }, + "defaultRTBRoute": { + "id": "defaultRTBRoute", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/DefaultRouteTable/defaultRTBRoute", + "children": { + "TransitGatewayRoute": { + "id": "TransitGatewayRoute", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/DefaultRouteTable/defaultRTBRoute/TransitGatewayRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::TransitGatewayRoute", + "aws:cdk:cloudformation:props": { + "blackhole": false, + "destinationCidrBlock": "0.0.0.0/0", + "transitGatewayAttachmentId": { + "Fn::GetAtt": [ + "TransitGatewayDefaultRtbAttachment6E19DB55", + "Id" + ] + }, + "transitGatewayRouteTableId": { + "Fn::GetAtt": [ + "TransitGatewayDefaultRouteTable608EC117", + "TransitGatewayRouteTableId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnTransitGatewayRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.TransitGatewayRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.TransitGatewayRouteTable", + "version": "0.0.0" + } + }, + "DefaultRtbAttachment": { + "id": "DefaultRtbAttachment", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/DefaultRtbAttachment", + "children": { + "DefaultRtbAttachment": { + "id": "DefaultRtbAttachment", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/DefaultRtbAttachment/DefaultRtbAttachment", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::TransitGatewayAttachment", + "aws:cdk:cloudformation:props": { + "subnetIds": [ + { + "Ref": "testSubnet1Subnet72087287" + } + ], + "transitGatewayId": { + "Fn::GetAtt": [ + "TransitGateway11B93D57", + "Id" + ] + }, + "vpcId": { + "Fn::GetAtt": [ + "SubnetTest3296A161", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnTransitGatewayAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.TransitGatewayVpcAttachment", + "version": "0.0.0" + } + }, + "RouteTable2": { + "id": "RouteTable2", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/RouteTable2", + "children": { + "RouteTable2": { + "id": "RouteTable2", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/RouteTable2/RouteTable2", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::TransitGatewayRouteTable", + "aws:cdk:cloudformation:props": { + "transitGatewayId": { + "Fn::GetAtt": [ + "TransitGateway11B93D57", + "Id" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnTransitGatewayRouteTable", + "version": "0.0.0" + } + }, + "customRtbAssociation": { + "id": "customRtbAssociation", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/RouteTable2/customRtbAssociation", + "children": { + "customRtbAssociation": { + "id": "customRtbAssociation", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/RouteTable2/customRtbAssociation/customRtbAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::TransitGatewayRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "transitGatewayAttachmentId": { + "Fn::GetAtt": [ + "TransitGatewayDefaultRtbAttachment6E19DB55", + "Id" + ] + }, + "transitGatewayRouteTableId": { + "Fn::GetAtt": [ + "TransitGatewayRouteTable2047E2A04", + "TransitGatewayRouteTableId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnTransitGatewayRouteTableAssociation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.TransitGatewayRouteTableAssociation", + "version": "0.0.0" + } + }, + "customRtbPropagation": { + "id": "customRtbPropagation", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/RouteTable2/customRtbPropagation", + "children": { + "customRtbPropagation": { + "id": "customRtbPropagation", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/TransitGateway/RouteTable2/customRtbPropagation/customRtbPropagation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::TransitGatewayRouteTablePropagation", + "aws:cdk:cloudformation:props": { + "transitGatewayAttachmentId": { + "Fn::GetAtt": [ + "TransitGatewayDefaultRtbAttachment6E19DB55", + "Id" + ] + }, + "transitGatewayRouteTableId": { + "Fn::GetAtt": [ + "TransitGatewayRouteTable2047E2A04", + "TransitGatewayRouteTableId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnTransitGatewayRouteTablePropagation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.TransitGatewayRouteTablePropagation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.TransitGatewayRouteTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.TransitGateway", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "integtest-model": { + "id": "integtest-model", + "path": "integtest-model", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "integtest-model/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "integtest-model/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "integtest-model/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integtest-model/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integtest-model/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.ts b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.ts new file mode 100644 index 0000000000000..99e38d2fa6d00 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.transit-gateway.ts @@ -0,0 +1,57 @@ +import * as vpc_v2 from '../lib/vpc-v2'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as cdk from 'aws-cdk-lib'; +import { TransitGateway } from '../lib/transit-gateway'; +import { SubnetType } from 'aws-cdk-lib/aws-ec2'; +import { IpCidr, SubnetV2 } from '../lib'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-ec2-alpha-transit-gateway-disable-automatic-settings'); + +const vpc = new vpc_v2.VpcV2(stack, 'SubnetTest', { + primaryAddressBlock: vpc_v2.IpAddresses.ipv4('10.1.0.0/16'), + secondaryAddressBlocks: [vpc_v2.IpAddresses.amazonProvidedIpv6( { + cidrBlockName: 'SecondaryTest', + })], + enableDnsHostnames: true, + enableDnsSupport: true, +}); + +const subnet = new SubnetV2(stack, 'testSubnet1', { + vpc, + availabilityZone: 'us-east-1a', + ipv4CidrBlock: new IpCidr('10.1.0.0/20'), + subnetType: SubnetType.PRIVATE_ISOLATED, +}); + +// Create a Transit Gateway with +const tgw = new TransitGateway(stack, 'TransitGateway', { + defaultRouteTableAssociation: false, + defaultRouteTablePropagation: false, +}); + +// Can attach a VPC to the Transit Gateway +const attachment = tgw.attachVpc('DefaultRtbAttachment', { + vpc: vpc, + subnets: [subnet], +}); + +// Can add additional route tables to the Transit Gateway +const customRtb = tgw.addRouteTable('RouteTable2'); + +// Can add a static route to the Transit Gateway Route Table +tgw.defaultRouteTable.addRoute('defaultRTBRoute', attachment, '0.0.0.0/0'); + +// Add an Association and enable Propagation from the attachment to the custom Route Table +customRtb.addAssociation('customRtbAssociation', attachment); + +// This will propagate the defaultRTBRoute to the custom route table. +// The propagation is done dynamically and is reflected in the AWS console but not in the generated CFN template. +// Run this test with --no-clean flag to confirm that the route is in both route tables. +customRtb.enablePropagation('customRtbPropagation', attachment); + +new IntegTest(app, 'integtest-model', { + testCases: [stack], +}); + diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table-association.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table-association.test.ts new file mode 100644 index 0000000000000..90ec35322f9d6 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table-association.test.ts @@ -0,0 +1,60 @@ +import { Template } from 'aws-cdk-lib/assertions'; +import { Stack } from 'aws-cdk-lib/core'; +import * as vpc from '../lib'; +import { TransitGateway } from '../lib/transit-gateway'; +import { ITransitGatewayRouteTable } from '../lib/transit-gateway-route-table'; +import { TransitGatewayVpcAttachment } from '../lib/transit-gateway-vpc-attachment'; +import { TransitGatewayRouteTableAssociation } from '../lib/transit-gateway-route-table-association'; +import * as subnet from '../lib/subnet-v2'; +import { SubnetType } from 'aws-cdk-lib/aws-ec2'; + +describe('TransitGatewayRouteTableAssociation', () => { + let stack: Stack; + let myVpc: vpc.VpcV2; + let transitGateway: TransitGateway; + let routeTable: ITransitGatewayRouteTable; + let attachment: TransitGatewayVpcAttachment; + let mySubnet: vpc.SubnetV2; + + beforeEach(() => { + stack = new Stack(); + myVpc = new vpc.VpcV2(stack, 'VpcA', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv4('10.1.0.0/16', { cidrBlockName: 'TempSecondaryBlock' })], + }); + + mySubnet = new subnet.SubnetV2(stack, 'TestSubnet', { + vpc: myVpc, + availabilityZone: 'us-east-1a', + ipv4CidrBlock: new subnet.IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }); + + transitGateway = new TransitGateway(stack, 'TransitGateway', { + defaultRouteTableAssociation: false, + defaultRouteTablePropagation: false, + }); + + routeTable = transitGateway.addRouteTable('RouteTable'); + + attachment = new TransitGatewayVpcAttachment(stack, 'TransitGatewayVpcAttachment', { + vpc: myVpc, + transitGateway: transitGateway, + subnets: [mySubnet], + }); + }); + + test('creates association with required properties', () => { + // WHEN + new TransitGatewayRouteTableAssociation(stack, 'TransitGatewayRouteTableAssociation', { + transitGatewayRouteTable: routeTable, + transitGatewayVpcAttachment: attachment, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRouteTableAssociation', { + TransitGatewayAttachmentId: stack.resolve(attachment.transitGatewayAttachmentId), + TransitGatewayRouteTableId: stack.resolve(routeTable.routeTableId), + }); + }); +}); diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table-propagation.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table-propagation.test.ts new file mode 100644 index 0000000000000..4a71402416743 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table-propagation.test.ts @@ -0,0 +1,60 @@ +import { Template } from 'aws-cdk-lib/assertions'; +import { Stack } from 'aws-cdk-lib/core'; +import * as vpc from '../lib'; +import { TransitGateway } from '../lib/transit-gateway'; +import { ITransitGatewayRouteTable } from '../lib/transit-gateway-route-table'; +import { TransitGatewayVpcAttachment } from '../lib/transit-gateway-vpc-attachment'; +import { TransitGatewayRouteTablePropagation } from '../lib/transit-gateway-route-table-propagation'; +import * as subnet from '../lib/subnet-v2'; +import { SubnetType } from 'aws-cdk-lib/aws-ec2'; + +describe('TransitGatewayRouteTablePropagation', () => { + let stack: Stack; + let myVpc: vpc.VpcV2; + let transitGateway: TransitGateway; + let routeTable: ITransitGatewayRouteTable; + let attachment: TransitGatewayVpcAttachment; + let mySubnet: vpc.SubnetV2; + + beforeEach(() => { + stack = new Stack(); + myVpc = new vpc.VpcV2(stack, 'VpcA', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv4('10.1.0.0/16', { cidrBlockName: 'TempSecondaryBlock' })], + }); + + mySubnet = new subnet.SubnetV2(stack, 'TestSubnet', { + vpc: myVpc, + availabilityZone: 'us-east-1a', + ipv4CidrBlock: new subnet.IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }); + + transitGateway = new TransitGateway(stack, 'TransitGateway', { + defaultRouteTableAssociation: false, + defaultRouteTablePropagation: false, + }); + + routeTable = transitGateway.addRouteTable('RouteTable'); + + attachment = new TransitGatewayVpcAttachment(stack, 'TransitGatewayVpcAttachment', { + vpc: myVpc, + transitGateway: transitGateway, + subnets: [mySubnet], + }); + }); + + test('creates propagation with required properties', () => { + // WHEN + new TransitGatewayRouteTablePropagation(stack, 'TransitGatewayRouteTablePropagation', { + transitGatewayRouteTable: routeTable, + transitGatewayVpcAttachment: attachment, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRouteTablePropagation', { + TransitGatewayAttachmentId: stack.resolve(attachment.transitGatewayAttachmentId), + TransitGatewayRouteTableId: stack.resolve(routeTable.routeTableId), + }); + }); +}); diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table.test.ts new file mode 100644 index 0000000000000..810fe8bc9fcf7 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route-table.test.ts @@ -0,0 +1,177 @@ +import * as cdk from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { TransitGateway } from '../lib/transit-gateway'; +import * as vpc from '../lib/vpc-v2'; +import * as subnet from '../lib/subnet-v2'; +import { SubnetType } from 'aws-cdk-lib/aws-ec2'; +import { TransitGatewayRouteTable } from '../lib/transit-gateway-route-table'; +import { TransitGatewayVpcAttachment } from '../lib/transit-gateway-vpc-attachment'; + +describe('Transit Gateway Route Table', () => { + let stack: cdk.Stack; + let tgw: TransitGateway; + let myVpc: vpc.VpcV2; + let mySubnet: subnet.SubnetV2; + let attachment: TransitGatewayVpcAttachment; + // let rtb1: TransitGatewayRouteTable; + + beforeEach(() => { + const app = new cdk.App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + + stack = new cdk.Stack(app, 'TransitGatewayRouteTableStack', { + env: { + region: 'us-east-1', + }, + }); + + myVpc = new vpc.VpcV2(stack, 'VpcA', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv4('10.1.0.0/16', { cidrBlockName: 'TempSecondaryBlock' })], + }); + + mySubnet = new subnet.SubnetV2(stack, 'TestSubnet', { + vpc: myVpc, + availabilityZone: 'us-east-1a', + ipv4CidrBlock: new subnet.IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }); + + tgw = new TransitGateway(stack, 'TransitGateway', { + defaultRouteTableAssociation: false, + defaultRouteTablePropagation: false, + }); + + attachment = new TransitGatewayVpcAttachment(stack, 'TransitGatewayVpcAttachment', { + vpc: myVpc, + transitGateway: tgw, + subnets: [mySubnet], + }); + + new TransitGatewayRouteTable(stack, 'TransitGatewayRouteTable', { + transitGateway: tgw, + }); + }); + + test('creates a transit gateway route table', () => { + Template.fromStack(stack).resourcePropertiesCountIs('AWS::EC2::TransitGatewayRouteTable', { + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + }, 2); + + Template.fromStack(stack).templateMatches({ + Resources: { + TransitGateway11B93D57: { + Type: 'AWS::EC2::TransitGateway', + Properties: { + DefaultRouteTableAssociation: 'disable', + DefaultRouteTablePropagation: 'disable', + }, + }, + TransitGatewayDefaultRouteTable608EC117: { + Type: 'AWS::EC2::TransitGatewayRouteTable', + Properties: { + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + }, + }, + TransitGatewayRouteTableD2EDBDC1: { + Type: 'AWS::EC2::TransitGatewayRouteTable', + Properties: { + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + }, + }, + }, + }); + }); + + test('addRoute method creates a transit gateway active route and adds it to the transit gateway route table', () => { + tgw.defaultRouteTable.addRoute('TransitGatewayRoute', attachment, '10.0.0.0/16'); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRoute', { + Blackhole: false, + DestinationCidrBlock: '10.0.0.0/16', + TransitGatewayAttachmentId: { + 'Fn::GetAtt': [ + 'TransitGatewayVpcAttachment0B27B76B', + 'Id', + ], + }, + TransitGatewayRouteTableId: { + 'Fn::GetAtt': [ + 'TransitGatewayDefaultRouteTable608EC117', + 'TransitGatewayRouteTableId', + ], + }, + }); + }); + + test('addBlackholeRoute method creates a transit gateway blackhole route and adds it to the transit gateway route table', () => { + tgw.defaultRouteTable.addBlackholeRoute('TransitGatewayRoute', '10.0.0.0/16'); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRoute', { + Blackhole: true, + DestinationCidrBlock: '10.0.0.0/16', + TransitGatewayRouteTableId: { + 'Fn::GetAtt': [ + 'TransitGatewayDefaultRouteTable608EC117', + 'TransitGatewayRouteTableId', + ], + }, + }); + }); + + test('addAssociation method creates a transit gateway route table association', () => { + tgw.defaultRouteTable.addAssociation('TransitGatewayRoute', attachment); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRouteTableAssociation', { + TransitGatewayAttachmentId: { + 'Fn::GetAtt': [ + 'TransitGatewayVpcAttachment0B27B76B', + 'Id', + ], + }, + TransitGatewayRouteTableId: { + 'Fn::GetAtt': [ + 'TransitGatewayDefaultRouteTable608EC117', + 'TransitGatewayRouteTableId', + ], + }, + }); + }); + + test('enablePropagation method creates a transit gateway route table propagation', () => { + tgw.defaultRouteTable.enablePropagation('TransitGatewayRoute', attachment); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRouteTablePropagation', { + TransitGatewayAttachmentId: { + 'Fn::GetAtt': [ + 'TransitGatewayVpcAttachment0B27B76B', + 'Id', + ], + }, + TransitGatewayRouteTableId: { + 'Fn::GetAtt': [ + 'TransitGatewayDefaultRouteTable608EC117', + 'TransitGatewayRouteTableId', + ], + }, + }); + }); +}); diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route.test.ts new file mode 100644 index 0000000000000..57b5fffd14333 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-route.test.ts @@ -0,0 +1,125 @@ +import * as cdk from 'aws-cdk-lib'; +import { Match, Template } from 'aws-cdk-lib/assertions'; +import { TransitGateway } from '../lib/transit-gateway'; +import * as vpc from '../lib/vpc-v2'; +import * as subnet from '../lib/subnet-v2'; +import { SubnetType } from 'aws-cdk-lib/aws-ec2'; +import { TransitGatewayRouteTable } from '../lib/transit-gateway-route-table'; +import { TransitGatewayVpcAttachment } from '../lib/transit-gateway-vpc-attachment'; +import { TransitGatewayRoute, TransitGatewayBlackholeRoute } from '../lib/transit-gateway-route'; + +describe('Transit Gateway Route', () => { + let stack: cdk.Stack; + let tgw: TransitGateway; + let myVpc: vpc.VpcV2; + let mySubnet: subnet.SubnetV2; + let attachment: TransitGatewayVpcAttachment; + let routeTable: TransitGatewayRouteTable; + + beforeEach(() => { + const app = new cdk.App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + + stack = new cdk.Stack(app, 'TransitGatewayRouteTableStack', { + env: { + region: 'us-east-1', + }, + }); + + myVpc = new vpc.VpcV2(stack, 'VpcA', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv4('10.1.0.0/16', { cidrBlockName: 'TempSecondaryBlock' })], + }); + + mySubnet = new subnet.SubnetV2(stack, 'TestSubnet', { + vpc: myVpc, + availabilityZone: 'us-east-1a', + ipv4CidrBlock: new subnet.IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }); + + tgw = new TransitGateway(stack, 'TransitGateway', { + defaultRouteTableAssociation: false, + defaultRouteTablePropagation: false, + }); + + attachment = new TransitGatewayVpcAttachment(stack, 'TransitGatewayVpcAttachment', { + vpc: myVpc, + transitGateway: tgw, + subnets: [mySubnet], + }); + + routeTable = new TransitGatewayRouteTable(stack, 'TransitGatewayRouteTable', { + transitGateway: tgw, + }); + }); + + describe('TransitGatewayActiveRoute', () => { + test('creates a route with the correct properties', () => { + // WHEN + new TransitGatewayRoute(stack, 'ActiveRoute', { + transitGatewayAttachment: attachment, + destinationCidrBlock: '10.1.0.0/16', + transitGatewayRouteTable: routeTable, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRoute', { + Blackhole: false, + DestinationCidrBlock: '10.1.0.0/16', + TransitGatewayAttachmentId: { + 'Fn::GetAtt': [ + 'TransitGatewayVpcAttachment0B27B76B', + 'Id', + ], + }, + TransitGatewayRouteTableId: { + 'Fn::GetAtt': [ + 'TransitGatewayRouteTableD2EDBDC1', + 'TransitGatewayRouteTableId', + ], + }, + }); + }); + }); + + describe('TransitGatewayBlackholeRoute', () => { + test('creates a blackhole route with the correct properties', () => { + // WHEN + new TransitGatewayBlackholeRoute(stack, 'BlackholeRoute', { + destinationCidrBlock: '10.2.0.0/16', + transitGatewayRouteTable: routeTable, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRoute', { + Blackhole: true, + DestinationCidrBlock: '10.2.0.0/16', + TransitGatewayRouteTableId: { + 'Fn::GetAtt': [ + 'TransitGatewayRouteTableD2EDBDC1', + 'TransitGatewayRouteTableId', + ], + }, + }); + }); + + test('does not include TransitGatewayAttachmentId', () => { + // WHEN + new TransitGatewayBlackholeRoute(stack, 'BlackholeRoute', { + destinationCidrBlock: '10.2.0.0/16', + transitGatewayRouteTable: routeTable, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRoute', { + Blackhole: true, + DestinationCidrBlock: '10.2.0.0/16', + TransitGatewayAttachmentId: Match.absent(), + }); + }); + }); +}); diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-vpc-attachment.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-vpc-attachment.test.ts new file mode 100644 index 0000000000000..e1cd415b3fee8 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway-vpc-attachment.test.ts @@ -0,0 +1,195 @@ +import { Annotations, Match, Template } from 'aws-cdk-lib/assertions'; +import { Stack } from 'aws-cdk-lib/core'; +import * as vpc from '../lib'; +import { TransitGateway } from '../lib/transit-gateway'; +import * as subnet from '../lib/subnet-v2'; +import { SubnetType } from 'aws-cdk-lib/aws-ec2'; + +describe('TransitGatewayVpcAttachment', () => { + let stack: Stack; + let myVpc: vpc.VpcV2; + let transitGateway: TransitGateway; + let mySubnet: vpc.SubnetV2; + + beforeEach(() => { + stack = new Stack(); + myVpc = new vpc.VpcV2(stack, 'VpcA', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv4('10.1.0.0/16', { cidrBlockName: 'TempSecondaryBlock' })], + }); + + mySubnet = new subnet.SubnetV2(stack, 'TestSubnet', { + vpc: myVpc, + availabilityZone: 'us-east-1a', + ipv4CidrBlock: new subnet.IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }); + + transitGateway = new TransitGateway(stack, 'TransitGateway', { + defaultRouteTableAssociation: false, + defaultRouteTablePropagation: false, + }); + }); + + test('creates vpc attachment with required properties', () => { + // WHEN + transitGateway.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [mySubnet], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayAttachment', { + SubnetIds: [ + { + Ref: 'TestSubnet2A4BE4CA', + }, + ], + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + VpcId: { + 'Fn::GetAtt': [ + 'VpcAAD85CA4C', + 'VpcId', + ], + }, + }); + }); + + test('creates vpc attachment with optional properties', () => { + // WHEN + transitGateway.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [mySubnet], + vpcAttachmentOptions: { + dnsSupport: true, + ipv6Support: true, + applianceModeSupport: true, + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayAttachment', { + VpcId: stack.resolve(myVpc.vpcId), + TransitGatewayId: stack.resolve(transitGateway.transitGatewayId), + SubnetIds: [stack.resolve(mySubnet.subnetId)], + Options: { + DnsSupport: 'enable', + Ipv6Support: 'enable', + ApplianceModeSupport: 'enable', + }, + }); + }); + + test('can add subnets', () => { + // GIVEN + const attachment = transitGateway.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [mySubnet], + }); + + const additionalSubnet = new subnet.SubnetV2(stack, 'AdditionalSubnet', { + vpc: myVpc, + availabilityZone: 'us-east-1b', + ipv4CidrBlock: new subnet.IpCidr('10.0.1.0/24'), + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }); + + // WHEN + attachment.addSubnets([additionalSubnet]); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayAttachment', { + SubnetIds: [ + { Ref: 'TestSubnet2A4BE4CA' }, + { Ref: 'AdditionalSubnetD5F4E6FA' }, + ], + }); + }); + + test('can remove subnets', () => { + // GIVEN + const additionalSubnet = new subnet.SubnetV2(stack, 'AdditionalSubnet', { + vpc: myVpc, + availabilityZone: 'us-east-1b', + ipv4CidrBlock: new subnet.IpCidr('10.0.1.0/24'), + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }); + + const attachment = transitGateway.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [mySubnet, additionalSubnet], + }); + + // WHEN + attachment.removeSubnets([additionalSubnet]); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayAttachment', { + SubnetIds: [{ Ref: 'TestSubnet2A4BE4CA' }], + }); + }); + + test('throws error when adding duplicate subnet', () => { + // GIVEN + const attachment = transitGateway.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [mySubnet], + }); + + // THEN + expect(() => attachment.addSubnets([mySubnet])).toThrow( + `Subnet with ID ${mySubnet.subnetId} is already added to the Attachment`, + ); + }); + + test('throws error when removing non-existent subnet', () => { + // GIVEN + const attachment = transitGateway.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [mySubnet], + }); + + const nonExistentSubnet = new subnet.SubnetV2(stack, 'NonExistentSubnet', { + vpc: myVpc, + availabilityZone: 'us-east-1c', + ipv4CidrBlock: new subnet.IpCidr('10.0.2.0/24'), + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }); + + // THEN + expect(() => attachment.removeSubnets([nonExistentSubnet])).toThrow( + `Subnet with ID ${nonExistentSubnet.subnetId} does not exist in the Attachment`, + ); + }); + + test('throws warning when options are enabled on attachment but not on the transit gateway', () => { + // GIVEN + const transitGateway2 = new TransitGateway(stack, 'TransitGateway2', { + dnsSupport: false, + securityGroupReferencingSupport: false, + }); + + // WHEN + transitGateway2.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [mySubnet], + vpcAttachmentOptions: { + dnsSupport: true, + securityGroupReferencingSupport: true, + }, + }); + + // THEN + const annotations = Annotations.fromStack(stack).findWarning('*', Match.anyValue()); + expect(annotations.length).toBe(2); + + Annotations.fromStack(stack).hasWarning('/Default/TransitGateway2/VpcAttachment', '\'DnsSupport\' is enabled for the VPC Attachment but disabled on the TransitGateway. The feature will not work unless \'DnsSupport\' is enabled on both. [ack: @aws-cdk/aws-ec2:transitGatewayDnsSupportMismatch]'); + + Annotations.fromStack(stack).hasWarning('/Default/TransitGateway2/VpcAttachment', '\'SecurityGroupReferencingSupport\' is enabled for the VPC Attachment but disabled on the TransitGateway. The feature will not work unless \'SecurityGroupReferencingSupport\' is enabled on both. [ack: @aws-cdk/aws-ec2:transitGatewaySecurityGroupReferencingSupportMismatch]'); + }); +}); diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway.test.ts new file mode 100644 index 0000000000000..68d4afc28d5bc --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/transit-gateway.test.ts @@ -0,0 +1,331 @@ +import * as cdk from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { TransitGateway } from '../lib/transit-gateway'; +import * as vpc from '../lib/vpc-v2'; +import * as subnet from '../lib/subnet-v2'; +import { SubnetType } from 'aws-cdk-lib/aws-ec2'; + +describe('Transit Gateway with default settings', () => { + let stack: cdk.Stack; + let tgw: TransitGateway; + let myVpc: vpc.VpcV2; + let mySubnet: subnet.SubnetV2; + + beforeEach(() => { + const app = new cdk.App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + + stack = new cdk.Stack(app, 'TransitGatewayStack', { + env: { + region: 'us-east-1', + }, + }); + + myVpc = new vpc.VpcV2(stack, 'VpcA', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv4('10.1.0.0/16', { cidrBlockName: 'TempSecondaryBlock' })], + }); + + mySubnet = new subnet.SubnetV2(stack, 'TestSubnet', { + vpc: myVpc, + availabilityZone: 'us-east-1a', + ipv4CidrBlock: new subnet.IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }); + + tgw = new TransitGateway(stack, 'TransitGateway'); + }); + + test('Creates a transit gateway with all default settings and default route table', () => { + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::EC2::TransitGateway', { + DefaultRouteTableAssociation: 'disable', + DefaultRouteTablePropagation: 'disable', + }); + + Template.fromStack(stack).templateMatches({ + Resources: { + TransitGateway11B93D57: { + Type: 'AWS::EC2::TransitGateway', + Properties: { + DefaultRouteTableAssociation: 'disable', + DefaultRouteTablePropagation: 'disable', + }, + }, + TransitGatewayDefaultRouteTable608EC117: { + Type: 'AWS::EC2::TransitGatewayRouteTable', + Properties: { + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + }, + }, + }, + }); + }); + + test('add route table method should create a second transit gateway route table tha references the transit gateway', () => { + tgw.addRouteTable('RouteTable2'); + + Template.fromStack(stack).templateMatches({ + Resources: { + TransitGateway11B93D57: { + Type: 'AWS::EC2::TransitGateway', + Properties: { + DefaultRouteTableAssociation: 'disable', + DefaultRouteTablePropagation: 'disable', + }, + }, + TransitGatewayDefaultRouteTable608EC117: { + Type: 'AWS::EC2::TransitGatewayRouteTable', + Properties: { + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + }, + }, + TransitGatewayRouteTable2047E2A04: { + Type: 'AWS::EC2::TransitGatewayRouteTable', + Properties: { + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + }, + }, + }, + }); + }); + + test('attach vpc method should create an attachment, association and propagation when default association/propagation are enabled', () => { + tgw.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [mySubnet], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayAttachment', { + SubnetIds: [ + { + Ref: 'TestSubnet2A4BE4CA', + }, + ], + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + VpcId: { + 'Fn::GetAtt': [ + 'VpcAAD85CA4C', + 'VpcId', + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRouteTableAssociation', { + TransitGatewayAttachmentId: { + 'Fn::GetAtt': [ + 'TransitGatewayVpcAttachment3EC29F61', + 'Id', + ], + }, + TransitGatewayRouteTableId: { + 'Fn::GetAtt': [ + 'TransitGatewayDefaultRouteTable608EC117', + 'TransitGatewayRouteTableId', + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayRouteTablePropagation', { + TransitGatewayAttachmentId: { + 'Fn::GetAtt': [ + 'TransitGatewayVpcAttachment3EC29F61', + 'Id', + ], + }, + TransitGatewayRouteTableId: { + 'Fn::GetAtt': [ + 'TransitGatewayDefaultRouteTable608EC117', + 'TransitGatewayRouteTableId', + ], + }, + }); + }); +}); + +describe('Transit Gateway with default route table association and propagation disabled', () => { + let stack: cdk.Stack; + let tgw: TransitGateway; + let myVpc: vpc.VpcV2; + let mySubnet: subnet.SubnetV2; + + beforeEach(() => { + const app = new cdk.App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + + stack = new cdk.Stack(app, 'TransitGatewayStack', { + env: { + region: 'us-east-1', + }, + }); + + myVpc = new vpc.VpcV2(stack, 'VpcA', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv4('10.1.0.0/16', { cidrBlockName: 'TempSecondaryBlock' })], + }); + + mySubnet = new subnet.SubnetV2(stack, 'TestSubnet', { + vpc: myVpc, + availabilityZone: 'us-east-1a', + ipv4CidrBlock: new subnet.IpCidr('10.0.0.0/24'), + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }); + + tgw = new TransitGateway(stack, 'TransitGateway', { + defaultRouteTableAssociation: false, + defaultRouteTablePropagation: false, + }); + }); + + test('should create a transit gateway with all default settings and default route table', () => { + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::EC2::TransitGateway', { + DefaultRouteTableAssociation: 'disable', + DefaultRouteTablePropagation: 'disable', + }); + + Template.fromStack(stack).templateMatches({ + Resources: { + TransitGateway11B93D57: { + Type: 'AWS::EC2::TransitGateway', + Properties: { + DefaultRouteTableAssociation: 'disable', + DefaultRouteTablePropagation: 'disable', + }, + }, + TransitGatewayDefaultRouteTable608EC117: { + Type: 'AWS::EC2::TransitGatewayRouteTable', + Properties: { + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + }, + }, + }, + }); + }); + + test('add route table method should create a second transit gateway route table tha references the transit gateway', () => { + tgw.addRouteTable('RouteTable2'); + + Template.fromStack(stack).templateMatches({ + Resources: { + TransitGateway11B93D57: { + Type: 'AWS::EC2::TransitGateway', + Properties: { + DefaultRouteTableAssociation: 'disable', + DefaultRouteTablePropagation: 'disable', + }, + }, + TransitGatewayDefaultRouteTable608EC117: { + Type: 'AWS::EC2::TransitGatewayRouteTable', + Properties: { + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + }, + }, + TransitGatewayRouteTable2047E2A04: { + Type: 'AWS::EC2::TransitGatewayRouteTable', + Properties: { + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + }, + }, + }, + }); + }); + + test('attach vpc method should create an attachment and not create an association or propagation when default association/propagation are disabled', () => { + tgw.attachVpc('VpcAttachment', { + vpc: myVpc, + subnets: [mySubnet], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::TransitGatewayAttachment', { + SubnetIds: [ + { + Ref: 'TestSubnet2A4BE4CA', + }, + ], + TransitGatewayId: { + 'Fn::GetAtt': [ + 'TransitGateway11B93D57', + 'Id', + ], + }, + VpcId: { + 'Fn::GetAtt': [ + 'VpcAAD85CA4C', + 'VpcId', + ], + }, + }); + + Template.fromStack(stack).resourcePropertiesCountIs('AWS::EC2::TransitGatewayRouteTableAssociation', { + TransitGatewayAttachmentId: { + 'Fn::GetAtt': [ + 'TransitGatewayVpcAttachmentTransitGatewayAttachment963F391D', + 'Id', + ], + }, + TransitGatewayRouteTableId: { + 'Fn::GetAtt': [ + 'TransitGatewayDefaultRouteTable608EC117', + 'TransitGatewayRouteTableId', + ], + }, + }, 0); + + Template.fromStack(stack).resourcePropertiesCountIs('AWS::EC2::TransitGatewayRouteTablePropagation', { + TransitGatewayAttachmentId: { + 'Fn::GetAtt': [ + 'TransitGatewayVpcAttachmentTransitGatewayAttachment963F391D', + 'Id', + ], + }, + TransitGatewayRouteTableId: { + 'Fn::GetAtt': [ + 'TransitGatewayDefaultRouteTable608EC117', + 'TransitGatewayRouteTableId', + ], + }, + }, 0); + }); +});