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); + }); +});