diff --git a/CHANGELOG.md b/CHANGELOG.md index 02bad01ae7f3f..61aaf8da350cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,44 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.79.0](https://github.com/aws/aws-cdk/compare/v1.78.0...v1.79.0) (2020-12-17) + + +### ⚠ BREAKING CHANGES TO EXPERIMENTAL FEATURES + +* **apigatewayv2:** `HttpApi.fromApiId()` has been replaced with +`HttpApi.fromHttpApiAttributes()`. +* **elasticsearch:** ES Domain LogGroup LogicalId will change, which will trigger new log group resources to be created + +### Features + +* **appmesh:** add timeout support to Routes ([#11973](https://github.com/aws/aws-cdk/issues/11973)) ([78c185d](https://github.com/aws/aws-cdk/commit/78c185d15e64e81ee86ee71cd6430cd80fdbb8fe)) +* **core:** expose custom resource provider's role ([#11923](https://github.com/aws/aws-cdk/issues/11923)) ([06f26d3](https://github.com/aws/aws-cdk/commit/06f26d390707b0e2a4e05e36405a4751c907a234)), closes [/github.com/aws/aws-cdk/pull/9751#issuecomment-723554595](https://github.com/aws//github.com/aws/aws-cdk/pull/9751/issues/issuecomment-723554595) +* **ec2:** add r5b instance type to instance class ([#12027](https://github.com/aws/aws-cdk/issues/12027)) ([d276b02](https://github.com/aws/aws-cdk/commit/d276b020e61ee4455c7ed9f093436d1aab319e76)), closes [#12025](https://github.com/aws/aws-cdk/issues/12025) +* **ecs-patterns:** Add DeploymentController option to Fargate services ([#10452](https://github.com/aws/aws-cdk/issues/10452)) ([2cd233a](https://github.com/aws/aws-cdk/commit/2cd233a94fc2f3cb06211157738e59e8c7ee85e5)), closes [aws/containers-roadmap#130](https://github.com/aws/containers-roadmap/issues/130) [#10971](https://github.com/aws/aws-cdk/issues/10971) +* **eks:** attach cluster security group to self-managed nodes ([#12042](https://github.com/aws/aws-cdk/issues/12042)) ([1078bea](https://github.com/aws/aws-cdk/commit/1078bea4c90afaac76a5e81328a9d6ec44a79e9a)) +* **elasticsearch:** support audit logs ([#12106](https://github.com/aws/aws-cdk/issues/12106)) ([d10ea63](https://github.com/aws/aws-cdk/commit/d10ea631f8699385cadf61d6e0a067b68da37df6)), closes [#12105](https://github.com/aws/aws-cdk/issues/12105) +* **ivs:** add IVS L2 Constructs ([#11454](https://github.com/aws/aws-cdk/issues/11454)) ([f813bff](https://github.com/aws/aws-cdk/commit/f813bff2da4792cfa7bfce6f572a7d2bb5c4759d)) +* **lambda:** encryption key for environment variables ([#11893](https://github.com/aws/aws-cdk/issues/11893)) ([ccbaf83](https://github.com/aws/aws-cdk/commit/ccbaf8399c3a9f3ff6e60758e0b713d82f37420b)), closes [#10837](https://github.com/aws/aws-cdk/issues/10837) +* **lambda-nodejs:** expose more esbuild options ([#12063](https://github.com/aws/aws-cdk/issues/12063)) ([bab21b3](https://github.com/aws/aws-cdk/commit/bab21b377593b7475b047d05a54914344352c054)), closes [#12046](https://github.com/aws/aws-cdk/issues/12046) +* **route53:** Vpc endpoint service private dns ([#10780](https://github.com/aws/aws-cdk/issues/10780)) ([8f6f9a8](https://github.com/aws/aws-cdk/commit/8f6f9a8678496e131a43ca4c76e561d50a0a0de8)) +* **s3-deployment:** support vpc in BucketDeploymentProps ([#12035](https://github.com/aws/aws-cdk/issues/12035)) ([6caf72f](https://github.com/aws/aws-cdk/commit/6caf72f67d6d3373186e57f32671369c2cc8b56e)), closes [#11734](https://github.com/aws/aws-cdk/issues/11734) +* **stepfunctions-tasks:** add support for ModelClientConfig to SageMakerCreateTransformJob ([#11892](https://github.com/aws/aws-cdk/issues/11892)) ([bf05092](https://github.com/aws/aws-cdk/commit/bf050928c033328b259746c0a7f33038aadc4c17)) + + +### Bug Fixes + +* **ec2:** 'encoded list token' error using Vpc imported from deploy-time lists ([#12040](https://github.com/aws/aws-cdk/issues/12040)) ([0690da9](https://github.com/aws/aws-cdk/commit/0690da925144c821a73bfab4ae8d678a8c074357)) +* **ec2:** fromInterfaceVpcEndpointAttributes: Security Groups should not be required ([#11857](https://github.com/aws/aws-cdk/issues/11857)) ([86ae5d6](https://github.com/aws/aws-cdk/commit/86ae5d6ec5291f7a8da37bbf021c31f88e66d283)), closes [#11050](https://github.com/aws/aws-cdk/issues/11050) +* **eks:** failure to deploy cluster since aws-auth configmap exists ([#12068](https://github.com/aws/aws-cdk/issues/12068)) ([dc8a98a](https://github.com/aws/aws-cdk/commit/dc8a98a5436a7a2347fa9676d84f73a8cf00cd49)), closes [#12053](https://github.com/aws/aws-cdk/issues/12053) +* **eks:** k8s resources accidentally deleted due to logical ID change ([#12053](https://github.com/aws/aws-cdk/issues/12053)) ([019852e](https://github.com/aws/aws-cdk/commit/019852e4834327d848c9fe8dc271f1d4d5117fb8)), closes [#10397](https://github.com/aws/aws-cdk/issues/10397) [#10397](https://github.com/aws/aws-cdk/issues/10397) +* **elasticsearch:** Defining 2 domains with logging enabled in the same stack fails on construct id conflict ([#12055](https://github.com/aws/aws-cdk/issues/12055)) ([ec3ce19](https://github.com/aws/aws-cdk/commit/ec3ce19bc8203703cb1abcecdb2afc674c2013f6)), closes [#12017](https://github.com/aws/aws-cdk/issues/12017) +* **elasticsearch:** log policies are overwritten when creating 2 domains which also results in a failure while destroying the stack ([#12056](https://github.com/aws/aws-cdk/issues/12056)) ([889d089](https://github.com/aws/aws-cdk/commit/889d0892bae10243e03900f0ae6db078fc7eb320)), closes [#12016](https://github.com/aws/aws-cdk/issues/12016) +* **stepfunctions-tasks:** policies created for EMR tasks have ARNs that are not partition-aware ([#11553](https://github.com/aws/aws-cdk/issues/11553)) ([1cf6713](https://github.com/aws/aws-cdk/commit/1cf6713b778c789af7a420ad890910a9516473f0)), closes [#11503](https://github.com/aws/aws-cdk/issues/11503) + + +* **apigatewayv2:** apiEndpoint is elevated to the IHttpApi interface ([#11988](https://github.com/aws/aws-cdk/issues/11988)) ([bc5b9b6](https://github.com/aws/aws-cdk/commit/bc5b9b659444bfbef9cfc3c8666fce7e6f45465a)) + ## [1.78.0](https://github.com/aws/aws-cdk/compare/v1.77.0...v1.78.0) (2020-12-11) diff --git a/packages/@aws-cdk/aws-ec2/README.md b/packages/@aws-cdk/aws-ec2/README.md index 664f3c2b2eff2..b8a583d1a2ea5 100644 --- a/packages/@aws-cdk/aws-ec2/README.md +++ b/packages/@aws-cdk/aws-ec2/README.md @@ -623,6 +623,23 @@ new VpcEndpointService(this, 'EndpointService', { }); ``` +Endpoint services support private DNS, which makes it easier for clients to connect to your service by automatically setting up DNS in their VPC. +You can enable private DNS on an endpoint service like so: + +```ts +import { VpcEndpointServiceDomainName } from '@aws-cdk/aws-route53'; + +new VpcEndpointServiceDomainName(stack, 'EndpointDomain', { + endpointService: vpces, + domainName: 'my-stuff.aws-cdk.dev', + publicHostedZone: zone, +}); +``` + +Note: The domain name must be owned (registered through Route53) by the account the endpoint service is in, or delegated to the account. +The VpcEndpointServiceDomainName will handle the AWS side of domain verification, the process for which can be found +[here](https://docs.aws.amazon.com/vpc/latest/userguide/endpoint-services-dns-validation.html) + ## Instances You can use the `Instance` class to start up a single EC2 instance. For production setups, we recommend diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint-service.ts b/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint-service.ts index d5d043eef0dc0..d17e56aab202c 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint-service.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint-service.ts @@ -27,6 +27,14 @@ export interface IVpcEndpointService extends IResource { * @attribute */ readonly vpcEndpointServiceName: string; + + /** + * The id of the VPC Endpoint Service that clients use to connect to, + * like vpce-svc-xxxxxxxxxxxxxxxx + * + * @attribute + */ + readonly vpcEndpointServiceId: string; } /** diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts index a1c7611e64825..5e4e160eaa717 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts @@ -87,7 +87,10 @@ export class NodejsFunction extends lambda.Function { if (!fs.existsSync(props.depsLockFilePath)) { throw new Error(`Lock file at ${props.depsLockFilePath} doesn't exist`); } - depsLockFilePath = props.depsLockFilePath; + if (!fs.statSync(props.depsLockFilePath).isFile()) { + throw new Error('`depsLockFilePath` should point to a file'); + } + depsLockFilePath = path.resolve(props.depsLockFilePath); } else { const lockFile = findUp(LockFile.YARN) ?? findUp(LockFile.NPM); if (!lockFile) { diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts index 743526d0e3899..d898d312be92c 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts @@ -107,6 +107,22 @@ test('throws with non existing lock file', () => { })).toThrow(/Lock file at \/does\/not\/exist.lock doesn't exist/); }); +test('throws when depsLockFilePath is not a file', () => { + expect(() => new NodejsFunction(stack, 'handler1', { + depsLockFilePath: __dirname, + })).toThrow(/\`depsLockFilePath\` should point to a file/); +}); + +test('resolves depsLockFilePath to an absolute path', () => { + new NodejsFunction(stack, 'handler1', { + depsLockFilePath: './package.json', + }); + + expect(Bundling.bundle).toHaveBeenCalledWith(expect.objectContaining({ + depsLockFilePath: expect.stringMatching(/@aws-cdk\/aws-lambda-nodejs\/package.json$/), + })); +}); + test('resolves entry to an absolute path', () => { // WHEN new NodejsFunction(stack, 'fn', { diff --git a/packages/@aws-cdk/aws-logs/lib/log-group.ts b/packages/@aws-cdk/aws-logs/lib/log-group.ts index 83c4a8e170b89..8b24506f11ba0 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-group.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-group.ts @@ -154,7 +154,7 @@ abstract class LogGroupBase extends Resource implements ILogGroup { } /** - * Give permissions to write to create and write to streams in this log group + * Give permissions to create and write to streams in this log group */ public grantWrite(grantee: iam.IGrantable) { return this.grant(grantee, 'logs:CreateLogStream', 'logs:PutLogEvents'); diff --git a/packages/@aws-cdk/aws-route53/README.md b/packages/@aws-cdk/aws-route53/README.md index d83ba3a547915..116dd217748cd 100644 --- a/packages/@aws-cdk/aws-route53/README.md +++ b/packages/@aws-cdk/aws-route53/README.md @@ -129,3 +129,51 @@ const zone = HostedZone.fromHostedZoneId(this, 'MyZone', { hostedZoneId: 'ZOJJZC49E0EPZ', }); ``` + +## VPC Endpoint Service Private DNS + +When you create a VPC endpoint service, AWS generates endpoint-specific DNS hostnames that consumers use to communicate with the service. +For example, vpce-1234-abcdev-us-east-1.vpce-svc-123345.us-east-1.vpce.amazonaws.com. +By default, your consumers access the service with that DNS name. +This can cause problems with HTTPS traffic because the DNS will not match the backend certificate: + +```console +curl: (60) SSL: no alternative certificate subject name matches target host name 'vpce-abcdefghijklmnopq-rstuvwx.vpce-svc-abcdefghijklmnopq.us-east-1.vpce.amazonaws.com' +``` + +Effectively, the endpoint appears untrustworthy. To mitigate this, clients have to create an alias for this DNS name in Route53. + +Private DNS for an endpoint service lets you configure a private DNS name so consumers can +access the service using an existing DNS name without creating this Route53 DNS alias +This DNS name can also be guaranteed to match up with the backend certificate. + +Before consumers can use the private DNS name, you must verify that you have control of the domain/subdomain. + +Assuming your account has ownership of the particlar domain/subdomain, +this construct sets up the private DNS configuration on the endpoint service, +creates all the necessary Route53 entries, and verifies domain ownership. + +```ts +import { Stack } from '@aws-cdk/core'; +import { Vpc, VpcEndpointService } from '@aws-cdk/aws-ec2'; +import { NetworkLoadBalancer } from '@aws-cdk/aws-elasticloadbalancingv2'; +import { PublicHostedZone } from '@aws-cdk/aws-route53'; + +stack = new Stack(); +vpc = new Vpc(stack, 'VPC'); +nlb = new NetworkLoadBalancer(stack, 'NLB', { + vpc, +}); +vpces = new VpcEndpointService(stack, 'VPCES', { + vpcEndpointServiceLoadBalancers: [nlb], +}); +// You must use a public hosted zone so domain ownership can be verified +zone = new PublicHostedZone(stack, 'PHZ', { + zoneName: 'aws-cdk.dev', +}); +new VpcEndpointServiceDomainName(stack, 'EndpointDomain', { + endpointService: vpces, + domainName: 'my-stuff.aws-cdk.dev', + publicHostedZone: zone, +}); +``` diff --git a/packages/@aws-cdk/aws-route53/lib/index.ts b/packages/@aws-cdk/aws-route53/lib/index.ts index 9a6ed0a853679..c35c57025fc26 100644 --- a/packages/@aws-cdk/aws-route53/lib/index.ts +++ b/packages/@aws-cdk/aws-route53/lib/index.ts @@ -1,8 +1,9 @@ +export * from './alias-record-target'; export * from './hosted-zone'; export * from './hosted-zone-provider'; export * from './hosted-zone-ref'; export * from './record-set'; -export * from './alias-record-target'; +export * from './vpc-endpoint-service-domain-name'; // AWS::Route53 CloudFormation Resources: export * from './route53.generated'; diff --git a/packages/@aws-cdk/aws-route53/lib/vpc-endpoint-service-domain-name.ts b/packages/@aws-cdk/aws-route53/lib/vpc-endpoint-service-domain-name.ts new file mode 100644 index 0000000000000..2af889661098a --- /dev/null +++ b/packages/@aws-cdk/aws-route53/lib/vpc-endpoint-service-domain-name.ts @@ -0,0 +1,230 @@ +import * as crypto from 'crypto'; +import { IVpcEndpointService } from '@aws-cdk/aws-ec2'; +import { Fn, Names, Stack } from '@aws-cdk/core'; +import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from '@aws-cdk/custom-resources'; +import { Construct } from 'constructs'; +import { IPublicHostedZone, TxtRecord } from '../lib'; + +// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch. +// eslint-disable-next-line +import { Construct as CoreConstruct } from '@aws-cdk/core'; + +/** + * Properties to configure a VPC Endpoint Service domain name + */ +export interface VpcEndpointServiceDomainNameProps { + + /** + * The VPC Endpoint Service to configure Private DNS for + */ + readonly endpointService: IVpcEndpointService; + + /** + * The domain name to use. + * + * This domain name must be owned by this account (registered through Route53), + * or delegated to this account. Domain ownership will be verified by AWS before + * private DNS can be used. + * @see https://docs.aws.amazon.com/vpc/latest/userguide/endpoint-services-dns-validation.html + */ + readonly domainName: string; + + /** + * The public hosted zone to use for the domain. + */ + readonly publicHostedZone: IPublicHostedZone; +} + +/** + * A Private DNS configuration for a VPC endpoint service. + */ +export class VpcEndpointServiceDomainName extends CoreConstruct { + + // Track all domain names created, so someone doesn't accidentally associate two domains with a single service + private static readonly endpointServices: IVpcEndpointService[] = []; + + // Track all domain names created, so someone doesn't accidentally associate two domains with a single service + private static readonly endpointServicesMap: { [endpointService: string]: string} = {}; + + // The way this class works is by using three custom resources and a TxtRecord in conjunction + // The first custom resource tells the VPC endpoint service to use the given DNS name + // The VPC endpoint service will then say: + // "ok, create a TXT record using these two values to prove you own the domain" + // The second custom resource retrieves these two values from the service + // The TxtRecord is created from these two values + // The third custom resource tells the VPC Endpoint Service to verify the domain ownership + constructor(scope: Construct, id: string, props: VpcEndpointServiceDomainNameProps) { + super(scope, id); + + const serviceUniqueId = Names.nodeUniqueId(props.endpointService.node); + const serviceId = props.endpointService.vpcEndpointServiceId; + const privateDnsName = props.domainName; + + // Make sure a user doesn't accidentally add multiple domains + this.validateProps(props); + + VpcEndpointServiceDomainName.endpointServicesMap[serviceUniqueId] = privateDnsName; + VpcEndpointServiceDomainName.endpointServices.push(props.endpointService); + + // Enable Private DNS on the endpoint service and retrieve the AWS-generated configuration + const privateDnsConfiguration = this.getPrivateDnsConfiguration(serviceUniqueId, serviceId, privateDnsName); + + // Tell AWS to verify that this account owns the domain attached to the service + this.verifyPrivateDnsConfiguration(privateDnsConfiguration, props.publicHostedZone); + + // Finally, don't do any of the above before the endpoint service is created + this.node.addDependency(props.endpointService); + } + + private validateProps(props: VpcEndpointServiceDomainNameProps): void { + const serviceUniqueId = Names.nodeUniqueId(props.endpointService.node); + if (serviceUniqueId in VpcEndpointServiceDomainName.endpointServicesMap) { + const endpoint = VpcEndpointServiceDomainName.endpointServicesMap[serviceUniqueId]; + throw new Error( + `Cannot create a VpcEndpointServiceDomainName for service ${serviceUniqueId}, another VpcEndpointServiceDomainName (${endpoint}) is already associated with it`); + } + } + + /** + * Sets up Custom Resources to make AWS calls to set up Private DNS on an endpoint service, + * returning the values to use in a TxtRecord, which AWS uses to verify domain ownership. + */ + private getPrivateDnsConfiguration(serviceUniqueId: string, serviceId: string, privateDnsName: string): PrivateDnsConfiguration { + + // The custom resource which tells AWS to enable Private DNS on the given service, using the given domain name + // AWS will generate a name/value pair for use in a TxtRecord, which is used to verify domain ownership. + const enablePrivateDnsAction = { + service: 'EC2', + action: 'modifyVpcEndpointServiceConfiguration', + parameters: { + ServiceId: serviceId, + PrivateDnsName: privateDnsName, + }, + physicalResourceId: PhysicalResourceId.of(serviceUniqueId), + }; + const removePrivateDnsAction = { + service: 'EC2', + action: 'modifyVpcEndpointServiceConfiguration', + parameters: { + ServiceId: serviceId, + RemovePrivateDnsName: true, + }, + }; + const enable = new AwsCustomResource(this, 'EnableDns', { + onCreate: enablePrivateDnsAction, + onUpdate: enablePrivateDnsAction, + onDelete: removePrivateDnsAction, + policy: AwsCustomResourcePolicy.fromSdkCalls({ + resources: [ + Fn.join(':', [ + 'arn', + Stack.of(this).partition, + 'ec2', + Stack.of(this).region, + Stack.of(this).account, + Fn.join('/', [ + 'vpc-endpoint-service', + serviceId, + ]), + ]), + ], + }), + }); + + // Look up the name/value pair if the domain changes, or the service changes, + // which would cause the values to be different. If the unique ID changes, + // the resource may be entirely recreated, so we will need to look it up again. + const lookup = hashcode(Names.uniqueId(this) + serviceUniqueId + privateDnsName); + + // Create the custom resource to look up the name/value pair generated by AWS + // after the previous API call + const retriveNameValuePairAction = { + service: 'EC2', + action: 'describeVpcEndpointServiceConfigurations', + parameters: { + ServiceIds: [serviceId], + }, + physicalResourceId: PhysicalResourceId.of(lookup), + }; + const getNames = new AwsCustomResource(this, 'GetNames', { + onCreate: retriveNameValuePairAction, + onUpdate: retriveNameValuePairAction, + // describeVpcEndpointServiceConfigurations can't take an ARN for granular permissions + policy: AwsCustomResourcePolicy.fromSdkCalls({ + resources: AwsCustomResourcePolicy.ANY_RESOURCE, + }), + }); + + // We only want to call and get the name/value pair after we've told AWS to enable Private DNS + // If we call before then, we'll get an empty pair of values. + getNames.node.addDependency(enable); + + // Get the references to the name/value pair associated with the endpoint service + const name = getNames.getResponseField('ServiceConfigurations.0.PrivateDnsNameConfiguration.Name'); + const value = getNames.getResponseField('ServiceConfigurations.0.PrivateDnsNameConfiguration.Value'); + + return { name, value, serviceId }; + } + + /** + * Creates a Route53 entry and a Custom Resource which explicitly tells AWS to verify ownership + * of the domain name attached to an endpoint service. + */ + private verifyPrivateDnsConfiguration(config: PrivateDnsConfiguration, publicHostedZone: IPublicHostedZone) { + // Create the TXT record in the provided hosted zone + const verificationRecord = new TxtRecord(this, 'DnsVerificationRecord', { + recordName: config.name, + values: [config.value], + zone: publicHostedZone, + }); + + // Tell the endpoint service to verify the domain ownership + const startVerificationAction = { + service: 'EC2', + action: 'startVpcEndpointServicePrivateDnsVerification', + parameters: { + ServiceId: config.serviceId, + }, + physicalResourceId: PhysicalResourceId.of(Fn.join(':', [config.name, config.value])), + }; + const startVerification = new AwsCustomResource(this, 'StartVerification', { + onCreate: startVerificationAction, + onUpdate: startVerificationAction, + policy: AwsCustomResourcePolicy.fromSdkCalls({ + resources: [ + Fn.join(':', [ + 'arn', + Stack.of(this).partition, + 'ec2', + Stack.of(this).region, + Stack.of(this).account, + Fn.join('/', [ + 'vpc-endpoint-service', + config.serviceId, + ]), + ]), + ], + }), + }); + // Only verify after the record has been created + startVerification.node.addDependency(verificationRecord); + } +} + +/** + * Represent the name/value pair associated with a Private DNS enabled endpoint service + */ +interface PrivateDnsConfiguration { + readonly name: string; + readonly value: string; + readonly serviceId: string; +} + +/** + * Hash a string + */ +function hashcode(s: string): string { + const hash = crypto.createHash('md5'); + hash.update(s); + return hash.digest('hex'); +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index c7cdda9c49f38..2145126c0e42d 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -78,6 +78,7 @@ "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", + "jest": "^26.6.0", "nodeunit": "^0.11.3", "pkglint": "0.0.0" }, @@ -86,6 +87,7 @@ "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/core": "0.0.0", "@aws-cdk/cloud-assembly-schema": "0.0.0", + "@aws-cdk/custom-resources": "0.0.0", "constructs": "^3.2.0" }, "homepage": "https://github.com/aws/aws-cdk", @@ -94,6 +96,7 @@ "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/core": "0.0.0", "@aws-cdk/cloud-assembly-schema": "0.0.0", + "@aws-cdk/custom-resources": "0.0.0", "constructs": "^3.2.0" }, "engines": { diff --git a/packages/@aws-cdk/aws-route53/test/integ.vpc-endpoint-service-domain-name.expected.json b/packages/@aws-cdk/aws-route53/test/integ.vpc-endpoint-service-domain-name.expected.json new file mode 100644 index 0000000000000..df7ab45491d39 --- /dev/null +++ b/packages/@aws-cdk/aws-route53/test/integ.vpc-endpoint-service-domain-name.expected.json @@ -0,0 +1,1004 @@ +{ + "Resources": { + "VPCB9E5F0B4": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC" + } + ] + } + }, + "VPCPublicSubnet1SubnetB4246D30": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableFEE4B781": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableAssociation0B0896DC": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "VPCPublicSubnet1DefaultRoute91CEF279": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet1EIP6AD938E8": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1NATGatewayE0556630": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet2Subnet74179F39": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.32.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTable6F1A15F1": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTableAssociation5A808732": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "VPCPublicSubnet2DefaultRouteB7481BBA": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet2EIP4947BC00": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2NATGateway3C070193": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet3Subnet631C5E25": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet3" + } + ] + } + }, + "VPCPublicSubnet3RouteTable98AE0E14": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet3" + } + ] + } + }, + "VPCPublicSubnet3RouteTableAssociation427FE0C6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet3RouteTable98AE0E14" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet3Subnet631C5E25" + } + } + }, + "VPCPublicSubnet3DefaultRouteA0D29D46": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet3RouteTable98AE0E14" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet3EIPAD4BC883": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet3" + } + ] + } + }, + "VPCPublicSubnet3NATGatewayD3048F5C": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet3EIPAD4BC883", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet3Subnet631C5E25" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PublicSubnet3" + } + ] + } + }, + "VPCPrivateSubnet1Subnet8BCA10E0": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.96.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableBE8A6027": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableAssociation347902D1": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "VPCPrivateSubnet1DefaultRouteAE1D6490": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "VPCPrivateSubnet2SubnetCFCDAA7A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTable0A19E10E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTableAssociation0C73D413": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "VPCPrivateSubnet2DefaultRouteF4F5CFD2": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet2NATGateway3C070193" + } + } + }, + "VPCPrivateSubnet3Subnet3EDCD457": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.160.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PrivateSubnet3" + } + ] + } + }, + "VPCPrivateSubnet3RouteTable192186F8": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC/PrivateSubnet3" + } + ] + } + }, + "VPCPrivateSubnet3RouteTableAssociationC28D144E": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet3RouteTable192186F8" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet3Subnet3EDCD457" + } + } + }, + "VPCPrivateSubnet3DefaultRoute27F311AE": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet3RouteTable192186F8" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet3NATGatewayD3048F5C" + } + } + }, + "VPCIGWB7E252D3": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-vpc-endpoint-dns-integ/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "mylb": { + "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", + "Properties": { + "Type": "network", + "Name": "mylb", + "Scheme": "internal", + "Subnets": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + ] + } + }, + "VPCES3AE7D565": { + "Type": "AWS::EC2::VPCEndpointService", + "Properties": { + "AcceptanceRequired": true, + "NetworkLoadBalancerArns": [ + { + "Ref": "mylb" + } + ] + } + }, + "PHZ45BE903D": { + "Type": "AWS::Route53::HostedZone", + "Properties": { + "Name": "aws-cdk.dev." + } + }, + "EndpointDomainEnableDnsCustomResourcePolicy5E6DE7EB": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ec2:ModifyVpcEndpointServiceConfiguration", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + ":", + [ + "arn", + { + "Ref": "AWS::Partition" + }, + "ec2", + { + "Ref": "AWS::Region" + }, + { + "Ref": "AWS::AccountId" + }, + { + "Fn::Join": [ + "/", + [ + "vpc-endpoint-service", + { + "Ref": "VPCES3AE7D565" + } + ] + ] + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "EndpointDomainEnableDnsCustomResourcePolicy5E6DE7EB", + "Roles": [ + { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + } + ] + }, + "DependsOn": [ + "VPCES3AE7D565" + ] + }, + "EndpointDomainEnableDnsDACBF5A6": { + "Type": "Custom::AWS", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "AWS679f53fac002430cb0da5b7982bd22872D164C4C", + "Arn" + ] + }, + "Create": { + "service": "EC2", + "action": "modifyVpcEndpointServiceConfiguration", + "parameters": { + "ServiceId": { + "Ref": "VPCES3AE7D565" + }, + "PrivateDnsName": "my-stuff.aws-cdk.dev" + }, + "physicalResourceId": { + "id": "awscdkvpcendpointdnsintegVPCES2D7BC258" + } + }, + "Update": { + "service": "EC2", + "action": "modifyVpcEndpointServiceConfiguration", + "parameters": { + "ServiceId": { + "Ref": "VPCES3AE7D565" + }, + "PrivateDnsName": "my-stuff.aws-cdk.dev" + }, + "physicalResourceId": { + "id": "awscdkvpcendpointdnsintegVPCES2D7BC258" + } + }, + "Delete": { + "service": "EC2", + "action": "modifyVpcEndpointServiceConfiguration", + "parameters": { + "ServiceId": { + "Ref": "VPCES3AE7D565" + }, + "RemovePrivateDnsName": "TRUE:BOOLEAN" + } + }, + "InstallLatestAwsSdk": true + }, + "DependsOn": [ + "EndpointDomainEnableDnsCustomResourcePolicy5E6DE7EB", + "VPCES3AE7D565" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "EndpointDomainGetNamesCustomResourcePolicy141775B1": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ec2:DescribeVpcEndpointServiceConfigurations", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "EndpointDomainGetNamesCustomResourcePolicy141775B1", + "Roles": [ + { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + } + ] + }, + "DependsOn": [ + "EndpointDomainEnableDnsCustomResourcePolicy5E6DE7EB", + "EndpointDomainEnableDnsDACBF5A6", + "VPCES3AE7D565" + ] + }, + "EndpointDomainGetNames9E697ED2": { + "Type": "Custom::AWS", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "AWS679f53fac002430cb0da5b7982bd22872D164C4C", + "Arn" + ] + }, + "Create": { + "service": "EC2", + "action": "describeVpcEndpointServiceConfigurations", + "parameters": { + "ServiceIds": [ + { + "Ref": "VPCES3AE7D565" + } + ] + }, + "physicalResourceId": { + "id": "0b26ca4969ad06c279e229b1b55b9bc2" + } + }, + "Update": { + "service": "EC2", + "action": "describeVpcEndpointServiceConfigurations", + "parameters": { + "ServiceIds": [ + { + "Ref": "VPCES3AE7D565" + } + ] + }, + "physicalResourceId": { + "id": "0b26ca4969ad06c279e229b1b55b9bc2" + } + }, + "InstallLatestAwsSdk": true + }, + "DependsOn": [ + "EndpointDomainEnableDnsCustomResourcePolicy5E6DE7EB", + "EndpointDomainEnableDnsDACBF5A6", + "EndpointDomainGetNamesCustomResourcePolicy141775B1", + "VPCES3AE7D565" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "EndpointDomainDnsVerificationRecord66623BDA": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "Name": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "EndpointDomainGetNames9E697ED2", + "ServiceConfigurations.0.PrivateDnsNameConfiguration.Name" + ] + }, + ".aws-cdk.dev." + ] + ] + }, + "Type": "TXT", + "HostedZoneId": { + "Ref": "PHZ45BE903D" + }, + "ResourceRecords": [ + { + "Fn::Join": [ + "", + [ + "\"", + { + "Fn::GetAtt": [ + "EndpointDomainGetNames9E697ED2", + "ServiceConfigurations.0.PrivateDnsNameConfiguration.Value" + ] + }, + "\"" + ] + ] + } + ], + "TTL": "1800" + }, + "DependsOn": [ + "VPCES3AE7D565" + ] + }, + "EndpointDomainStartVerificationCustomResourcePolicyD2BAC9A6": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ec2:StartVpcEndpointServicePrivateDnsVerification", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + ":", + [ + "arn", + { + "Ref": "AWS::Partition" + }, + "ec2", + { + "Ref": "AWS::Region" + }, + { + "Ref": "AWS::AccountId" + }, + { + "Fn::Join": [ + "/", + [ + "vpc-endpoint-service", + { + "Ref": "VPCES3AE7D565" + } + ] + ] + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "EndpointDomainStartVerificationCustomResourcePolicyD2BAC9A6", + "Roles": [ + { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + } + ] + }, + "DependsOn": [ + "EndpointDomainDnsVerificationRecord66623BDA", + "VPCES3AE7D565" + ] + }, + "EndpointDomainStartVerification05E2F7A3": { + "Type": "Custom::AWS", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "AWS679f53fac002430cb0da5b7982bd22872D164C4C", + "Arn" + ] + }, + "Create": { + "service": "EC2", + "action": "startVpcEndpointServicePrivateDnsVerification", + "parameters": { + "ServiceId": { + "Ref": "VPCES3AE7D565" + } + }, + "physicalResourceId": { + "id": { + "Fn::Join": [ + ":", + [ + { + "Fn::GetAtt": [ + "EndpointDomainGetNames9E697ED2", + "ServiceConfigurations.0.PrivateDnsNameConfiguration.Name" + ] + }, + { + "Fn::GetAtt": [ + "EndpointDomainGetNames9E697ED2", + "ServiceConfigurations.0.PrivateDnsNameConfiguration.Value" + ] + } + ] + ] + } + } + }, + "Update": { + "service": "EC2", + "action": "startVpcEndpointServicePrivateDnsVerification", + "parameters": { + "ServiceId": { + "Ref": "VPCES3AE7D565" + } + }, + "physicalResourceId": { + "id": { + "Fn::Join": [ + ":", + [ + { + "Fn::GetAtt": [ + "EndpointDomainGetNames9E697ED2", + "ServiceConfigurations.0.PrivateDnsNameConfiguration.Name" + ] + }, + { + "Fn::GetAtt": [ + "EndpointDomainGetNames9E697ED2", + "ServiceConfigurations.0.PrivateDnsNameConfiguration.Value" + ] + } + ] + ] + } + } + }, + "InstallLatestAwsSdk": true + }, + "DependsOn": [ + "EndpointDomainDnsVerificationRecord66623BDA", + "EndpointDomainStartVerificationCustomResourcePolicyD2BAC9A6", + "VPCES3AE7D565" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "AWS679f53fac002430cb0da5b7982bd22872D164C4C": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParametersb64b129569a5ac7a9abf88a18ac0b504d1fb1208872460476ed3fd435830eb94S3Bucket38F1BB8E" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersb64b129569a5ac7a9abf88a18ac0b504d1fb1208872460476ed3fd435830eb94S3VersionKeyCCDC67C0" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersb64b129569a5ac7a9abf88a18ac0b504d1fb1208872460476ed3fd435830eb94S3VersionKeyCCDC67C0" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Timeout": 120 + }, + "DependsOn": [ + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + ] + } + }, + "Parameters": { + "AssetParametersb64b129569a5ac7a9abf88a18ac0b504d1fb1208872460476ed3fd435830eb94S3Bucket38F1BB8E": { + "Type": "String", + "Description": "S3 bucket for asset \"b64b129569a5ac7a9abf88a18ac0b504d1fb1208872460476ed3fd435830eb94\"" + }, + "AssetParametersb64b129569a5ac7a9abf88a18ac0b504d1fb1208872460476ed3fd435830eb94S3VersionKeyCCDC67C0": { + "Type": "String", + "Description": "S3 key for asset version \"b64b129569a5ac7a9abf88a18ac0b504d1fb1208872460476ed3fd435830eb94\"" + }, + "AssetParametersb64b129569a5ac7a9abf88a18ac0b504d1fb1208872460476ed3fd435830eb94ArtifactHash782948FC": { + "Type": "String", + "Description": "Artifact hash for asset \"b64b129569a5ac7a9abf88a18ac0b504d1fb1208872460476ed3fd435830eb94\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-route53/test/integ.vpc-endpoint-service-domain-name.ts b/packages/@aws-cdk/aws-route53/test/integ.vpc-endpoint-service-domain-name.ts new file mode 100644 index 0000000000000..672b2133f077b --- /dev/null +++ b/packages/@aws-cdk/aws-route53/test/integ.vpc-endpoint-service-domain-name.ts @@ -0,0 +1,48 @@ +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { PublicHostedZone, VpcEndpointServiceDomainName } from '../lib'; + +/** + * A load balancer that can host a VPC Endpoint Service. + * + * Why do this instead of using the NetworkLoadBalancer construct? aws-route53 + * cannot depend on aws-elasticloadbalancingv2 because aws-elasticloadbalancingv2 + * already takes a dependency on aws-route53. + */ +class DummyEndpointLoadBalancer implements ec2.IVpcEndpointServiceLoadBalancer { + /** + * The ARN of the load balancer that hosts the VPC Endpoint Service + */ + public readonly loadBalancerArn: string; + constructor(scope: Construct, id: string, vpc: ec2.Vpc) { + const lb = new cdk.CfnResource(scope, id, { + type: 'AWS::ElasticLoadBalancingV2::LoadBalancer', + properties: { + Type: 'network', + Name: 'mylb', + Scheme: 'internal', + Subnets: [vpc.privateSubnets[0].subnetId], + }, + }); + this.loadBalancerArn = lb.ref; + } +} + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-vpc-endpoint-dns-integ'); +const vpc = new ec2.Vpc(stack, 'VPC'); +const nlb = new DummyEndpointLoadBalancer(stack, 'mylb', vpc); +const vpces = new ec2.VpcEndpointService(stack, 'VPCES', { + vpcEndpointServiceLoadBalancers: [nlb], +}); +const zone = new PublicHostedZone(stack, 'PHZ', { + zoneName: 'aws-cdk.dev', +}); +new VpcEndpointServiceDomainName(stack, 'EndpointDomain', { + endpointService: vpces, + domainName: 'my-stuff.aws-cdk.dev', + publicHostedZone: zone, +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-route53/test/vpc-endpoint-service-domain-name.test.ts b/packages/@aws-cdk/aws-route53/test/vpc-endpoint-service-domain-name.test.ts new file mode 100644 index 0000000000000..70ba07c201d5f --- /dev/null +++ b/packages/@aws-cdk/aws-route53/test/vpc-endpoint-service-domain-name.test.ts @@ -0,0 +1,254 @@ +import { expect as cdkExpect, haveResource, haveResourceLike, ResourcePart } from '@aws-cdk/assert'; +import '@aws-cdk/assert/jest'; +import { IVpcEndpointServiceLoadBalancer, VpcEndpointService } from '@aws-cdk/aws-ec2'; +import { Stack } from '@aws-cdk/core'; +import { PublicHostedZone, VpcEndpointServiceDomainName } from '../lib'; + +let stack: Stack; +let nlb: IVpcEndpointServiceLoadBalancer; +let vpces: VpcEndpointService; +let zone: PublicHostedZone; + +/** + * A load balancer that can host a VPC Endpoint Service + */ +class DummyEndpointLoadBalancer implements IVpcEndpointServiceLoadBalancer { + /** + * The ARN of the load balancer that hosts the VPC Endpoint Service + */ + public readonly loadBalancerArn: string; + constructor(arn: string) { + this.loadBalancerArn = arn; + } +} + +beforeEach(() => { + stack = new Stack(); + nlb = new DummyEndpointLoadBalancer('arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/net/Test/9bn6qkf4e9jrw77a'); + vpces = new VpcEndpointService(stack, 'VPCES', { + vpcEndpointServiceLoadBalancers: [nlb], + }); + zone = new PublicHostedZone(stack, 'PHZ', { + zoneName: 'aws-cdk.dev', + }); +}); + +test('create domain name resource', () => { + // GIVEN + + // WHEN + new VpcEndpointServiceDomainName(stack, 'EndpointDomain', { + endpointService: vpces, + domainName: 'my-stuff.aws-cdk.dev', + publicHostedZone: zone, + }); + + // THEN + cdkExpect(stack).to(haveResourceLike('Custom::AWS', { + Properties: { + Create: { + action: 'modifyVpcEndpointServiceConfiguration', + service: 'EC2', + parameters: { + PrivateDnsName: 'my-stuff.aws-cdk.dev', + ServiceId: { + Ref: 'VPCES3AE7D565', + }, + }, + physicalResourceId: { + id: 'EndpointDomain', + }, + }, + Update: { + action: 'modifyVpcEndpointServiceConfiguration', + service: 'EC2', + parameters: { + PrivateDnsName: 'my-stuff.aws-cdk.dev', + ServiceId: { + Ref: 'VPCES3AE7D565', + }, + }, + physicalResourceId: { + id: 'EndpointDomain', + }, + }, + Delete: { + action: 'modifyVpcEndpointServiceConfiguration', + service: 'EC2', + parameters: { + RemovePrivateDnsName: 'TRUE:BOOLEAN', + ServiceId: { + Ref: 'VPCES3AE7D565', + }, + }, + }, + }, + DependsOn: [ + 'EndpointDomainEnableDnsCustomResourcePolicy5E6DE7EB', + 'VPCES3AE7D565', + ], + }, ResourcePart.CompleteDefinition)); + + // Have to use `haveResourceLike` because there is a property that, by design, changes on every build + cdkExpect(stack).to(haveResourceLike('Custom::AWS', { + Properties: { + Create: { + action: 'describeVpcEndpointServiceConfigurations', + service: 'EC2', + parameters: { + ServiceIds: [{ + Ref: 'VPCES3AE7D565', + }], + }, + }, + Update: { + action: 'describeVpcEndpointServiceConfigurations', + service: 'EC2', + parameters: { + ServiceIds: [{ + Ref: 'VPCES3AE7D565', + }], + }, + }, + }, + DependsOn: [ + 'EndpointDomainEnableDnsCustomResourcePolicy5E6DE7EB', + 'EndpointDomainEnableDnsDACBF5A6', + 'EndpointDomainGetNamesCustomResourcePolicy141775B1', + 'VPCES3AE7D565', + ], + }, ResourcePart.CompleteDefinition)); + + cdkExpect(stack).to(haveResource('AWS::Route53::RecordSet', { + Properties: { + Name: { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'EndpointDomainGetNames9E697ED2', + 'ServiceConfigurations.0.PrivateDnsNameConfiguration.Name', + ], + }, + '.aws-cdk.dev.', + ], + ], + }, + Type: 'TXT', + HostedZoneId: { + Ref: 'PHZ45BE903D', + }, + ResourceRecords: [ + { + 'Fn::Join': [ + '', + [ + '\"', + { + 'Fn::GetAtt': [ + 'EndpointDomainGetNames9E697ED2', + 'ServiceConfigurations.0.PrivateDnsNameConfiguration.Value', + ], + }, + '\"', + ], + ], + }, + ], + TTL: '1800', + }, + DependsOn: [ + 'VPCES3AE7D565', + ], + }, ResourcePart.CompleteDefinition)); + + cdkExpect(stack).to(haveResourceLike('Custom::AWS', { + Properties: { + Create: { + action: 'startVpcEndpointServicePrivateDnsVerification', + service: 'EC2', + parameters: { + ServiceId: { + Ref: 'VPCES3AE7D565', + }, + }, + physicalResourceId: { + id: { + 'Fn::Join': [ + ':', + [ + { + 'Fn::GetAtt': [ + 'EndpointDomainGetNames9E697ED2', + 'ServiceConfigurations.0.PrivateDnsNameConfiguration.Name', + ], + }, + { + 'Fn::GetAtt': [ + 'EndpointDomainGetNames9E697ED2', + 'ServiceConfigurations.0.PrivateDnsNameConfiguration.Value', + ], + }, + ], + ], + }, + }, + }, + Update: { + action: 'startVpcEndpointServicePrivateDnsVerification', + service: 'EC2', + parameters: { + ServiceId: { + Ref: 'VPCES3AE7D565', + }, + }, + physicalResourceId: { + id: { + 'Fn::Join': [ + ':', + [ + { + 'Fn::GetAtt': [ + 'EndpointDomainGetNames9E697ED2', + 'ServiceConfigurations.0.PrivateDnsNameConfiguration.Name', + ], + }, + { + 'Fn::GetAtt': [ + 'EndpointDomainGetNames9E697ED2', + 'ServiceConfigurations.0.PrivateDnsNameConfiguration.Value', + ], + }, + ], + ], + }, + }, + }, + }, + DependsOn: [ + 'EndpointDomainDnsVerificationRecord66623BDA', + 'EndpointDomainStartVerificationCustomResourcePolicyD2BAC9A6', + 'VPCES3AE7D565', + ], + }, ResourcePart.CompleteDefinition)); +}); + +test('throws if creating multiple domains for a single service', () => { + // GIVEN + + new VpcEndpointServiceDomainName(stack, 'EndpointDomain', { + endpointService: vpces, + domainName: 'my-stuff-1.aws-cdk.dev', + publicHostedZone: zone, + }); + + // WHEN / THEN + expect(() => { + new VpcEndpointServiceDomainName(stack, 'EndpointDomain2', { + endpointService: vpces, + domainName: 'my-stuff-2.aws-cdk.dev', + publicHostedZone: zone, + }); + }).toThrow(); +}); \ No newline at end of file diff --git a/version.v1.json b/version.v1.json index 0e9c47b816903..1d87fb5f6b025 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.78.0" + "version": "1.79.0" }