diff --git a/src/aws-route53/README.md b/src/aws-route53/README.md new file mode 100644 index 0000000..a2b7734 --- /dev/null +++ b/src/aws-route53/README.md @@ -0,0 +1,12 @@ +# Split Horizon DNS CDK Construct + +## Overview + + +## Usage + + +### Basic Example + + +### Advanced Example \ No newline at end of file diff --git a/src/aws-route53/index.ts b/src/aws-route53/index.ts new file mode 100644 index 0000000..12232e6 --- /dev/null +++ b/src/aws-route53/index.ts @@ -0,0 +1 @@ +export * from './split-horizon-dns'; \ No newline at end of file diff --git a/src/aws-route53/split-horizon-dns.ts b/src/aws-route53/split-horizon-dns.ts new file mode 100644 index 0000000..39f4a50 --- /dev/null +++ b/src/aws-route53/split-horizon-dns.ts @@ -0,0 +1,162 @@ +import * as cdk from 'aws-cdk-lib'; +import * as acm from 'aws-cdk-lib/aws-certificatemanager'; +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as route53 from 'aws-cdk-lib/aws-route53'; +import { Construct } from 'constructs'; + +const isAliasRecordTarget = (value: route53.IAliasRecordTarget | Array): value is route53.IAliasRecordTarget => { + return !('length' in value); +}; + +type ARecordArray = Array; + +export interface AliasTarget { + readonly target: route53.IAliasRecordTarget | Array; + readonly private?: boolean; + readonly public?: boolean; + readonly ttl?: cdk.Duration; +} + +/** + * @param zoneName The DNS name of the zone to create + * @param existingPublicZone An existing public zone to use instead of creating a new one + * @param existingPrivateZone An existing private zone to use instead of creating a new one + * @param disallowPrivateZone Override the default behavior of creating a private zone. Will also block adding private records. + * @param includeCertificate Whether to create an ACM certificate for the zone + * @param certAlternateNames Alternate names to include in the certificate + * @param privateZoneVpcs VPCs to associate with the private zone + * @param targets Targets to create A records for + */ +export interface ISplitHorizonDnsProps { + readonly zoneName: string; + readonly recordName?: string; + readonly existingPublicZone?: route53.IHostedZone; + readonly existingPrivateZone?: route53.IHostedZone; + readonly disallowPrivateZone?: boolean; + readonly certAlternateNames?: Array; + readonly privateZoneVpcs?: Array; + readonly targets: Array; + readonly includeCertificate?: boolean; +} + +export interface ISplitHorizonDns { + publicZone: route53.IHostedZone; + privateZone?: route53.IHostedZone; + records: Array; +} + +interface createHostedZoneProps { + zoneName: string; + vpcs?: Array; +} + +interface createCertificateProps { + + domainName: string; + subjectAlternativeNames?: Array; +} +/** + * Creates a public and private zone for a given domain name, and creates A records for the given targets. + * @property publicZone The public zone created + * @property privateZone The private zone created + * @property records The A records created + */ +export class SplitHorizonDns extends Construct implements ISplitHorizonDns { + public publicZone: route53.IHostedZone; + + public privateZone?: route53.IHostedZone; + + public records: Array; + + public certificate?: acm.ICertificate; + + constructor(scope: Construct, id: string, private props: ISplitHorizonDnsProps) { + super(scope, id); + + const { + zoneName, + existingPublicZone, + existingPrivateZone, + disallowPrivateZone, + includeCertificate, + certAlternateNames, + privateZoneVpcs, + targets, + } = this.props; + + if (existingPublicZone) { + this.publicZone = existingPublicZone; + } else { + this.publicZone = this.createHostedZone('PublicZone', { + zoneName: zoneName, + }); + } + + if (includeCertificate) { + this.certificate = this.createCertificate('Certificate', { + domainName: zoneName, + subjectAlternativeNames: certAlternateNames, + }); + } + + if (disallowPrivateZone) { + console.log('Private zone creation is disallowed. Skipping...'); + } else if (existingPrivateZone) { + this.privateZone = existingPrivateZone; + } else { + this.privateZone = this.createHostedZone('PrivateZone', { + zoneName: zoneName, + vpcs: privateZoneVpcs, + }); + } + + this.records = targets.reduce((accu: Array, curr: AliasTarget) => { + let target; + + if (isAliasRecordTarget(curr.target)) { + target = route53.RecordTarget.fromAlias(curr.target); + } else { + target = route53.RecordTarget.fromValues(...curr.target); + } + + if (!curr.private && !curr.public) { + console.error(`Neither public nor private was specified for ${JSON.stringify(curr)}. Omitting...`); + return accu; + } + + const records = [] as ARecordArray; + if (curr.public) { + const publicARecord = new route53.ARecord(this, `${curr.target.toString()}PublicARecord`, { + zone: this.publicZone, + target: target, + ttl: curr.ttl, + recordName: props.recordName, + }); + records.push(publicARecord); + } + + if (disallowPrivateZone) { + console.log('Private zone creation is disallowed. Skipping...'); + } else if (curr.private && this.privateZone) { + const privateARecord = new route53.ARecord(this, `${curr.target.toString()}PrivateARecord`, { + zone: this.privateZone, + target: target, + recordName: props.recordName, + }); + records.push(privateARecord); + } else if (curr.private && !this.privateZone) { + console.error(`Private zone was specified for ${curr}, but private zone was not created. Omitting...`); + } + accu.push(records); + return accu; + }, [] as Array); + } + + protected createHostedZone(zoneId: string, props: createHostedZoneProps): route53.IHostedZone { + return new route53.HostedZone(this, zoneId, props); + } + + protected createCertificate(certId: string, props: createCertificateProps): acm.ICertificate { + return new acm.Certificate(this, certId, props); + } +} diff --git a/src/index.ts b/src/index.ts index 8b2b719..a0401d4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ // Export constructs here export * as aws_cur from './aws-cur'; -export * as aws_ec2 from './aws-ec2'; \ No newline at end of file +export * as aws_ec2 from './aws-ec2'; +export * as aws_route53 from './aws-route53'; \ No newline at end of file diff --git a/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.assets.json b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.assets.json new file mode 100644 index 0000000..3ec3047 --- /dev/null +++ b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.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/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.template.json b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.template.json new file mode 100644 index 0000000..ad9d0fb --- /dev/null +++ b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.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/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsStack.assets.json b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsStack.assets.json new file mode 100644 index 0000000..0c852b8 --- /dev/null +++ b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsStack.assets.json @@ -0,0 +1,34 @@ +{ + "version": "36.0.0", + "files": { + "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e": { + "source": { + "path": "asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e", + "packaging": "zip" + }, + "destinations": { + "697863601562-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-697863601562-us-east-1", + "objectKey": "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e.zip", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::697863601562:role/cdk-hnb659fds-file-publishing-role-697863601562-us-east-1" + } + } + }, + "251d0dc1993c060b2439b54cb243f4ead92e308a2b7c7a5f0c5e0e3a565d6d51": { + "source": { + "path": "SplitHorizonDnsStack.template.json", + "packaging": "file" + }, + "destinations": { + "697863601562-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-697863601562-us-east-1", + "objectKey": "251d0dc1993c060b2439b54cb243f4ead92e308a2b7c7a5f0c5e0e3a565d6d51.json", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::697863601562:role/cdk-hnb659fds-file-publishing-role-697863601562-us-east-1" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsStack.template.json b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsStack.template.json new file mode 100644 index 0000000..4d4d67f --- /dev/null +++ b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/SplitHorizonDnsStack.template.json @@ -0,0 +1,712 @@ +{ + "Resources": { + "myvpc632CA88B": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc" + } + ] + } + }, + "myvpcPublicSubnet1SubnetE2DAA156": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "dummy1a", + "CidrBlock": "10.0.0.0/19", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPublicSubnet1RouteTable8A33B640": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPublicSubnet1RouteTableAssociationE9EE9198": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "myvpcPublicSubnet1RouteTable8A33B640" + }, + "SubnetId": { + "Ref": "myvpcPublicSubnet1SubnetE2DAA156" + } + } + }, + "myvpcPublicSubnet1DefaultRoute541095B8": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "myvpcIGW2E52BF2F" + }, + "RouteTableId": { + "Ref": "myvpcPublicSubnet1RouteTable8A33B640" + } + }, + "DependsOn": [ + "myvpcVPCGW8FDE359B" + ] + }, + "myvpcPublicSubnet1EIPFACB5379": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet1" + } + ] + } + }, + "myvpcPublicSubnet1NATGatewayD79F3015": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "myvpcPublicSubnet1EIPFACB5379", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "myvpcPublicSubnet1SubnetE2DAA156" + }, + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet1" + } + ] + }, + "DependsOn": [ + "myvpcPublicSubnet1DefaultRoute541095B8", + "myvpcPublicSubnet1RouteTableAssociationE9EE9198" + ] + }, + "myvpcPublicSubnet2SubnetAC1228E3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "dummy1b", + "CidrBlock": "10.0.32.0/19", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPublicSubnet2RouteTable0532E281": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPublicSubnet2RouteTableAssociation1BF1FAEB": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "myvpcPublicSubnet2RouteTable0532E281" + }, + "SubnetId": { + "Ref": "myvpcPublicSubnet2SubnetAC1228E3" + } + } + }, + "myvpcPublicSubnet2DefaultRoute438AD888": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "myvpcIGW2E52BF2F" + }, + "RouteTableId": { + "Ref": "myvpcPublicSubnet2RouteTable0532E281" + } + }, + "DependsOn": [ + "myvpcVPCGW8FDE359B" + ] + }, + "myvpcPublicSubnet2EIP766CF0F6": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet2" + } + ] + } + }, + "myvpcPublicSubnet2NATGatewayD80F7867": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "myvpcPublicSubnet2EIP766CF0F6", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "myvpcPublicSubnet2SubnetAC1228E3" + }, + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet2" + } + ] + }, + "DependsOn": [ + "myvpcPublicSubnet2DefaultRoute438AD888", + "myvpcPublicSubnet2RouteTableAssociation1BF1FAEB" + ] + }, + "myvpcPublicSubnet3Subnet6352D232": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "dummy1c", + "CidrBlock": "10.0.64.0/19", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet3" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPublicSubnet3RouteTable7A3A1442": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet3" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPublicSubnet3RouteTableAssociation212EEC13": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "myvpcPublicSubnet3RouteTable7A3A1442" + }, + "SubnetId": { + "Ref": "myvpcPublicSubnet3Subnet6352D232" + } + } + }, + "myvpcPublicSubnet3DefaultRouteEF1138EE": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "myvpcIGW2E52BF2F" + }, + "RouteTableId": { + "Ref": "myvpcPublicSubnet3RouteTable7A3A1442" + } + }, + "DependsOn": [ + "myvpcVPCGW8FDE359B" + ] + }, + "myvpcPublicSubnet3EIP250C394C": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet3" + } + ] + } + }, + "myvpcPublicSubnet3NATGateway07EDA442": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "myvpcPublicSubnet3EIP250C394C", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "myvpcPublicSubnet3Subnet6352D232" + }, + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PublicSubnet3" + } + ] + }, + "DependsOn": [ + "myvpcPublicSubnet3DefaultRouteEF1138EE", + "myvpcPublicSubnet3RouteTableAssociation212EEC13" + ] + }, + "myvpcPrivateSubnet1SubnetC2DB5FF8": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "dummy1a", + "CidrBlock": "10.0.96.0/19", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPrivateSubnet1RouteTable02B66492": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPrivateSubnet1RouteTableAssociation49BD9818": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "myvpcPrivateSubnet1RouteTable02B66492" + }, + "SubnetId": { + "Ref": "myvpcPrivateSubnet1SubnetC2DB5FF8" + } + } + }, + "myvpcPrivateSubnet1DefaultRouteCCFAFFFB": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "myvpcPublicSubnet1NATGatewayD79F3015" + }, + "RouteTableId": { + "Ref": "myvpcPrivateSubnet1RouteTable02B66492" + } + } + }, + "myvpcPrivateSubnet2SubnetEC1D7719": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "dummy1b", + "CidrBlock": "10.0.128.0/19", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPrivateSubnet2RouteTable95011678": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPrivateSubnet2RouteTableAssociationCAD1B140": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "myvpcPrivateSubnet2RouteTable95011678" + }, + "SubnetId": { + "Ref": "myvpcPrivateSubnet2SubnetEC1D7719" + } + } + }, + "myvpcPrivateSubnet2DefaultRoute4C6E9A31": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "myvpcPublicSubnet2NATGatewayD80F7867" + }, + "RouteTableId": { + "Ref": "myvpcPrivateSubnet2RouteTable95011678" + } + } + }, + "myvpcPrivateSubnet3Subnet5D10AF27": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "dummy1c", + "CidrBlock": "10.0.160.0/19", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PrivateSubnet3" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPrivateSubnet3RouteTableD62F598B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc/PrivateSubnet3" + } + ], + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcPrivateSubnet3RouteTableAssociationEA6A6A0D": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "myvpcPrivateSubnet3RouteTableD62F598B" + }, + "SubnetId": { + "Ref": "myvpcPrivateSubnet3Subnet5D10AF27" + } + } + }, + "myvpcPrivateSubnet3DefaultRouteEE4F2A64": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "myvpcPublicSubnet3NATGateway07EDA442" + }, + "RouteTableId": { + "Ref": "myvpcPrivateSubnet3RouteTableD62F598B" + } + } + }, + "myvpcIGW2E52BF2F": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "SplitHorizonDnsStack/myvpc" + } + ] + } + }, + "myvpcVPCGW8FDE359B": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "InternetGatewayId": { + "Ref": "myvpcIGW2E52BF2F" + }, + "VpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "myvpcRestrictDefaultSecurityGroupCustomResourceEBBA2AC0": { + "Type": "Custom::VpcRestrictDefaultSG", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E", + "Arn" + ] + }, + "DefaultSecurityGroupId": { + "Fn::GetAtt": [ + "myvpc632CA88B", + "DefaultSecurityGroup" + ] + }, + "Account": "697863601562" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:AuthorizeSecurityGroupEgress", + "ec2:RevokeSecurityGroupIngress", + "ec2:RevokeSecurityGroupEgress" + ], + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:aws:ec2:us-east-1:697863601562:security-group/", + { + "Fn::GetAtt": [ + "myvpc632CA88B", + "DefaultSecurityGroup" + ] + } + ] + ] + } + ] + } + ] + } + } + ] + } + }, + "CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-697863601562-us-east-1", + "S3Key": "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Description": "Lambda function for removing all inbound/outbound rules from the VPC default security group" + }, + "DependsOn": [ + "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0" + ] + }, + "SplitHorizonDnsPublicZone855A5549": { + "Type": "AWS::Route53::HostedZone", + "Properties": { + "Name": "example.com." + } + }, + "SplitHorizonDnsPrivateZoneB6E7E17E": { + "Type": "AWS::Route53::HostedZone", + "Properties": { + "Name": "example.com.", + "VPCs": [ + { + "VPCId": { + "Ref": "myvpc632CA88B" + }, + "VPCRegion": "us-east-1" + } + ] + } + }, + "SplitHorizonDnswwwexamplecomPublicARecord072A1B07": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "SplitHorizonDnsPublicZone855A5549" + }, + "Name": "example.com.", + "ResourceRecords": [ + "www.example.com" + ], + "TTL": "1800", + "Type": "A" + } + }, + "SplitHorizonDnswwwexamplecomPrivateARecord0F95A172": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "SplitHorizonDnsPrivateZoneB6E7E17E" + }, + "Name": "example.com.", + "ResourceRecords": [ + "www.example.com" + ], + "TTL": "1800", + "Type": "A" + } + } + }, + "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/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/__entrypoint__.js b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/__entrypoint__.js new file mode 100644 index 0000000..5a1714e --- /dev/null +++ b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/__entrypoint__.js @@ -0,0 +1 @@ +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.withRetries=exports.handler=exports.external=void 0;const https=require("https"),url=require("url");exports.external={sendHttpRequest:defaultSendHttpRequest,log:defaultLog,includeStackTraces:!0,userHandlerIndex:"./index"};const CREATE_FAILED_PHYSICAL_ID_MARKER="AWSCDK::CustomResourceProviderFramework::CREATE_FAILED",MISSING_PHYSICAL_ID_MARKER="AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID";async function handler(event,context){const sanitizedEvent={...event,ResponseURL:"..."};if(exports.external.log(JSON.stringify(sanitizedEvent,void 0,2)),event.RequestType==="Delete"&&event.PhysicalResourceId===CREATE_FAILED_PHYSICAL_ID_MARKER){exports.external.log("ignoring DELETE event caused by a failed CREATE event"),await submitResponse("SUCCESS",event);return}try{const userHandler=require(exports.external.userHandlerIndex).handler,result=await userHandler(sanitizedEvent,context),responseEvent=renderResponse(event,result);await submitResponse("SUCCESS",responseEvent)}catch(e){const resp={...event,Reason:exports.external.includeStackTraces?e.stack:e.message};resp.PhysicalResourceId||(event.RequestType==="Create"?(exports.external.log("CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored"),resp.PhysicalResourceId=CREATE_FAILED_PHYSICAL_ID_MARKER):exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`)),await submitResponse("FAILED",resp)}}exports.handler=handler;function renderResponse(cfnRequest,handlerResponse={}){const physicalResourceId=handlerResponse.PhysicalResourceId??cfnRequest.PhysicalResourceId??cfnRequest.RequestId;if(cfnRequest.RequestType==="Delete"&&physicalResourceId!==cfnRequest.PhysicalResourceId)throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`);return{...cfnRequest,...handlerResponse,PhysicalResourceId:physicalResourceId}}async function submitResponse(status,event){const json={Status:status,Reason:event.Reason??status,StackId:event.StackId,RequestId:event.RequestId,PhysicalResourceId:event.PhysicalResourceId||MISSING_PHYSICAL_ID_MARKER,LogicalResourceId:event.LogicalResourceId,NoEcho:event.NoEcho,Data:event.Data};exports.external.log("submit response to cloudformation",json);const responseBody=JSON.stringify(json),parsedUrl=url.parse(event.ResponseURL),req={hostname:parsedUrl.hostname,path:parsedUrl.path,method:"PUT",headers:{"content-type":"","content-length":Buffer.byteLength(responseBody,"utf8")}};await withRetries({attempts:5,sleep:1e3},exports.external.sendHttpRequest)(req,responseBody)}async function defaultSendHttpRequest(options,responseBody){return new Promise((resolve,reject)=>{try{const request=https.request(options,_=>resolve());request.on("error",reject),request.write(responseBody),request.end()}catch(e){reject(e)}})}function defaultLog(fmt,...params){console.log(fmt,...params)}function withRetries(options,fn){return async(...xs)=>{let attempts=options.attempts,ms=options.sleep;for(;;)try{return await fn(...xs)}catch(e){if(attempts--<=0)throw e;await sleep(Math.floor(Math.random()*ms)),ms*=2}}}exports.withRetries=withRetries;async function sleep(ms){return new Promise(ok=>setTimeout(ok,ms))} diff --git a/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/index.js b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/index.js new file mode 100644 index 0000000..9f1466d --- /dev/null +++ b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/index.js @@ -0,0 +1 @@ +"use strict";var I=Object.create,t=Object.defineProperty,y=Object.getOwnPropertyDescriptor,P=Object.getOwnPropertyNames,g=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,G=(r,e)=>{for(var o in e)t(r,o,{get:e[o],enumerable:!0})},n=(r,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of P(e))!l.call(r,s)&&s!==o&&t(r,s,{get:()=>e[s],enumerable:!(i=y(e,s))||i.enumerable});return r},R=(r,e,o)=>(o=r!=null?I(g(r)):{},n(e||!r||!r.__esModule?t(o,"default",{value:r,enumerable:!0}):o,r)),S=r=>n(t({},"__esModule",{value:!0}),r),k={};G(k,{handler:()=>f}),module.exports=S(k);var a=R(require("@aws-sdk/client-ec2")),u=new a.EC2({});function c(r,e){return{GroupId:r,IpPermissions:[{UserIdGroupPairs:[{GroupId:r,UserId:e}],IpProtocol:"-1"}]}}function d(r){return{GroupId:r,IpPermissions:[{IpRanges:[{CidrIp:"0.0.0.0/0"}],IpProtocol:"-1"}]}}async function f(r){let e=r.ResourceProperties.DefaultSecurityGroupId,o=r.ResourceProperties.Account;switch(r.RequestType){case"Create":return p(e,o);case"Update":return h(r);case"Delete":return m(e,o)}}async function h(r){let e=r.OldResourceProperties.DefaultSecurityGroupId,o=r.ResourceProperties.DefaultSecurityGroupId;e!==o&&(await m(e,r.ResourceProperties.Account),await p(o,r.ResourceProperties.Account))}async function p(r,e){try{await u.revokeSecurityGroupEgress(d(r))}catch(o){if(o.name!=="InvalidPermission.NotFound")throw o}try{await u.revokeSecurityGroupIngress(c(r,e))}catch(o){if(o.name!=="InvalidPermission.NotFound")throw o}}async function m(r,e){await u.authorizeSecurityGroupIngress(c(r,e)),await u.authorizeSecurityGroupEgress(d(r))} diff --git a/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/cdk.out b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/cdk.out new file mode 100644 index 0000000..1f0068d --- /dev/null +++ b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/integ.json b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/integ.json new file mode 100644 index 0000000..5a72dc6 --- /dev/null +++ b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "SplitHorizonDns/DefaultTest": { + "stacks": [ + "SplitHorizonDnsStack" + ], + "assertionStack": "SplitHorizonDns/DefaultTest/DeployAssert", + "assertionStackName": "SplitHorizonDnsDefaultTestDeployAssertB4E57D9C" + } + } +} \ No newline at end of file diff --git a/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/manifest.json b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/manifest.json new file mode 100644 index 0000000..9e76ac9 --- /dev/null +++ b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/manifest.json @@ -0,0 +1,381 @@ +{ + "version": "36.0.0", + "artifacts": { + "SplitHorizonDnsStack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "SplitHorizonDnsStack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "SplitHorizonDnsStack": { + "type": "aws:cloudformation:stack", + "environment": "aws://697863601562/us-east-1", + "properties": { + "templateFile": "SplitHorizonDnsStack.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::697863601562:role/cdk-hnb659fds-deploy-role-697863601562-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::697863601562:role/cdk-hnb659fds-cfn-exec-role-697863601562-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-697863601562-us-east-1/251d0dc1993c060b2439b54cb243f4ead92e308a2b7c7a5f0c5e0e3a565d6d51.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "SplitHorizonDnsStack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::697863601562:role/cdk-hnb659fds-lookup-role-697863601562-us-east-1", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "SplitHorizonDnsStack.assets" + ], + "metadata": { + "/SplitHorizonDnsStack": [ + { + "type": "aws:cdk:error", + "data": "Need to perform AWS calls for account 697863601562, but no credentials have been configured", + "trace": [ + "Annotations.addMessage (/Users/dakotalewallen/Projects/github/aws-cdk-library/node_modules/aws-cdk-lib/core/lib/annotations.js:1:1608)", + "Annotations.addError (/Users/dakotalewallen/Projects/github/aws-cdk-library/node_modules/aws-cdk-lib/core/lib/annotations.js:1:1100)", + "Function.getValue (/Users/dakotalewallen/Projects/github/aws-cdk-library/node_modules/aws-cdk-lib/core/lib/context-provider.js:2:1172)", + "SplitHorizonDnsStack.get availabilityZones [as availabilityZones] (/Users/dakotalewallen/Projects/github/aws-cdk-library/node_modules/aws-cdk-lib/core/lib/stack.js:1:10375)", + "new Vpc (/Users/dakotalewallen/Projects/github/aws-cdk-library/node_modules/aws-cdk-lib/aws-ec2/lib/vpc.js:1:12149)", + "new SplitHorizonDnsStack (/Users/dakotalewallen/Projects/github/aws-cdk-library/test/aws-route53/integ.split-horizon-dns.ts:15:17)", + "Object. (/Users/dakotalewallen/Projects/github/aws-cdk-library/test/aws-route53/integ.split-horizon-dns.ts:37:18)", + "Module._compile (node:internal/modules/cjs/loader:1358:14)", + "Module.m._compile (/Users/dakotalewallen/Projects/github/aws-cdk-library/node_modules/ts-node/src/index.ts:1618:23)", + "Module._extensions..js (node:internal/modules/cjs/loader:1416:10)", + "Object.require.extensions. [as .ts] (/Users/dakotalewallen/Projects/github/aws-cdk-library/node_modules/ts-node/src/index.ts:1621:12)", + "Module.load (node:internal/modules/cjs/loader:1208:32)", + "Function.Module._load (node:internal/modules/cjs/loader:1024:12)", + "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:174:12)", + "node:internal/main/run_main_module:28:49" + ] + } + ], + "/SplitHorizonDnsStack/myvpc/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpc632CA88B" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet1SubnetE2DAA156" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet1RouteTable8A33B640" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet1RouteTableAssociationE9EE9198" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet1DefaultRoute541095B8" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet1EIPFACB5379" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet1NATGatewayD79F3015" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet2SubnetAC1228E3" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet2RouteTable0532E281" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet2RouteTableAssociation1BF1FAEB" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet2DefaultRoute438AD888" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet2/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet2EIP766CF0F6" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet2/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet2NATGatewayD80F7867" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet3/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet3Subnet6352D232" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet3/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet3RouteTable7A3A1442" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet3/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet3RouteTableAssociation212EEC13" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet3/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet3DefaultRouteEF1138EE" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet3/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet3EIP250C394C" + } + ], + "/SplitHorizonDnsStack/myvpc/PublicSubnet3/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPublicSubnet3NATGateway07EDA442" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet1SubnetC2DB5FF8" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet1RouteTable02B66492" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet1RouteTableAssociation49BD9818" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet1DefaultRouteCCFAFFFB" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet2SubnetEC1D7719" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet2RouteTable95011678" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet2RouteTableAssociationCAD1B140" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet2DefaultRoute4C6E9A31" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet3/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet3Subnet5D10AF27" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet3/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet3RouteTableD62F598B" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet3/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet3RouteTableAssociationEA6A6A0D" + } + ], + "/SplitHorizonDnsStack/myvpc/PrivateSubnet3/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcPrivateSubnet3DefaultRouteEE4F2A64" + } + ], + "/SplitHorizonDnsStack/myvpc/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcIGW2E52BF2F" + } + ], + "/SplitHorizonDnsStack/myvpc/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcVPCGW8FDE359B" + } + ], + "/SplitHorizonDnsStack/myvpc/RestrictDefaultSecurityGroupCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "myvpcRestrictDefaultSecurityGroupCustomResourceEBBA2AC0" + } + ], + "/SplitHorizonDnsStack/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0" + } + ], + "/SplitHorizonDnsStack/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E" + } + ], + "/SplitHorizonDnsStack/SplitHorizonDns/PublicZone/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SplitHorizonDnsPublicZone855A5549" + } + ], + "/SplitHorizonDnsStack/SplitHorizonDns/PrivateZone/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SplitHorizonDnsPrivateZoneB6E7E17E" + } + ], + "/SplitHorizonDnsStack/SplitHorizonDns/www.example.comPublicARecord/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SplitHorizonDnswwwexamplecomPublicARecord072A1B07" + } + ], + "/SplitHorizonDnsStack/SplitHorizonDns/www.example.comPrivateARecord/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SplitHorizonDnswwwexamplecomPrivateARecord0F95A172" + } + ], + "/SplitHorizonDnsStack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/SplitHorizonDnsStack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "SplitHorizonDnsStack" + }, + "SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "SplitHorizonDnsDefaultTestDeployAssertB4E57D9C": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.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": [ + "SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.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": [ + "SplitHorizonDnsDefaultTestDeployAssertB4E57D9C.assets" + ], + "metadata": { + "/SplitHorizonDns/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/SplitHorizonDns/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "SplitHorizonDns/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + }, + "missing": [ + { + "key": "availability-zones:account=697863601562:region=us-east-1", + "provider": "availability-zones", + "props": { + "account": "697863601562", + "region": "us-east-1", + "lookupRoleArn": "arn:${AWS::Partition}:iam::697863601562:role/cdk-hnb659fds-lookup-role-697863601562-us-east-1" + } + } + ] +} \ No newline at end of file diff --git a/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/tree.json b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/tree.json new file mode 100644 index 0000000..d70bf6d --- /dev/null +++ b/test/aws-route53/cdk-integ.out.integ.split-horizon-dns.ts.snapshot/tree.json @@ -0,0 +1,1170 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "SplitHorizonDnsStack": { + "id": "SplitHorizonDnsStack", + "path": "SplitHorizonDnsStack", + "children": { + "myvpc": { + "id": "myvpc", + "path": "SplitHorizonDnsStack/myvpc", + "children": { + "Resource": { + "id": "Resource", + "path": "SplitHorizonDnsStack/myvpc/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "2.120.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": "dummy1a", + "cidrBlock": "10.0.0.0/19", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "2.120.0" + } + }, + "Acl": { + "id": "Acl", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.120.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "2.120.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "myvpcPublicSubnet1RouteTable8A33B640" + }, + "subnetId": { + "Ref": "myvpcPublicSubnet1SubnetE2DAA156" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "2.120.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "myvpcIGW2E52BF2F" + }, + "routeTableId": { + "Ref": "myvpcPublicSubnet1RouteTable8A33B640" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "2.120.0" + } + }, + "EIP": { + "id": "EIP", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "2.120.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "myvpcPublicSubnet1EIPFACB5379", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "myvpcPublicSubnet1SubnetE2DAA156" + }, + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "2.120.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": "dummy1b", + "cidrBlock": "10.0.32.0/19", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "2.120.0" + } + }, + "Acl": { + "id": "Acl", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.120.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "2.120.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "myvpcPublicSubnet2RouteTable0532E281" + }, + "subnetId": { + "Ref": "myvpcPublicSubnet2SubnetAC1228E3" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "2.120.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "myvpcIGW2E52BF2F" + }, + "routeTableId": { + "Ref": "myvpcPublicSubnet2RouteTable0532E281" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "2.120.0" + } + }, + "EIP": { + "id": "EIP", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet2/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "2.120.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet2/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "myvpcPublicSubnet2EIP766CF0F6", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "myvpcPublicSubnet2SubnetAC1228E3" + }, + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "2.120.0" + } + }, + "PublicSubnet3": { + "id": "PublicSubnet3", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet3", + "children": { + "Subnet": { + "id": "Subnet", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet3/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": "dummy1c", + "cidrBlock": "10.0.64.0/19", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet3" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "2.120.0" + } + }, + "Acl": { + "id": "Acl", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet3/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.120.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet3/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet3" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "2.120.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet3/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "myvpcPublicSubnet3RouteTable7A3A1442" + }, + "subnetId": { + "Ref": "myvpcPublicSubnet3Subnet6352D232" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "2.120.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet3/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "myvpcIGW2E52BF2F" + }, + "routeTableId": { + "Ref": "myvpcPublicSubnet3RouteTable7A3A1442" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "2.120.0" + } + }, + "EIP": { + "id": "EIP", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet3/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet3" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "2.120.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "SplitHorizonDnsStack/myvpc/PublicSubnet3/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "myvpcPublicSubnet3EIP250C394C", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "myvpcPublicSubnet3Subnet6352D232" + }, + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PublicSubnet3" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "2.120.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": "dummy1a", + "cidrBlock": "10.0.96.0/19", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "2.120.0" + } + }, + "Acl": { + "id": "Acl", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.120.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "2.120.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "myvpcPrivateSubnet1RouteTable02B66492" + }, + "subnetId": { + "Ref": "myvpcPrivateSubnet1SubnetC2DB5FF8" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "2.120.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "myvpcPublicSubnet1NATGatewayD79F3015" + }, + "routeTableId": { + "Ref": "myvpcPrivateSubnet1RouteTable02B66492" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "2.120.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": "dummy1b", + "cidrBlock": "10.0.128.0/19", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "2.120.0" + } + }, + "Acl": { + "id": "Acl", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.120.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "2.120.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "myvpcPrivateSubnet2RouteTable95011678" + }, + "subnetId": { + "Ref": "myvpcPrivateSubnet2SubnetEC1D7719" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "2.120.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "myvpcPublicSubnet2NATGatewayD80F7867" + }, + "routeTableId": { + "Ref": "myvpcPrivateSubnet2RouteTable95011678" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "2.120.0" + } + }, + "PrivateSubnet3": { + "id": "PrivateSubnet3", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet3", + "children": { + "Subnet": { + "id": "Subnet", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet3/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": "dummy1c", + "cidrBlock": "10.0.160.0/19", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PrivateSubnet3" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "2.120.0" + } + }, + "Acl": { + "id": "Acl", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet3/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.120.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet3/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc/PrivateSubnet3" + } + ], + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "2.120.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet3/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "myvpcPrivateSubnet3RouteTableD62F598B" + }, + "subnetId": { + "Ref": "myvpcPrivateSubnet3Subnet5D10AF27" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "2.120.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "SplitHorizonDnsStack/myvpc/PrivateSubnet3/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "myvpcPublicSubnet3NATGateway07EDA442" + }, + "routeTableId": { + "Ref": "myvpcPrivateSubnet3RouteTableD62F598B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "2.120.0" + } + }, + "IGW": { + "id": "IGW", + "path": "SplitHorizonDnsStack/myvpc/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "SplitHorizonDnsStack/myvpc" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnInternetGateway", + "version": "2.120.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "SplitHorizonDnsStack/myvpc/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "internetGatewayId": { + "Ref": "myvpcIGW2E52BF2F" + }, + "vpcId": { + "Ref": "myvpc632CA88B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCGatewayAttachment", + "version": "2.120.0" + } + }, + "RestrictDefaultSecurityGroupCustomResource": { + "id": "RestrictDefaultSecurityGroupCustomResource", + "path": "SplitHorizonDnsStack/myvpc/RestrictDefaultSecurityGroupCustomResource", + "children": { + "Default": { + "id": "Default", + "path": "SplitHorizonDnsStack/myvpc/RestrictDefaultSecurityGroupCustomResource/Default", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.CustomResource", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.Vpc", + "version": "2.120.0" + } + }, + "Custom::VpcRestrictDefaultSGCustomResourceProvider": { + "id": "Custom::VpcRestrictDefaultSGCustomResourceProvider", + "path": "SplitHorizonDnsStack/Custom::VpcRestrictDefaultSGCustomResourceProvider", + "children": { + "Staging": { + "id": "Staging", + "path": "SplitHorizonDnsStack/Custom::VpcRestrictDefaultSGCustomResourceProvider/Staging", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "2.120.0" + } + }, + "Role": { + "id": "Role", + "path": "SplitHorizonDnsStack/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "2.120.0" + } + }, + "Handler": { + "id": "Handler", + "path": "SplitHorizonDnsStack/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.CustomResourceProviderBase", + "version": "2.120.0" + } + }, + "SplitHorizonDns": { + "id": "SplitHorizonDns", + "path": "SplitHorizonDnsStack/SplitHorizonDns", + "children": { + "PublicZone": { + "id": "PublicZone", + "path": "SplitHorizonDnsStack/SplitHorizonDns/PublicZone", + "children": { + "Resource": { + "id": "Resource", + "path": "SplitHorizonDnsStack/SplitHorizonDns/PublicZone/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::HostedZone", + "aws:cdk:cloudformation:props": { + "name": "example.com." + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnHostedZone", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.HostedZone", + "version": "2.120.0" + } + }, + "PrivateZone": { + "id": "PrivateZone", + "path": "SplitHorizonDnsStack/SplitHorizonDns/PrivateZone", + "children": { + "Resource": { + "id": "Resource", + "path": "SplitHorizonDnsStack/SplitHorizonDns/PrivateZone/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::HostedZone", + "aws:cdk:cloudformation:props": { + "name": "example.com.", + "vpcs": [ + { + "vpcId": { + "Ref": "myvpc632CA88B" + }, + "vpcRegion": "us-east-1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnHostedZone", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.HostedZone", + "version": "2.120.0" + } + }, + "www.example.comPublicARecord": { + "id": "www.example.comPublicARecord", + "path": "SplitHorizonDnsStack/SplitHorizonDns/www.example.comPublicARecord", + "children": { + "Resource": { + "id": "Resource", + "path": "SplitHorizonDnsStack/SplitHorizonDns/www.example.comPublicARecord/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "SplitHorizonDnsPublicZone855A5549" + }, + "name": "example.com.", + "resourceRecords": [ + "www.example.com" + ], + "ttl": "1800", + "type": "A" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "2.120.0" + } + }, + "www.example.comPrivateARecord": { + "id": "www.example.comPrivateARecord", + "path": "SplitHorizonDnsStack/SplitHorizonDns/www.example.comPrivateARecord", + "children": { + "Resource": { + "id": "Resource", + "path": "SplitHorizonDnsStack/SplitHorizonDns/www.example.comPrivateARecord/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "SplitHorizonDnsPrivateZoneB6E7E17E" + }, + "name": "example.com.", + "resourceRecords": [ + "www.example.com" + ], + "ttl": "1800", + "type": "A" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "SplitHorizonDnsStack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "2.120.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "SplitHorizonDnsStack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "2.120.0" + } + }, + "SplitHorizonDns": { + "id": "SplitHorizonDns", + "path": "SplitHorizonDns", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "SplitHorizonDns/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "SplitHorizonDns/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "SplitHorizonDns/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "SplitHorizonDns/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "2.120.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "SplitHorizonDns/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "2.120.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "2.120.0-alpha.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "2.120.0-alpha.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "2.120.0" + } + } +} \ No newline at end of file diff --git a/test/aws-route53/cdk.context.json b/test/aws-route53/cdk.context.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/test/aws-route53/cdk.context.json @@ -0,0 +1 @@ +{} diff --git a/test/aws-route53/integ.split-horizon-dns.ts b/test/aws-route53/integ.split-horizon-dns.ts new file mode 100644 index 0000000..20d53ab --- /dev/null +++ b/test/aws-route53/integ.split-horizon-dns.ts @@ -0,0 +1,35 @@ +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as cdk from 'aws-cdk-lib'; +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import { Construct } from 'constructs'; +import * as ocf from '../../src'; + +class SplitHorizonDnsStack extends cdk.Stack { + constructor(scope: Construct) { + super(scope, 'SplitHorizonDnsStack'); + const vpc = new ec2.Vpc(this, 'myvpc'); + + const targets = [ + { + target: ['www.example.com'], + public: true, + }, + { + target: ['www.example.com'], + private: true, + }, + ]; + + new ocf.aws_route53.SplitHorizonDns(this, 'SplitHorizonDns', { + zoneName: 'example.com', + privateZoneVpcs: [vpc], + targets, + }); + } +} + +const app = new cdk.App(); +const testCase = new SplitHorizonDnsStack(app); +new IntegTest(app, 'SplitHorizonDns', { + testCases: [testCase], +}); \ No newline at end of file diff --git a/test/aws-route53/split-horizon-dns.test.ts b/test/aws-route53/split-horizon-dns.test.ts new file mode 100644 index 0000000..42674db --- /dev/null +++ b/test/aws-route53/split-horizon-dns.test.ts @@ -0,0 +1,490 @@ +import * as cdk from 'aws-cdk-lib'; +import { Duration } from 'aws-cdk-lib'; +import { Match, Template } from 'aws-cdk-lib/assertions'; +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as route53 from 'aws-cdk-lib/aws-route53'; +import * as targets from 'aws-cdk-lib/aws-route53-targets'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import { SplitHorizonDns } from '../../src/aws-route53'; + +const exampleDomain = 'example.com'; +const googleDns = '8.8.8.8'; + +describe('split horizon', () => { + + it('builds zones', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + const vpc = new ec2.Vpc(stack, 'myvpc'); + new SplitHorizonDns(stack, 'MostBasicTestConstruct', { + zoneName: 'example.com', + privateZoneVpcs: [vpc], + targets: [], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::HostedZone', { + Name: Match.anyValue(), + VPCs: Match.arrayWith([ + Match.objectLike({ + VPCId: Match.anyValue(), + }), + ]), + }); + + template.hasResourceProperties('AWS::Route53::HostedZone', { + Name: Match.anyValue(), + }); + }); + + it('creates a public A record', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + const vpc = new ec2.Vpc(stack, 'myvpc'); + + const firstTarget = { + target: [googleDns], + public: true, + }; + + new SplitHorizonDns(stack, 'PublicATestConstruct', { + zoneName: 'example.com', + privateZoneVpcs: [vpc], + targets: [firstTarget], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.stringLikeRegexp(`${exampleDomain}\.`), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('PublicZone'), + }), + TTL: '1800', // the default if no ttl is supplied + }); + }); + + it('creates a private A record', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + const vpc = new ec2.Vpc(stack, 'myvpc'); + + const firstTarget = { + target: [googleDns], + private: true, + }; + + new SplitHorizonDns(stack, 'PrivateATestConstruct', { + zoneName: 'example.com', + privateZoneVpcs: [vpc], + targets: [firstTarget], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.stringLikeRegexp(`${exampleDomain}\.`), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('PrivateZone'), + }), + }); + }); + + it('creates both a public and private A record', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + const vpc = new ec2.Vpc(stack, 'myvpc'); + + const firstTarget = { + target: [googleDns], + private: true, + public: true, + }; + + new SplitHorizonDns(stack, 'PrivateAndPublicATestConstruct', { + zoneName: 'example.com', + privateZoneVpcs: [vpc], + targets: [firstTarget], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.stringLikeRegexp(`${exampleDomain}\.`), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('PrivateZone'), + }), + }); + + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.stringLikeRegexp(`${exampleDomain}\.`), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('PublicZone'), + }), + }); + }); + + it('creates no records if neither flag is set', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + const vpc = new ec2.Vpc(stack, 'myvpc'); + + const firstTarget = { + target: ['4.4.4.4'], + }; + + const splitHorizonDns = new SplitHorizonDns(stack, 'NeitherATestConstruct', { + zoneName: 'example.com', + privateZoneVpcs: [vpc], + targets: [firstTarget], + }); + + // an empty array is always created + expect(splitHorizonDns.records.length).toBe(0); + }); + + // need to mock .fromValues and assert it's called + it.skip('uses from values if string is passed', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + const vpc = new ec2.Vpc(stack, 'myvpc'); + + const firstTarget = { + target: ['4.4.4.4'], + public: true, + }; + + const splitHorizonDns = new SplitHorizonDns(stack, 'NeitherATestConstruct', { + zoneName: 'example.com', + privateZoneVpcs: [vpc], + targets: [firstTarget], + }); + + // an empty array is always created + expect(splitHorizonDns.records.length).toBe(1); + }); + + it('can create a public A record for constructs', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack', { + env: { + account: '11111111', + region: 'us-east-1', + }, + }); + const vpc = new ec2.Vpc(stack, 'myvpc'); + + const bucketWebsite = new s3.Bucket(stack, 'BucketWebsite', { + bucketName: exampleDomain, // www.example.com + publicReadAccess: true, + websiteIndexDocument: 'index.html', + }); + + const constructTarget = { + target: new targets.BucketWebsiteTarget(bucketWebsite), + public: true, + }; + + new SplitHorizonDns(stack, 'NeitherATestConstruct', { + zoneName: exampleDomain, + privateZoneVpcs: [vpc], + targets: [constructTarget], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.anyValue(), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('PublicZone'), + }), + AliasTarget: Match.objectLike({ + DNSName: Match.anyValue(), + HostedZoneId: Match.anyValue(), + }), + }); + }); + + it('can create a private A record for constructs', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack', { + env: { + account: '11111111', + region: 'us-east-1', + }, + }); + const vpc = new ec2.Vpc(stack, 'myvpc'); + + const bucketWebsite = new s3.Bucket(stack, 'BucketWebsite', { + bucketName: exampleDomain, // www.example.com + publicReadAccess: true, + websiteIndexDocument: 'index.html', + }); + + const constructTarget = { + target: new targets.BucketWebsiteTarget(bucketWebsite), + private: true, + }; + + new SplitHorizonDns(stack, 'NeitherATestConstruct', { + zoneName: exampleDomain, + privateZoneVpcs: [vpc], + targets: [constructTarget], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.anyValue(), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('PrivateZone'), + }), + AliasTarget: Match.objectLike({ + DNSName: Match.anyValue(), + HostedZoneId: Match.anyValue(), + }), + }); + }); + + it('can create a private and a public A record for constructs', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack', { + env: { + account: '11111111', + region: 'us-east-1', + }, + }); + const vpc = new ec2.Vpc(stack, 'myvpc'); + + const bucketWebsite = new s3.Bucket(stack, 'BucketWebsite', { + bucketName: exampleDomain, // www.example.com + publicReadAccess: true, + websiteIndexDocument: 'index.html', + }); + + const constructTarget = { + target: new targets.BucketWebsiteTarget(bucketWebsite), + private: true, + public: true, + }; + + new SplitHorizonDns(stack, 'NeitherATestConstruct', { + zoneName: exampleDomain, + privateZoneVpcs: [vpc], + targets: [constructTarget], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.anyValue(), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('PrivateZone'), + }), + AliasTarget: Match.objectLike({ + DNSName: Match.anyValue(), + HostedZoneId: Match.anyValue(), + }), + }); + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.anyValue(), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('PublicZone'), + }), + AliasTarget: Match.objectLike({ + DNSName: Match.anyValue(), + HostedZoneId: Match.anyValue(), + }), + }); + }); + + it('sets the TTL of records if provided', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + const vpc = new ec2.Vpc(stack, 'myvpc'); + + const firstTarget = { + target: [googleDns], + public: true, + ttl: Duration.seconds(3600), + }; + + new SplitHorizonDns(stack, 'PublicATestConstruct', { + zoneName: 'example.com', + privateZoneVpcs: [vpc], + targets: [firstTarget], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.stringLikeRegexp(`${exampleDomain}\.`), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('PublicZone'), + }), + TTL: '3600', + }); + }); + + it('omits the private zone if disallowed', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + new SplitHorizonDns(stack, 'MostBasicTestConstruct', { + zoneName: 'example.com', + disallowPrivateZone: true, + targets: [], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::HostedZone', Match.not({ + Name: Match.anyValue(), + VPCs: Match.arrayWith([ + Match.objectLike({ + VPCId: Match.anyValue(), + }), + ]), + })); + + template.hasResourceProperties('AWS::Route53::HostedZone', { + Name: Match.anyValue(), + }); + }); + + it('doesn\'t create private records if disallowed', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + + const firstTarget = { + target: [googleDns], + private: true, + public: true, + }; + + new SplitHorizonDns(stack, 'MostBasicTestConstruct', { + zoneName: 'example.com', + disallowPrivateZone: true, + targets: [firstTarget], + }); + + const template = Template.fromStack(stack); + + template.resourcePropertiesCountIs('AWS::Route53::RecordSet', { + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('PrivateZone'), + }), + }, 0); + }); + + it('can receive an existing public zone', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + const existingZone = new route53.PublicHostedZone(stack, 'ExistingPublicZone', { + zoneName: 'example.com', + }); + + const firstTarget = { + target: [googleDns], + public: true, + }; + + new SplitHorizonDns(stack, 'MostBasicTestConstruct', { + zoneName: 'example.com', + existingPublicZone: existingZone, + targets: [firstTarget], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.stringLikeRegexp(`${exampleDomain}\.`), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('ExistingPublicZone'), + }), + }); + }); + + it('can receive an existing private zone', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + const vpc = new ec2.Vpc(stack, 'myvpc'); + const existingZone = new route53.PrivateHostedZone(stack, 'ExistingPrivateZone', { + zoneName: 'example.com', + vpc, + }); + + const firstTarget = { + target: [googleDns], + private: true, + }; + + new SplitHorizonDns(stack, 'MostBasicTestConstruct', { + zoneName: 'example.com', + existingPrivateZone: existingZone, + targets: [firstTarget], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Route53::RecordSet', { + Name: Match.stringLikeRegexp(`${exampleDomain}\.`), + Type: 'A', + HostedZoneId: Match.objectLike({ + Ref: Match.stringLikeRegexp('ExistingPrivateZone'), + }), + }); + }); + + it('creates an ACM certificate if requested', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + + const firstTarget = { + target: [googleDns], + public: true, + }; + + new SplitHorizonDns(stack, 'MostBasicTestConstruct', { + zoneName: 'example.com', + includeCertificate: true, + targets: [firstTarget], + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::CertificateManager::Certificate', { + DomainName: exampleDomain, + }); + }); + + it('does not create an ACM certificate if requested', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + + const firstTarget = { + target: [googleDns], + public: true, + }; + + new SplitHorizonDns(stack, 'MostBasicTestConstruct', { + zoneName: 'example.com', + includeCertificate: false, + targets: [firstTarget], + }); + + const template = Template.fromStack(stack); + + template.resourceCountIs('AWS::CertificateManager::Certificate', 0); + }); +});