From 5e43348ecdb6a8da865bb0db22c4782b6fa4bc96 Mon Sep 17 00:00:00 2001
From: Pahud Hsieh <pahudnet@gmail.com>
Date: Mon, 6 Jul 2020 18:48:38 +0800
Subject: [PATCH 01/22] feat(apigatewayv2): http api - custom domain & stage
 mapping (#8027)

- [x] implementation
- [x] README
- [x] integ test
- [x] 100% unit test coverage

### Commit Message
feat(apigatewayv2): http api - custom domain & stage mapping (#8027)

- Add new `DomainName` and `HttpApiMapping` construct classes and `addDomainName()` method for `HttpApi` resource.
- Add `defaultDomainMapping` construct property for `HttpApi`
- Add `domainMapping` attribute for `addStage`

Closes #7847

### End Commit Message

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-apigatewayv2/.npmignore |   1 +
 packages/@aws-cdk/aws-apigatewayv2/README.md  |  66 ++++
 .../lib/common/api-mapping.ts                 |  13 +
 .../lib/common/domain-name.ts                 | 117 ++++++
 .../aws-apigatewayv2/lib/common/index.ts      |   4 +-
 .../aws-apigatewayv2/lib/http/api-mapping.ts  |  78 ++++
 .../@aws-cdk/aws-apigatewayv2/lib/http/api.ts |  20 +-
 .../aws-apigatewayv2/lib/http/index.ts        |   3 +-
 .../aws-apigatewayv2/lib/http/stage.ts        |  57 ++-
 .../@aws-cdk/aws-apigatewayv2/package.json    |   4 +
 .../test/http/api-mapping.test.ts             |  89 +++++
 .../aws-apigatewayv2/test/http/api.test.ts    |  11 +-
 .../test/http/domain-name.test.ts             | 154 ++++++++
 .../http/integ.custom-domain.expected.json    | 348 ++++++++++++++++++
 .../test/http/integ.custom-domain.ts          |  57 +++
 .../test/http/integrations/http.test.ts       |  36 +-
 .../aws-apigatewayv2/test/http/route.test.ts  |  23 ++
 .../aws-apigatewayv2/test/http/stage.test.ts  |  15 +
 18 files changed, 1089 insertions(+), 7 deletions(-)
 create mode 100644 packages/@aws-cdk/aws-apigatewayv2/lib/common/api-mapping.ts
 create mode 100644 packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts
 create mode 100644 packages/@aws-cdk/aws-apigatewayv2/lib/http/api-mapping.ts
 create mode 100644 packages/@aws-cdk/aws-apigatewayv2/test/http/api-mapping.test.ts
 create mode 100644 packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts
 create mode 100644 packages/@aws-cdk/aws-apigatewayv2/test/http/integ.custom-domain.expected.json
 create mode 100644 packages/@aws-cdk/aws-apigatewayv2/test/http/integ.custom-domain.ts

diff --git a/packages/@aws-cdk/aws-apigatewayv2/.npmignore b/packages/@aws-cdk/aws-apigatewayv2/.npmignore
index 683e3e0847e1f..4bed40b6573da 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/.npmignore
+++ b/packages/@aws-cdk/aws-apigatewayv2/.npmignore
@@ -4,6 +4,7 @@
 *.snk
 !*.d.ts
 !*.js
+**/cdk.out
 
 # Coverage
 coverage
diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md
index a885fa9979695..cbf0958608abd 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/README.md
+++ b/packages/@aws-cdk/aws-apigatewayv2/README.md
@@ -21,6 +21,7 @@
   - [Defining HTTP APIs](#defining-http-apis)
   - [Cross Origin Resource Sharing (CORS)](#cross-origin-resource-sharing-cors)
   - [Publishing HTTP APIs](#publishing-http-apis)
+  - [Custom Domain](#custom-domain)
 
 ## Introduction
 
@@ -134,3 +135,68 @@ If you omit the `stageName` will create a `$default` stage. A `$default` stage i
 the API's URL - `https://{api_id}.execute-api.{region}.amazonaws.com/`.
 
 Note that, `HttpApi` will always creates a `$default` stage, unless the `createDefaultStage` property is unset.
+
+
+
+### Custom Domain
+
+Custom domain names are simpler and more intuitive URLs that you can provide to your API users. Custom domain name are associated to API stages.
+
+The code snippet below creates a custom domain and configures a default domain mapping for your API that maps the
+custom domain to the `$default` stage of the API.
+
+```ts
+const certArn = 'arn:aws:acm:us-east-1:111111111111:certificate';
+const domainName = 'example.com';
+
+const dn = new DomainName(stack, 'DN', {
+  domainName,
+  certificate: acm.Certificate.fromCertificateArn(stack, 'cert', certArn),
+});
+
+const api = new HttpApi(stack, 'HttpProxyProdApi', {
+  defaultIntegration: new LambdaProxyIntegration({ handler }),
+  // https://${dn.domainName} goes to prodApi $default stage
+  defaultDomainMapping: {
+    domainName: dn,
+    mappingKey: '/',
+  },
+});
+```
+
+To associate a specifc `Stage` to a custom domain mapping -
+
+```ts
+api.addStage('beta', {
+  stageName: 'beta',
+  autoDeploy: true,
+  // https://${dn.domainName}/beta goes to the beta stage
+  domainMapping: {
+    domainName: dn,
+    mappingKey: 'beta',
+  },
+});
+```
+
+The same domain name can be associated with stages across different `HttpApi` as so -
+
+```ts
+const apiDemo = new HttpApi(stack, 'DemoApi', {
+  defaultIntegration: new LambdaProxyIntegration({ handler }),
+  // https://${dn.domainName}/demo goes to apiDemo $default stage
+  defaultDomainMapping: {
+    domainName: dn,
+    mappingKey: 'demo',
+  },
+});
+```
+
+The `mappingKey` determines the `path` of the URL with the custom domain. Each custom domain is only allowed
+to have one API mapping with the root(/) `mappingKey`. In the sample above, the custom domain is associated
+with 3 API mapping resources across different APIs and Stages.
+
+|        API     |     Stage   |   URL  |
+| :------------: | :---------: | :----: |
+| api | $default  |   `https://${domainName}`  |
+| api | beta  |   `https://${domainName}/beta`  |
+| apiDemo | $default  |   `https://${domainName}/demo`  |
diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/api-mapping.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/api-mapping.ts
new file mode 100644
index 0000000000000..d843b51f8b315
--- /dev/null
+++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/api-mapping.ts
@@ -0,0 +1,13 @@
+import { IResource } from '@aws-cdk/core';
+
+/**
+ * Represents an ApiGatewayV2 ApiMapping resource
+ * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigatewayv2-apimapping.html
+ */
+export interface IApiMapping extends IResource {
+  /**
+   * ID of the api mapping
+   * @attribute
+   */
+  readonly apiMappingId: string;
+}
diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts
new file mode 100644
index 0000000000000..93234807bbf09
--- /dev/null
+++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts
@@ -0,0 +1,117 @@
+import { ICertificate } from '@aws-cdk/aws-certificatemanager';
+import { Construct, IResource, Resource, Token } from '@aws-cdk/core';
+import { CfnDomainName, CfnDomainNameProps } from '../apigatewayv2.generated';
+
+/**
+ * Represents an APIGatewayV2 DomainName
+ * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigatewayv2-domainname.html
+ */
+export interface IDomainName extends IResource {
+  /**
+   * The custom domain name
+   *
+   * @attribute
+   *
+   */
+  readonly domainName: string;
+
+  /**
+   * The domain name associated with the regional endpoint for this custom domain name.
+   *
+   * @attribute
+   */
+  readonly regionalDomainName: string;
+
+  /**
+   * The region-specific Amazon Route 53 Hosted Zone ID of the regional endpoint.
+   *
+   * @attribute
+   */
+  readonly regionalHostedZoneId: string;
+}
+
+/**
+ * custom domain name attributes
+ */
+export interface DomainNameAttributes {
+  /**
+   * domain name string
+   */
+  readonly domainName: string;
+
+  /**
+   * The domain name associated with the regional endpoint for this custom domain name.
+   */
+  readonly regionalDomainName: string;
+
+  /**
+   * The region-specific Amazon Route 53 Hosted Zone ID of the regional endpoint.
+   */
+  readonly regionalHostedZoneId: string;
+}
+
+/**
+ * properties used for creating the DomainName
+ */
+export interface DomainNameProps {
+  /**
+   * The custom domain name
+   */
+  readonly domainName: string;
+  /**
+   * The ACM certificate for this domain name
+   */
+  readonly certificate: ICertificate;
+}
+
+/**
+ * Custom domain resource for the API
+ */
+export class DomainName extends Resource implements IDomainName {
+  /**
+   * import from attributes
+   */
+  public static fromDomainNameAttributes(scope: Construct, id: string, attrs: DomainNameAttributes): IDomainName {
+    class Import extends Resource implements IDomainName {
+      public readonly regionalDomainName = attrs.regionalDomainName;
+      public readonly regionalHostedZoneId = attrs.regionalHostedZoneId;
+      public readonly domainName = attrs.domainName;
+    }
+    return new Import(scope, id);
+  }
+
+  /**
+   * The custom domain name for your API in Amazon API Gateway.
+   *
+   * @attribute
+   */
+  public readonly domainName: string;
+
+  /**
+   * The domain name associated with the regional endpoint for this custom domain name.
+   */
+  public readonly regionalDomainName: string;
+
+  /**
+   * The region-specific Amazon Route 53 Hosted Zone ID of the regional endpoint.
+   */
+  public readonly regionalHostedZoneId: string;
+
+  constructor(scope: Construct, id: string, props: DomainNameProps) {
+    super(scope, id);
+
+    const domainNameProps: CfnDomainNameProps = {
+      domainName: props.domainName,
+      domainNameConfigurations: [
+        {
+          certificateArn: props.certificate.certificateArn,
+          endpointType: 'REGIONAL',
+        },
+      ],
+    };
+    const resource = new CfnDomainName(this, 'Resource', domainNameProps);
+    this.domainName = props.domainName ?? resource.ref;
+    this.regionalDomainName = Token.asString(resource.getAtt('RegionalDomainName'));
+    this.regionalHostedZoneId = Token.asString(resource.getAtt('RegionalHostedZoneId'));
+  }
+}
diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts
index 5995c40125978..d727436b86c99 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts
+++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts
@@ -1,3 +1,5 @@
 export * from './integration';
 export * from './route';
-export * from './stage';
\ No newline at end of file
+export * from './stage';
+export * from './domain-name';
+export * from './api-mapping';
diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api-mapping.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api-mapping.ts
new file mode 100644
index 0000000000000..855c6a1f30638
--- /dev/null
+++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api-mapping.ts
@@ -0,0 +1,78 @@
+import { Construct, Resource } from '@aws-cdk/core';
+import { CfnApiMapping, CfnApiMappingProps } from '../apigatewayv2.generated';
+import { IApiMapping, IDomainName } from '../common';
+import { IHttpApi } from '../http/api';
+import { IHttpStage } from './stage';
+
+/**
+ * Properties used to create the HttpApiMapping resource
+ */
+export interface HttpApiMappingProps {
+  /**
+   * Api mapping key. The path where this stage should be mapped to on the domain
+   * @default '/'
+   */
+  readonly apiMappingKey?: string;
+
+  /**
+   * The HttpApi to which this mapping is applied
+   */
+  readonly api: IHttpApi;
+
+  /**
+   * custom domain name of the mapping target
+   */
+  readonly domainName: IDomainName;
+
+  /**
+   * stage for the HttpApiMapping resource
+   *
+   * @default - the $default stage
+   */
+  readonly stage?: IHttpStage;
+}
+
+/**
+ * The attributes used to import existing HttpApiMapping
+ */
+export interface HttpApiMappingAttributes {
+  /**
+   * The API mapping ID
+   */
+  readonly apiMappingId: string;
+}
+
+/**
+ * Create a new API mapping for API Gateway HTTP API endpoint.
+ * @resource AWS::ApiGatewayV2::ApiMapping
+ */
+export class HttpApiMapping extends Resource implements IApiMapping {
+  /**
+   * import from API ID
+   */
+  public static fromHttpApiMappingAttributes(scope: Construct, id: string, attrs: HttpApiMappingAttributes): IApiMapping {
+    class Import extends Resource implements IApiMapping {
+      public readonly apiMappingId = attrs.apiMappingId;
+    }
+    return new Import(scope, id);
+  }
+  /**
+   * ID of the API Mapping
+   */
+  public readonly apiMappingId: string;
+
+  constructor(scope: Construct, id: string, props: HttpApiMappingProps) {
+    super(scope, id);
+
+    const apiMappingProps: CfnApiMappingProps = {
+      apiId: props.api.httpApiId,
+      domainName: props.domainName.domainName,
+      stage: props.stage?.stageName ?? '$default',
+      apiMappingKey: props.apiMappingKey,
+    };
+
+    const resource = new CfnApiMapping(this, 'Resource', apiMappingProps);
+    this.apiMappingId = resource.ref;
+  }
+
+}
diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts
index 7856ff5f87bf7..c8a12d35ed354 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts
+++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts
@@ -1,5 +1,6 @@
 import { Construct, Duration, IResource, Resource } from '@aws-cdk/core';
 import { CfnApi, CfnApiProps } from '../apigatewayv2.generated';
+import { DefaultDomainMappingOptions } from '../http/stage';
 import { IHttpRouteIntegration } from './integration';
 import { BatchHttpRouteOptions, HttpMethod, HttpRoute, HttpRouteKey } from './route';
 import { HttpStage, HttpStageOptions } from './stage';
@@ -43,6 +44,13 @@ export interface HttpApiProps {
    * @default - CORS disabled.
    */
   readonly corsPreflight?: CorsPreflightOptions;
+
+  /**
+   * Configure a custom domain with the API mapping resource to the HTTP API
+   *
+   * @default - no default domain mapping configured. meaningless if `createDefaultStage` is `false`.
+   */
+  readonly defaultDomainMapping?: DefaultDomainMappingOptions;
 }
 
 /**
@@ -118,6 +126,9 @@ export class HttpApi extends Resource implements IHttpApi {
   }
 
   public readonly httpApiId: string;
+  /**
+   * default stage of the api resource
+   */
   private readonly defaultStage: HttpStage | undefined;
 
   constructor(scope: Construct, id: string, props?: HttpApiProps) {
@@ -166,8 +177,14 @@ export class HttpApi extends Resource implements IHttpApi {
       this.defaultStage = new HttpStage(this, 'DefaultStage', {
         httpApi: this,
         autoDeploy: true,
+        domainMapping: props?.defaultDomainMapping,
       });
     }
+
+    if (props?.createDefaultStage === false && props.defaultDomainMapping) {
+      throw new Error('defaultDomainMapping not supported with createDefaultStage disabled',
+      );
+    }
   }
 
   /**
@@ -182,10 +199,11 @@ export class HttpApi extends Resource implements IHttpApi {
    * Add a new stage.
    */
   public addStage(id: string, options: HttpStageOptions): HttpStage {
-    return new HttpStage(this, id, {
+    const stage = new HttpStage(this, id, {
       httpApi: this,
       ...options,
     });
+    return stage;
   }
 
   /**
diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts
index 4fbb1c4e76f6a..c42e089aa1d08 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts
+++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts
@@ -2,4 +2,5 @@ export * from './api';
 export * from './route';
 export * from './integration';
 export * from './integrations';
-export * from './stage';
\ No newline at end of file
+export * from './stage';
+export * from './api-mapping';
diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/stage.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/stage.ts
index 1335cbf839984..8fc3e605d87b4 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/stage.ts
+++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/stage.ts
@@ -1,14 +1,27 @@
 import { Construct, Resource, Stack } from '@aws-cdk/core';
 import { CfnStage } from '../apigatewayv2.generated';
-import { CommonStageOptions, IStage } from '../common';
+import { CommonStageOptions, IDomainName, IStage } from '../common';
 import { IHttpApi } from './api';
+import { HttpApiMapping } from './api-mapping';
 
 const DEFAULT_STAGE_NAME = '$default';
 
+/**
+ * Represents the HttpStage
+ */
+export interface IHttpStage extends IStage {
+}
+
 /**
  * Options to create a new stage for an HTTP API.
  */
 export interface HttpStageOptions extends CommonStageOptions {
+  /**
+   * The options for custom domain and api mapping
+   *
+   * @default - no custom domain and api mapping configuration
+   */
+  readonly domainMapping?: DomainMappingOptions;
 }
 
 /**
@@ -21,6 +34,36 @@ export interface HttpStageProps extends HttpStageOptions {
   readonly httpApi: IHttpApi;
 }
 
+/**
+ * Options for defaultDomainMapping
+ */
+export interface DefaultDomainMappingOptions {
+  /**
+   * The domain name for the mapping
+   *
+   */
+  readonly domainName: IDomainName;
+
+  /**
+   * The API mapping key. Specify '/' for the root path mapping.
+   *
+   */
+  readonly mappingKey: string;
+
+}
+
+/**
+ * Options for DomainMapping
+ */
+export interface DomainMappingOptions extends DefaultDomainMappingOptions {
+  /**
+   * The API Stage
+   *
+   * @default - the $default stage
+   */
+  readonly stage?: IStage;
+}
+
 /**
  * Represents a stage where an instance of the API is deployed.
  * @resource AWS::ApiGatewayV2::Stage
@@ -52,6 +95,16 @@ export class HttpStage extends Resource implements IStage {
 
     this.stageName = this.physicalName;
     this.httpApi = props.httpApi;
+
+    if (props.domainMapping) {
+      new HttpApiMapping(this, `${props.domainMapping.domainName}${props.domainMapping.mappingKey}`, {
+        api: props.httpApi,
+        domainName: props.domainMapping.domainName,
+        stage: this,
+        apiMappingKey: props.domainMapping.mappingKey,
+      });
+    }
+
   }
 
   /**
@@ -62,4 +115,4 @@ export class HttpStage extends Resource implements IStage {
     const urlPath = this.stageName === DEFAULT_STAGE_NAME ? '' : this.stageName;
     return `https://${this.httpApi.httpApiId}.execute-api.${s.region}.${s.urlSuffix}/${urlPath}`;
   }
-}
\ No newline at end of file
+}
diff --git a/packages/@aws-cdk/aws-apigatewayv2/package.json b/packages/@aws-cdk/aws-apigatewayv2/package.json
index 0920b42061902..bc22bfc9ed2f7 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/package.json
+++ b/packages/@aws-cdk/aws-apigatewayv2/package.json
@@ -71,6 +71,7 @@
     "pkglint": "0.0.0"
   },
   "dependencies": {
+    "@aws-cdk/aws-certificatemanager": "0.0.0",
     "@aws-cdk/aws-iam": "0.0.0",
     "@aws-cdk/aws-lambda": "0.0.0",
     "@aws-cdk/core": "0.0.0",
@@ -79,6 +80,7 @@
   "peerDependencies": {
     "@aws-cdk/aws-iam": "0.0.0",
     "@aws-cdk/aws-lambda": "0.0.0",
+    "@aws-cdk/aws-certificatemanager": "0.0.0",
     "@aws-cdk/core": "0.0.0",
     "constructs": "^3.0.2"
   },
@@ -89,7 +91,9 @@
     "exclude": [
       "from-method:@aws-cdk/aws-apigatewayv2.HttpIntegration",
       "from-method:@aws-cdk/aws-apigatewayv2.HttpRoute",
+      "from-method:@aws-cdk/aws-apigatewayv2.HttpStage",
       "props-physical-name-type:@aws-cdk/aws-apigatewayv2.HttpStageProps.stageName",
+      "props-physical-name:@aws-cdk/aws-apigatewayv2.HttpApiMappingProps",
       "props-physical-name:@aws-cdk/aws-apigatewayv2.HttpIntegrationProps",
       "props-physical-name:@aws-cdk/aws-apigatewayv2.HttpRouteProps"
     ]
diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api-mapping.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api-mapping.test.ts
new file mode 100644
index 0000000000000..d2a536ae52862
--- /dev/null
+++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api-mapping.test.ts
@@ -0,0 +1,89 @@
+import '@aws-cdk/assert/jest';
+import { Certificate } from '@aws-cdk/aws-certificatemanager';
+import { Stack } from '@aws-cdk/core';
+import { DomainName, HttpApi, HttpApiMapping } from '../../lib';
+
+const domainName = 'example.com';
+const certArn = 'arn:aws:acm:us-east-1:111111111111:certificate';
+
+describe('ApiMapping', () => {
+  test('default stage', () => {
+
+    const stack = new Stack();
+    const api = new HttpApi(stack, 'Api');
+
+    const dn = new DomainName(stack, 'DomainName', {
+      domainName,
+      certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
+    });
+
+    new HttpApiMapping(stack, 'Mapping', {
+      api,
+      domainName: dn,
+    });
+
+    expect(stack).toHaveResource('AWS::ApiGatewayV2::ApiMapping', {
+      ApiId: {
+        Ref: 'ApiF70053CD',
+      },
+      DomainName: 'example.com',
+      Stage: '$default',
+    });
+  });
+
+  test('beta stage mapping', () => {
+
+    const stack = new Stack();
+    const api = new HttpApi(stack, 'Api', {
+      createDefaultStage: false,
+    });
+
+    const beta = api.addStage('beta', {
+      stageName: 'beta',
+    });
+
+    const dn = new DomainName(stack, 'DomainName', {
+      domainName,
+      certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
+    });
+
+    new HttpApiMapping(stack, 'Mapping', {
+      api,
+      domainName: dn,
+      stage: beta,
+      apiMappingKey: 'beta',
+    });
+
+    expect(stack).toHaveResource('AWS::ApiGatewayV2::ApiMapping', {
+      ApiId: {
+        Ref: 'ApiF70053CD',
+      },
+      DomainName: 'example.com',
+      Stage: 'beta',
+      ApiMappingKey: 'beta',
+    });
+  });
+
+  test('import mapping', () => {
+
+    const stack = new Stack();
+    const api = new HttpApi(stack, 'Api');
+
+    const dn = new DomainName(stack, 'DomainName', {
+      domainName,
+      certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
+    });
+
+    const mapping = new HttpApiMapping(stack, 'Mapping', {
+      api,
+      domainName: dn,
+      apiMappingKey: '/',
+    });
+
+    const imported = HttpApiMapping.fromHttpApiMappingAttributes(stack, 'ImportedMapping', {
+      apiMappingId: mapping.apiMappingId,
+    } );
+
+    expect(imported.apiMappingId).toEqual(mapping.apiMappingId);
+  });
+});
diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts
index 3f89ff312b14f..5e88dbfe3cbf5 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts
+++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts
@@ -25,6 +25,15 @@ describe('HttpApi', () => {
     expect(api.url).toBeDefined();
   });
 
+  test('import', () => {
+    const stack = new Stack();
+    const api = new HttpApi(stack, 'api', { apiName: 'customName' });
+    const imported = HttpApi.fromApiId(stack, 'imported', api.httpApiId );
+
+    expect(imported.httpApiId).toEqual(api.httpApiId);
+
+  });
+
   test('unsetting createDefaultStage', () => {
     const stack = new Stack();
     const api = new HttpApi(stack, 'api', {
@@ -104,4 +113,4 @@ describe('HttpApi', () => {
       RouteKey: 'ANY /pets',
     });
   });
-});
\ No newline at end of file
+});
diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts
new file mode 100644
index 0000000000000..12d3327300df3
--- /dev/null
+++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts
@@ -0,0 +1,154 @@
+import '@aws-cdk/assert/jest';
+// import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert';
+import {  Certificate } from '@aws-cdk/aws-certificatemanager';
+import { Stack } from '@aws-cdk/core';
+import { DomainName, HttpApi } from '../../lib';
+
+const domainName = 'example.com';
+const certArn = 'arn:aws:acm:us-east-1:111111111111:certificate';
+
+describe('DomainName', () => {
+  test('create domain name correctly', () => {
+    // GIVEN
+    const stack = new Stack();
+
+    // WHEN
+    new DomainName(stack, 'DomainName', {
+      domainName,
+      certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
+    });
+
+    // THEN
+    expect(stack).toHaveResource('AWS::ApiGatewayV2::DomainName', {
+      DomainName: 'example.com',
+      DomainNameConfigurations: [
+        {
+          CertificateArn: 'arn:aws:acm:us-east-1:111111111111:certificate',
+          EndpointType: 'REGIONAL',
+        },
+      ],
+    });
+  });
+
+  test('import domain name correctly', () => {
+    // GIVEN
+    const stack = new Stack();
+
+    const dn = new DomainName(stack, 'DomainName', {
+      domainName,
+      certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
+    });
+
+    // WHEN
+    const imported = DomainName.fromDomainNameAttributes(stack, 'dn', {
+      domainName: dn.domainName,
+      regionalDomainName: dn.regionalDomainName,
+      regionalHostedZoneId: dn.regionalHostedZoneId,
+    });
+
+    // THEN;
+    expect(imported.domainName).toEqual(dn.domainName);
+    expect(imported.regionalDomainName).toEqual(dn.regionalDomainName);
+    expect(imported.regionalHostedZoneId).toEqual(dn.regionalHostedZoneId);
+  });
+
+  test('addStage with domainNameMapping', () => {
+    // GIVEN
+    const stack = new Stack();
+    const api = new HttpApi(stack, 'Api', {
+      createDefaultStage: true,
+    });
+
+    // WHEN
+    const dn = new DomainName(stack, 'DN', {
+      domainName,
+      certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
+    });
+
+    api.addStage('beta', {
+      stageName: 'beta',
+      autoDeploy: true,
+      domainMapping: {
+        domainName: dn,
+        mappingKey: 'beta',
+      },
+    });
+
+    expect(stack).toHaveResourceLike('AWS::ApiGatewayV2::DomainName', {
+      DomainName: 'example.com',
+      DomainNameConfigurations: [
+        {
+          CertificateArn: 'arn:aws:acm:us-east-1:111111111111:certificate',
+          EndpointType: 'REGIONAL',
+        },
+      ],
+    });
+    expect(stack).toHaveResourceLike('AWS::ApiGatewayV2::ApiMapping', {
+      ApiId: {
+        Ref: 'ApiF70053CD',
+      },
+      DomainName: 'example.com',
+      Stage: 'beta',
+      ApiMappingKey: 'beta',
+    });
+  });
+
+  test('api with defaultDomainMapping', () => {
+    // GIVEN
+    const stack = new Stack();
+    const dn = new DomainName(stack, 'DN', {
+      domainName,
+      certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
+    });
+
+    // WHEN
+    new HttpApi(stack, 'Api', {
+      createDefaultStage: true,
+      defaultDomainMapping: {
+        domainName: dn,
+        mappingKey: '/',
+      },
+    });
+
+    // THEN
+    expect(stack).toHaveResourceLike('AWS::ApiGatewayV2::DomainName', {
+      DomainName: 'example.com',
+      DomainNameConfigurations: [
+        {
+          CertificateArn: 'arn:aws:acm:us-east-1:111111111111:certificate',
+          EndpointType: 'REGIONAL',
+        },
+      ],
+    });
+
+    expect(stack).toHaveResourceLike('AWS::ApiGatewayV2::ApiMapping', {
+      ApiId: {
+        Ref: 'ApiF70053CD',
+      },
+      DomainName: 'example.com',
+      Stage: '$default',
+    });
+  });
+
+  test('throws when defaultDomainMapping enabled with createDefaultStage disabled', () => {
+    // GIVEN
+    const stack = new Stack();
+    const dn = new DomainName(stack, 'DN', {
+      domainName,
+      certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
+    });
+    const t = () => {
+      new HttpApi(stack, 'Api', {
+        createDefaultStage: false,
+        defaultDomainMapping: {
+          domainName: dn,
+          mappingKey: '/',
+        },
+      });
+    };
+
+    // WHEN/THEN
+    expect(t).toThrow('defaultDomainMapping not supported with createDefaultStage disabled');
+
+  });
+});
diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.custom-domain.expected.json b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.custom-domain.expected.json
new file mode 100644
index 0000000000000..2fb796e79265c
--- /dev/null
+++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.custom-domain.expected.json
@@ -0,0 +1,348 @@
+{
+  "Resources": {
+    "echohandlerServiceRole833A8F7A": {
+      "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"
+            ]
+          ]
+        }]
+      }
+    },
+    "echohandler8F648AB2": {
+      "Type": "AWS::Lambda::Function",
+      "Properties": {
+        "Code": {
+          "ZipFile": "exports.handler = async function(event, context) { return { statusCode: 200, headers: { \"content-type\": \"application/json\"  }, body: JSON.stringify(event) }; };"
+        },
+        "Handler": "index.handler",
+        "Role": {
+          "Fn::GetAtt": [
+            "echohandlerServiceRole833A8F7A",
+            "Arn"
+          ]
+        },
+        "Runtime": "nodejs12.x"
+      },
+      "DependsOn": [
+        "echohandlerServiceRole833A8F7A"
+      ]
+    },
+    "echohandlerinteghttpproxyHttpProxyProdApiDefaultRoute20082F68PermissionBE86C6B3": {
+      "Type": "AWS::Lambda::Permission",
+      "Properties": {
+        "Action": "lambda:InvokeFunction",
+        "FunctionName": {
+          "Fn::GetAtt": [
+            "echohandler8F648AB2",
+            "Arn"
+          ]
+        },
+        "Principal": "apigateway.amazonaws.com",
+        "SourceArn": {
+          "Fn::Join": [
+            "",
+            [
+              "arn:",
+              {
+                "Ref": "AWS::Partition"
+              },
+              ":execute-api:",
+              {
+                "Ref": "AWS::Region"
+              },
+              ":",
+              {
+                "Ref": "AWS::AccountId"
+              },
+              ":",
+              {
+                "Ref": "HttpProxyProdApi368B6161"
+              },
+              "/*/*"
+            ]
+          ]
+        }
+      }
+    },
+    "echohandlerinteghttpproxyHttpProxyBetaApiDefaultRouteC328B302Permission40FB964B": {
+      "Type": "AWS::Lambda::Permission",
+      "Properties": {
+        "Action": "lambda:InvokeFunction",
+        "FunctionName": {
+          "Fn::GetAtt": [
+            "echohandler8F648AB2",
+            "Arn"
+          ]
+        },
+        "Principal": "apigateway.amazonaws.com",
+        "SourceArn": {
+          "Fn::Join": [
+            "",
+            [
+              "arn:",
+              {
+                "Ref": "AWS::Partition"
+              },
+              ":execute-api:",
+              {
+                "Ref": "AWS::Region"
+              },
+              ":",
+              {
+                "Ref": "AWS::AccountId"
+              },
+              ":",
+              {
+                "Ref": "HttpProxyBetaApiBFB4DA5E"
+              },
+              "/*/*"
+            ]
+          ]
+        }
+      }
+    },
+    "DNFDC76583": {
+      "Type": "AWS::ApiGatewayV2::DomainName",
+      "Properties": {
+        "DomainName": "apigv2.demo.com",
+        "DomainNameConfigurations": [{
+          "CertificateArn": "arn:aws:acm:us-east-1:111111111111:certificate",
+          "EndpointType": "REGIONAL"
+        }]
+      }
+    },
+    "HttpProxyProdApi368B6161": {
+      "Type": "AWS::ApiGatewayV2::Api",
+      "Properties": {
+        "Name": "HttpProxyProdApi",
+        "ProtocolType": "HTTP"
+      }
+    },
+    "HttpProxyProdApiDefaultRouteDefaultRouteIntegration702F0DF7": {
+      "Type": "AWS::ApiGatewayV2::Integration",
+      "Properties": {
+        "ApiId": {
+          "Ref": "HttpProxyProdApi368B6161"
+        },
+        "IntegrationType": "AWS_PROXY",
+        "IntegrationUri": {
+          "Fn::GetAtt": [
+            "echohandler8F648AB2",
+            "Arn"
+          ]
+        },
+        "PayloadFormatVersion": "2.0"
+      }
+    },
+    "HttpProxyProdApiDefaultRoute40EFC108": {
+      "Type": "AWS::ApiGatewayV2::Route",
+      "Properties": {
+        "ApiId": {
+          "Ref": "HttpProxyProdApi368B6161"
+        },
+        "RouteKey": "$default",
+        "Target": {
+          "Fn::Join": [
+            "",
+            [
+              "integrations/",
+              {
+                "Ref": "HttpProxyProdApiDefaultRouteDefaultRouteIntegration702F0DF7"
+              }
+            ]
+          ]
+        }
+      }
+    },
+    "HttpProxyProdApiDefaultStage0038B180": {
+      "Type": "AWS::ApiGatewayV2::Stage",
+      "Properties": {
+        "ApiId": {
+          "Ref": "HttpProxyProdApi368B6161"
+        },
+        "StageName": "$default",
+        "AutoDeploy": true
+      }
+    },
+    "HttpProxyProdApiDefaultStageinteghttpproxyDN4CD83A2F": {
+      "Type": "AWS::ApiGatewayV2::ApiMapping",
+      "Properties": {
+        "ApiId": {
+          "Ref": "HttpProxyProdApi368B6161"
+        },
+        "ApiMappingKey": "/",
+        "DomainName": "apigv2.demo.com",
+        "Stage": "$default"
+      }
+    },
+    "HttpProxyProdApitesting225373A0": {
+      "Type": "AWS::ApiGatewayV2::Stage",
+      "Properties": {
+        "ApiId": {
+          "Ref": "HttpProxyProdApi368B6161"
+        },
+        "StageName": "testing",
+        "AutoDeploy": true
+      }
+    },
+    "HttpProxyProdApitestinginteghttpproxyDNtestingBEBAEF7B": {
+      "Type": "AWS::ApiGatewayV2::ApiMapping",
+      "Properties": {
+        "ApiId": {
+          "Ref": "HttpProxyProdApi368B6161"
+        },
+        "DomainName": "apigv2.demo.com",
+        "Stage": "testing",
+        "ApiMappingKey": "testing"
+      }
+    },
+    "HttpProxyBetaApiBFB4DA5E": {
+      "Type": "AWS::ApiGatewayV2::Api",
+      "Properties": {
+        "Name": "HttpProxyBetaApi",
+        "ProtocolType": "HTTP"
+      }
+    },
+    "HttpProxyBetaApiDefaultRouteDefaultRouteIntegration24A25241": {
+      "Type": "AWS::ApiGatewayV2::Integration",
+      "Properties": {
+        "ApiId": {
+          "Ref": "HttpProxyBetaApiBFB4DA5E"
+        },
+        "IntegrationType": "AWS_PROXY",
+        "IntegrationUri": {
+          "Fn::GetAtt": [
+            "echohandler8F648AB2",
+            "Arn"
+          ]
+        },
+        "PayloadFormatVersion": "2.0"
+      }
+    },
+    "HttpProxyBetaApiDefaultRoute12DC547F": {
+      "Type": "AWS::ApiGatewayV2::Route",
+      "Properties": {
+        "ApiId": {
+          "Ref": "HttpProxyBetaApiBFB4DA5E"
+        },
+        "RouteKey": "$default",
+        "Target": {
+          "Fn::Join": [
+            "",
+            [
+              "integrations/",
+              {
+                "Ref": "HttpProxyBetaApiDefaultRouteDefaultRouteIntegration24A25241"
+              }
+            ]
+          ]
+        }
+      }
+    },
+    "HttpProxyBetaApiDefaultStage4890F8A1": {
+      "Type": "AWS::ApiGatewayV2::Stage",
+      "Properties": {
+        "ApiId": {
+          "Ref": "HttpProxyBetaApiBFB4DA5E"
+        },
+        "StageName": "$default",
+        "AutoDeploy": true
+      }
+    },
+    "HttpProxyBetaApiDefaultStageinteghttpproxyDNbeta0904192E": {
+      "Type": "AWS::ApiGatewayV2::ApiMapping",
+      "Properties": {
+        "ApiId": {
+          "Ref": "HttpProxyBetaApiBFB4DA5E"
+        },
+        "ApiMappingKey": "beta",
+        "DomainName": "apigv2.demo.com",
+        "Stage": "$default"
+      }
+    }
+  },
+  "Outputs": {
+    "RegionalDomainName": {
+      "Value": {
+        "Fn::GetAtt": [
+          "DNFDC76583",
+          "RegionalDomainName"
+        ]
+      }
+    },
+    "DomainName": {
+      "Value": "apigv2.demo.com"
+    },
+    "CustomUDomainURL": {
+      "Value": "https://apigv2.demo.com"
+    },
+    "ProdApiEndpoint": {
+      "Value": {
+        "Fn::Join": [
+          "",
+          [
+            "https://",
+            {
+              "Ref": "HttpProxyProdApi368B6161"
+            },
+            ".execute-api.",
+            {
+              "Ref": "AWS::Region"
+            },
+            ".",
+            {
+              "Ref": "AWS::URLSuffix"
+            },
+            "/"
+          ]
+        ]
+      }
+    },
+    "BetaApiEndpoint": {
+      "Value": {
+        "Fn::Join": [
+          "",
+          [
+            "https://",
+            {
+              "Ref": "HttpProxyBetaApiBFB4DA5E"
+            },
+            ".execute-api.",
+            {
+              "Ref": "AWS::Region"
+            },
+            ".",
+            {
+              "Ref": "AWS::URLSuffix"
+            },
+            "/"
+          ]
+        ]
+      }
+    },
+    "Region": {
+      "Value": {
+        "Ref": "AWS::Region"
+      }
+    }
+  }
+}
diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.custom-domain.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.custom-domain.ts
new file mode 100644
index 0000000000000..4064a256d961f
--- /dev/null
+++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.custom-domain.ts
@@ -0,0 +1,57 @@
+import * as acm from '@aws-cdk/aws-certificatemanager';
+import * as lambda from '@aws-cdk/aws-lambda';
+import { App, CfnOutput, Stack } from '@aws-cdk/core';
+import { DomainName, HttpApi, LambdaProxyIntegration } from '../../lib';
+
+const app = new App();
+
+const stack = new Stack(app, 'integ-http-proxy');
+
+const handler = new lambda.Function(stack, 'echohandler', {
+  runtime: lambda.Runtime.NODEJS_12_X,
+  handler: 'index.handler',
+  code: new lambda.InlineCode('exports.handler = async function(event, context) { return { statusCode: 200, headers: { "content-type": "application/json"  }, body: JSON.stringify(event) }; };'),
+});
+
+const certArn = 'arn:aws:acm:us-east-1:111111111111:certificate';
+const domainName = 'apigv2.demo.com';
+
+const dn = new DomainName(stack, 'DN', {
+  domainName,
+  certificate: acm.Certificate.fromCertificateArn(stack, 'cert', certArn),
+});
+
+const prodApi = new HttpApi(stack, 'HttpProxyProdApi', {
+  defaultIntegration: new LambdaProxyIntegration({ handler }),
+  // https://${dn.domainName} goes to prodApi $default stage
+  defaultDomainMapping: {
+    domainName: dn,
+    mappingKey: '/',
+  },
+});
+
+const betaApi = new HttpApi(stack, 'HttpProxyBetaApi', {
+  defaultIntegration: new LambdaProxyIntegration({ handler }),
+  // https://${dn.domainName}/beta goes to betaApi $default stage
+  defaultDomainMapping: {
+    domainName: dn,
+    mappingKey: 'beta',
+  },
+});
+
+prodApi.addStage('testing', {
+  stageName: 'testing',
+  autoDeploy: true,
+  // https://${dn.domainName}/testing goes to prodApi testing stage
+  domainMapping: {
+    domainName: dn,
+    mappingKey: 'testing',
+  },
+} );
+
+new CfnOutput(stack, 'RegionalDomainName', { value: dn.regionalDomainName });
+new CfnOutput(stack, 'DomainName', { value: dn.domainName });
+new CfnOutput(stack, 'CustomUDomainURL', { value: `https://${dn.domainName}` });
+new CfnOutput(stack, 'ProdApiEndpoint', { value: prodApi.url! });
+new CfnOutput(stack, 'BetaApiEndpoint', { value: betaApi.url! });
+new CfnOutput(stack, 'Region', { value: Stack.of(stack).region});
diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integrations/http.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/integrations/http.test.ts
index cc5ed64df20eb..8f187d0ab9c78 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/test/http/integrations/http.test.ts
+++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integrations/http.test.ts
@@ -1,7 +1,7 @@
 import { ABSENT } from '@aws-cdk/assert';
 import '@aws-cdk/assert/jest';
 import { Duration, Stack } from '@aws-cdk/core';
-import { HttpApi, HttpMethod, HttpProxyIntegration, HttpRoute, HttpRouteKey } from '../../../lib';
+import { HttpApi, HttpIntegration, HttpIntegrationType, HttpMethod, HttpProxyIntegration, HttpRoute, HttpRouteKey, PayloadFormatVersion } from '../../../lib';
 
 describe('HttpProxyIntegration', () => {
   test('default', () => {
@@ -40,6 +40,40 @@ describe('HttpProxyIntegration', () => {
     });
   });
 
+  test('custom payload format version is allowed', () => {
+    const stack = new Stack();
+    const api = new HttpApi(stack, 'HttpApi');
+    new HttpIntegration(stack, 'HttpInteg', {
+      payloadFormatVersion: PayloadFormatVersion.custom('99.99'),
+      httpApi: api,
+      integrationType: HttpIntegrationType.HTTP_PROXY,
+      integrationUri: 'some-target-url',
+    });
+
+    expect(stack).toHaveResource('AWS::ApiGatewayV2::Integration', {
+      IntegrationType: 'HTTP_PROXY',
+      IntegrationUri: 'some-target-url',
+      PayloadFormatVersion: '99.99',
+    });
+  });
+
+  test('HttpIntegration without payloadFormatVersion is allowed', () => {
+    const stack = new Stack();
+    const api = new HttpApi(stack, 'HttpApi');
+    new HttpIntegration(stack, 'HttpInteg', {
+      httpApi: api,
+      integrationType: HttpIntegrationType.HTTP_PROXY,
+      integrationUri: 'some-target-url',
+    });
+
+    expect(stack).toHaveResource('AWS::ApiGatewayV2::Integration', {
+      IntegrationType: 'HTTP_PROXY',
+      IntegrationUri: 'some-target-url',
+    });
+  });
+});
+
+describe('CORS', () => {
   test('CORS Configuration is correctly configured.', () => {
     const stack = new Stack();
     new HttpApi(stack, 'HttpApi', {
diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts
index 25df12528e63e..f129db5186486 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts
+++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts
@@ -52,6 +52,29 @@ describe('HttpRoute', () => {
       IntegrationUri: 'some-uri',
     });
   });
+
+  test('throws when path not start with /', () => {
+    const stack = new Stack();
+    const httpApi = new HttpApi(stack, 'HttpApi');
+
+    expect(() => new HttpRoute(stack, 'HttpRoute', {
+      httpApi,
+      integration: new DummyIntegration(),
+      routeKey: HttpRouteKey.with('books', HttpMethod.GET),
+    })).toThrowError(/path must always start with a "\/" and not end with a "\/"/);
+  });
+
+  test('throws when path ends with /', () => {
+    const stack = new Stack();
+    const httpApi = new HttpApi(stack, 'HttpApi');
+
+    expect(() => new HttpRoute(stack, 'HttpRoute', {
+      httpApi,
+      integration: new DummyIntegration(),
+      routeKey: HttpRouteKey.with('/books/', HttpMethod.GET),
+    })).toThrowError(/path must always start with a "\/" and not end with a "\/"/);
+  });
+
 });
 
 class DummyIntegration implements IHttpRouteIntegration {
diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/stage.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/stage.test.ts
index 336d3a74852a4..06e7a9efbc4ce 100644
--- a/packages/@aws-cdk/aws-apigatewayv2/test/http/stage.test.ts
+++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/stage.test.ts
@@ -19,6 +19,21 @@ describe('HttpStage', () => {
     });
   });
 
+  test('import', () => {
+    const stack = new Stack();
+    const api = new HttpApi(stack, 'Api', {
+      createDefaultStage: false,
+    });
+
+    const stage = new HttpStage(stack, 'Stage', {
+      httpApi: api,
+    });
+
+    const imported = HttpStage.fromStageName(stack, 'Import', stage.stageName );
+
+    expect(imported.stageName).toEqual(stage.stageName);
+  });
+
   test('url returns the correct path', () => {
     const stack = new Stack();
     const api = new HttpApi(stack, 'Api', {

From 4ba20fd2f1579034483683995fac1e18e97a1b12 Mon Sep 17 00:00:00 2001
From: Jonathan Goldwasser <jogold@users.noreply.github.com>
Date: Mon, 6 Jul 2020 15:05:22 +0200
Subject: [PATCH 02/22] feat(lambda-nodejs): allow jsx and tsx entry files
 (#8892)

Support JSX adn TSX for entry files

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-lambda-nodejs/README.md      |  2 +-
 .../@aws-cdk/aws-lambda-nodejs/lib/function.ts     |  2 +-
 .../aws-lambda-nodejs/test/function.test.ts        | 14 ++++++++++++++
 3 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/packages/@aws-cdk/aws-lambda-nodejs/README.md b/packages/@aws-cdk/aws-lambda-nodejs/README.md
index b4e4579991f9e..20b79f7878cdc 100644
--- a/packages/@aws-cdk/aws-lambda-nodejs/README.md
+++ b/packages/@aws-cdk/aws-lambda-nodejs/README.md
@@ -34,7 +34,7 @@ automatically transpiled and bundled whether it's written in JavaScript or TypeS
 Alternatively, an entry file and handler can be specified:
 ```ts
 new lambda.NodejsFunction(this, 'MyFunction', {
-  entry: '/path/to/my/file.ts',
+  entry: '/path/to/my/file.ts', // accepts .js, .jsx, .ts and .tsx files
   handler: 'myExportedFunc'
 });
 ```
diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts
index 1138807443fec..3f623aa08e94f 100644
--- a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts
+++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts
@@ -84,7 +84,7 @@ export class NodejsFunction extends lambda.Function {
  */
 function findEntry(id: string, entry?: string): string {
   if (entry) {
-    if (!/\.(js|ts)$/.test(entry)) {
+    if (!/\.(jsx?|tsx?)$/.test(entry)) {
       throw new Error('Only JavaScript or TypeScript entry files are supported.');
     }
     if (!fs.existsSync(entry)) {
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 200200fdeb1ce..fca00ea0f9562 100644
--- a/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts
+++ b/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts
@@ -1,6 +1,8 @@
 import '@aws-cdk/assert/jest';
 import { Runtime } from '@aws-cdk/aws-lambda';
 import { Stack } from '@aws-cdk/core';
+import * as fs from 'fs';
+import * as path from 'path';
 import { NodejsFunction } from '../lib';
 import { Bundling } from '../lib/bundling';
 
@@ -67,6 +69,18 @@ test('throws when entry is not js/ts', () => {
   })).toThrow(/Only JavaScript or TypeScript entry files are supported/);
 });
 
+test('accepts tsx', () => {
+  const entry = path.join(__dirname, 'handler.tsx');
+
+  fs.symlinkSync(path.join(__dirname, 'function.test.handler1.ts'), entry);
+
+  expect(() => new NodejsFunction(stack, 'Fn', {
+    entry,
+  })).not.toThrow();
+
+  fs.unlinkSync(entry);
+});
+
 test('throws when entry does not exist', () => {
   expect(() => new NodejsFunction(stack, 'Fn', {
     entry: 'notfound.ts',

From 021533caa8f4e515299d1f0cdaadd9f625d6f64d Mon Sep 17 00:00:00 2001
From: Shiv Lakshminarayan <shivlaks@amazon.com>
Date: Mon, 6 Jul 2020 07:42:14 -0700
Subject: [PATCH 03/22] feat(stepfunctions-tasks): task for invoking a Step
 Functions activity worker (#8840)

Replacement class for `InvokeActivity` which currently uses the embedded task.

The notable changes are:
* This change merges task and service integration level properties by extending
`TaskStateBase`, similar to all the other task states.
* `activity` is now a property in the new class and not specified in the constructor

I've left the current tests intact for fidelity and updated the `README`

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 .../aws-stepfunctions-tasks/README.md         | 31 +++++++
 .../aws-stepfunctions-tasks/lib/index.ts      |  1 +
 .../lib/invoke-activity.ts                    |  2 +
 .../lib/stepfunctions/invoke-activity.ts      | 44 ++++++++++
 .../integ.invoke-activity.expected.json       | 85 +++++++++++++++++++
 .../stepfunctions/integ.invoke-activity.ts    | 69 +++++++++++++++
 .../test/stepfunctions/invoke-activity.ts     | 64 ++++++++++++++
 7 files changed, 296 insertions(+)
 create mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts
 create mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.expected.json
 create mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.ts
 create mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.ts

diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md
index d5142e2159d2b..eb391390b2056 100644
--- a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md
+++ b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md
@@ -54,6 +54,8 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aw
   - [Create Transform Job](#create-transform-job)
 - [SNS](#sns)
 - [Step Functions](#step-functions)
+  - [Start Execution](#start-execution)
+  - [Invoke Activity Worker](#invoke-activity)
 - [SQS](#sqs)
 
 ## Task
@@ -750,6 +752,8 @@ const task2 = new tasks.SnsPublish(this, 'Publish2', {
 
 ## Step Functions
 
+### Start Execution
+
 You can manage [AWS Step Functions](https://docs.aws.amazon.com/step-functions/latest/dg/connect-stepfunctions.html) executions.
 
 AWS Step Functions supports it's own [`StartExecution`](https://docs.aws.amazon.com/step-functions/latest/apireference/API_StartExecution.html) API as a service integration.
@@ -777,6 +781,33 @@ new sfn.StateMachine(stack, 'ParentStateMachine', {
 });
 ```
 
+### Invoke Activity
+
+You can invoke a [Step Functions Activity](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-activities.html) which enables you to have
+a task in your state machine where the work is performed by a *worker* that can
+be hosted on Amazon EC2, Amazon ECS, AWS Lambda, basically anywhere. Activities
+are a way to associate code running somewhere (known as an activity worker) with
+a specific task in a state machine.
+
+When Step Functions reaches an activity task state, the workflow waits for an
+activity worker to poll for a task. An activity worker polls Step Functions by
+using GetActivityTask, and sending the ARN for the related activity.  
+
+After the activity worker completes its work, it can provide a report of its
+success or failure by using `SendTaskSuccess` or `SendTaskFailure`. These two
+calls use the taskToken provided by GetActivityTask to associate the result
+with that task.
+
+The following example creates an activity and creates a task that invokes the activity.
+
+```ts
+const submitJobActivity = new sfn.Activity(this, 'SubmitJob');
+
+new tasks.StepFunctionsInvokeActivity(this, 'Submit Job', {
+  activity: submitJobActivity,
+});
+```
+
 ## SQS
 
 Step Functions supports [Amazon SQS](https://docs.aws.amazon.com/step-functions/latest/dg/connect-sqs.html)
diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/index.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/index.ts
index 68d8c30b93997..ee034e389ad96 100644
--- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/index.ts
+++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/index.ts
@@ -16,6 +16,7 @@ export * from './sagemaker/create-training-job';
 export * from './sagemaker/create-transform-job';
 export * from './start-execution';
 export * from './stepfunctions/start-execution';
+export * from './stepfunctions/invoke-activity';
 export * from './evaluate-expression';
 export * from './emr/emr-create-cluster';
 export * from './emr/emr-set-cluster-termination-protection';
diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/invoke-activity.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/invoke-activity.ts
index e9d6342fefcc4..a0c78b5cf073c 100644
--- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/invoke-activity.ts
+++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/invoke-activity.ts
@@ -19,6 +19,8 @@ export interface InvokeActivityProps {
  * A Step Functions Task to invoke an Activity worker.
  *
  * An Activity can be used directly as a Resource.
+ *
+ * @deprecated - use `StepFunctionsInvokeActivity`
  */
 export class InvokeActivity implements sfn.IStepFunctionsTask {
   constructor(private readonly activity: sfn.IActivity, private readonly props: InvokeActivityProps = {}) {
diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts
new file mode 100644
index 0000000000000..829a35eb03658
--- /dev/null
+++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts
@@ -0,0 +1,44 @@
+import * as iam from '@aws-cdk/aws-iam';
+import * as sfn from '@aws-cdk/aws-stepfunctions';
+import * as cdk from '@aws-cdk/core';
+
+/**
+ * Properties for invoking an Activity worker
+ */
+export interface StepFunctionsInvokeActivityProps extends sfn.TaskStateBaseProps {
+
+  /**
+   * Step Functions Activity to invoke
+   */
+  readonly activity: sfn.IActivity
+}
+
+/**
+ * A Step Functions Task to invoke an Activity worker.
+ *
+ * An Activity can be used directly as a Resource.
+ */
+export class StepFunctionsInvokeActivity extends sfn.TaskStateBase {
+  protected readonly taskMetrics?: sfn.TaskMetricsConfig;
+  // No IAM permissions necessary, execution role implicitly has Activity permissions.
+  protected readonly taskPolicies?: iam.PolicyStatement[];
+
+  constructor(scope: cdk.Construct, id: string, private readonly props: StepFunctionsInvokeActivityProps) {
+    super(scope, id, props);
+
+    this.taskMetrics = {
+      metricDimensions: { ActivityArn: this.props.activity.activityArn },
+      metricPrefixSingular: 'Activity',
+      metricPrefixPlural: 'Activities',
+    };
+  }
+
+  /**
+   * @internal
+   */
+  protected _renderTask(): any {
+    return {
+      Resource: this.props.activity.activityArn,
+    };
+  }
+}
diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.expected.json
new file mode 100644
index 0000000000000..e58efc93007de
--- /dev/null
+++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.expected.json
@@ -0,0 +1,85 @@
+{
+  "Resources": {
+    "SubmitJobFB773A16": {
+      "Type": "AWS::StepFunctions::Activity",
+      "Properties": {
+        "Name": "awsstepfunctionsintegSubmitJobA2508960"
+      }
+    },
+    "CheckJob5FFC1D6F": {
+      "Type": "AWS::StepFunctions::Activity",
+      "Properties": {
+        "Name": "awsstepfunctionsintegCheckJobC4AC762D"
+      }
+    },
+    "StateMachineRoleB840431D": {
+      "Type": "AWS::IAM::Role",
+      "Properties": {
+        "AssumeRolePolicyDocument": {
+          "Statement": [
+            {
+              "Action": "sts:AssumeRole",
+              "Effect": "Allow",
+              "Principal": {
+                "Service": {
+                  "Fn::Join": [
+                    "",
+                    [
+                      "states.",
+                      {
+                        "Ref": "AWS::Region"
+                      },
+                      ".amazonaws.com"
+                    ]
+                  ]
+                }
+              }
+            }
+          ],
+          "Version": "2012-10-17"
+        }
+      }
+    },
+    "StateMachine2E01A3A5": {
+      "Type": "AWS::StepFunctions::StateMachine",
+      "Properties": {
+        "RoleArn": {
+          "Fn::GetAtt": [
+            "StateMachineRoleB840431D",
+            "Arn"
+          ]
+        },
+        "DefinitionString": {
+          "Fn::Join": [
+            "",
+            [
+              "{\"StartAt\":\"Submit Job\",\"States\":{\"Submit Job\":{\"Next\":\"Wait X Seconds\",\"Type\":\"Task\",\"ResultPath\":\"$.guid\",\"Resource\":\"",
+              {
+                "Ref": "SubmitJobFB773A16"
+              },
+              "\"},\"Wait X Seconds\":{\"Type\":\"Wait\",\"SecondsPath\":\"$.wait_time\",\"Next\":\"Get Job Status\"},\"Get Job Status\":{\"Next\":\"Job Complete?\",\"Type\":\"Task\",\"InputPath\":\"$.guid\",\"ResultPath\":\"$.status\",\"Resource\":\"",
+              {
+                "Ref": "CheckJob5FFC1D6F"
+              },
+              "\"},\"Job Complete?\":{\"Type\":\"Choice\",\"Choices\":[{\"Variable\":\"$.status\",\"StringEquals\":\"FAILED\",\"Next\":\"Job Failed\"},{\"Variable\":\"$.status\",\"StringEquals\":\"SUCCEEDED\",\"Next\":\"Get Final Job Status\"}],\"Default\":\"Wait X Seconds\"},\"Job Failed\":{\"Type\":\"Fail\",\"Error\":\"DescribeJob returned FAILED\",\"Cause\":\"AWS Batch Job Failed\"},\"Get Final Job Status\":{\"End\":true,\"Type\":\"Task\",\"InputPath\":\"$.guid\",\"Resource\":\"",
+              {
+                "Ref": "CheckJob5FFC1D6F"
+              },
+              "\"}},\"TimeoutSeconds\":300}"
+            ]
+          ]
+        }
+      },
+      "DependsOn": [
+        "StateMachineRoleB840431D"
+      ]
+    }
+  },
+  "Outputs": {
+    "stateMachineArn": {
+      "Value": {
+        "Ref": "StateMachine2E01A3A5"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.ts
new file mode 100644
index 0000000000000..918c31d809aaf
--- /dev/null
+++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.ts
@@ -0,0 +1,69 @@
+import * as sfn from '@aws-cdk/aws-stepfunctions';
+import * as cdk from '@aws-cdk/core';
+import * as tasks from '../../lib';
+
+/*
+ * Creates a state machine with a job poller sample project
+ * https://docs.aws.amazon.com/step-functions/latest/dg/sample-project-job-poller.html
+ *
+ * Stack verification steps:
+ * The generated State Machine can be executed from the CLI (or Step Functions console)
+ * and runs with an execution status of `Running`.
+ *
+ * An external process can call the state machine to send a heartbeat or response before it times out.
+ *
+ * -- aws stepfunctions start-execution --state-machine-arn <state-machine-arn-from-output> provides execution arn
+ * -- aws stepfunctions describe-execution --execution-arn <state-machine-arn-from-output> returns a status of `Running`
+ *
+ * CHANGEME: extend this test to create the external resources to report heartbeats
+ */
+class InvokeActivityStack extends cdk.Stack {
+  constructor(scope: cdk.App, id: string, props: cdk.StackProps = {}) {
+    super(scope, id, props);
+
+    const submitJobActivity = new sfn.Activity(this, 'SubmitJob');
+    const checkJobActivity = new sfn.Activity(this, 'CheckJob');
+
+    const submitJob = new tasks.StepFunctionsInvokeActivity(this, 'Submit Job', {
+      activity: submitJobActivity,
+      resultPath: '$.guid',
+    });
+    const waitX = new sfn.Wait(this, 'Wait X Seconds', { time: sfn.WaitTime.secondsPath('$.wait_time') });
+    const getStatus = new tasks.StepFunctionsInvokeActivity(this, 'Get Job Status', {
+      activity: checkJobActivity,
+      inputPath: '$.guid',
+      resultPath: '$.status',
+    });
+    const isComplete = new sfn.Choice(this, 'Job Complete?');
+    const jobFailed = new sfn.Fail(this, 'Job Failed', {
+      cause: 'AWS Batch Job Failed',
+      error: 'DescribeJob returned FAILED',
+    });
+    const finalStatus = new tasks.StepFunctionsInvokeActivity(this, 'Get Final Job Status', {
+      activity: checkJobActivity,
+      inputPath: '$.guid',
+    });
+
+    const chain = sfn.Chain
+      .start(submitJob)
+      .next(waitX)
+      .next(getStatus)
+      .next(isComplete
+        .when(sfn.Condition.stringEquals('$.status', 'FAILED'), jobFailed)
+        .when(sfn.Condition.stringEquals('$.status', 'SUCCEEDED'), finalStatus)
+        .otherwise(waitX));
+
+    const sm = new sfn.StateMachine(this, 'StateMachine', {
+      definition: chain,
+      timeout: cdk.Duration.seconds(300),
+    });
+
+    new cdk.CfnOutput(this, 'stateMachineArn', {
+      value: sm.stateMachineArn,
+    });
+  }
+}
+
+const app = new cdk.App();
+new InvokeActivityStack(app, 'aws-stepfunctions-integ');
+app.synth();
diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.ts
new file mode 100644
index 0000000000000..940517e6bdbf7
--- /dev/null
+++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.ts
@@ -0,0 +1,64 @@
+import '@aws-cdk/assert/jest';
+import * as sfn from '@aws-cdk/aws-stepfunctions';
+import { Stack } from '@aws-cdk/core';
+import { StepFunctionsInvokeActivity } from '../../lib/stepfunctions/invoke-activity';
+
+test('Activity can be used in a Task', () => {
+  // GIVEN
+  const stack = new Stack();
+
+  // WHEN
+  const activity = new sfn.Activity(stack, 'Activity');
+  const task = new StepFunctionsInvokeActivity(stack, 'Task', { activity });
+  new sfn.StateMachine(stack, 'SM', {
+    definition: task,
+  });
+
+  // THEN
+  expect(stack).toHaveResource('AWS::StepFunctions::StateMachine', {
+    DefinitionString: {
+      'Fn::Join': ['', [
+        '{"StartAt":"Task","States":{"Task":{"End":true,"Type":"Task","Resource":"',
+        { Ref: 'Activity04690B0A' },
+        '"}}}',
+      ]],
+    },
+  });
+});
+
+test('Activity Task metrics and Activity metrics are the same', () => {
+  // GIVEN
+  const stack = new Stack();
+  const activity = new sfn.Activity(stack, 'Activity');
+  const task = new StepFunctionsInvokeActivity(stack, 'Invoke', {activity });
+
+  // WHEN
+  const activityMetrics = [
+    activity.metricFailed(),
+    activity.metricHeartbeatTimedOut(),
+    activity.metricRunTime(),
+    activity.metricScheduled(),
+    activity.metricScheduleTime(),
+    activity.metricStarted(),
+    activity.metricSucceeded(),
+    activity.metricTime(),
+    activity.metricTimedOut(),
+  ];
+
+  const taskMetrics = [
+    task.metricFailed(),
+    task.metricHeartbeatTimedOut(),
+    task.metricRunTime(),
+    task.metricScheduled(),
+    task.metricScheduleTime(),
+    task.metricStarted(),
+    task.metricSucceeded(),
+    task.metricTime(),
+    task.metricTimedOut(),
+  ];
+
+  // THEN
+  for (let i = 0; i < activityMetrics.length; i++) {
+    expect(activityMetrics[i]).toEqual(taskMetrics[i]);
+  }
+});

From 33263d7bce032307eb7779de85e07c681d245e67 Mon Sep 17 00:00:00 2001
From: Jonathan Goldwasser <jogold@users.noreply.github.com>
Date: Mon, 6 Jul 2020 17:02:54 +0200
Subject: [PATCH 04/22] chore(lambda-nodejs): make entry file path absolute
 (#8874)

Although I haven't seen issues with a relative entry file path, it is safer
to use a absolute one.


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts   |  4 ++--
 .../@aws-cdk/aws-lambda-nodejs/test/function.test.ts  | 11 +++++++++++
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts
index 3f623aa08e94f..1215330212436 100644
--- a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts
+++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts
@@ -47,7 +47,7 @@ export class NodejsFunction extends lambda.Function {
     }
 
     // Entry and defaults
-    const entry = findEntry(id, props.entry);
+    const entry = path.resolve(findEntry(id, props.entry));
     const handler = props.handler ?? 'handler';
     const defaultRunTime = nodeMajorVersion() >= 12
       ? lambda.Runtime.NODEJS_12_X
@@ -63,9 +63,9 @@ export class NodejsFunction extends lambda.Function {
         ...props,
         runtime,
         code: Bundling.parcel({
+          ...props,
           entry,
           runtime,
-          ...props,
         }),
         handler: `index.${handler}`,
       });
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 fca00ea0f9562..e8f559fdfbec5 100644
--- a/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts
+++ b/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts
@@ -96,3 +96,14 @@ test('throws with the wrong runtime family', () => {
     runtime: Runtime.PYTHON_3_8,
   })).toThrow(/Only `NODEJS` runtimes are supported/);
 });
+
+test('resolves entry to an absolute path', () => {
+  // WHEN
+  new NodejsFunction(stack, 'fn', {
+    entry: 'lib/index.ts',
+  });
+
+  expect(Bundling.parcel).toHaveBeenCalledWith(expect.objectContaining({
+    entry: expect.stringMatching(/@aws-cdk\/aws-lambda-nodejs\/lib\/index.ts$/),
+  }));
+});

From c585e1873e437341ac1b90afbe85a9cb9e6dc2d6 Mon Sep 17 00:00:00 2001
From: Jonathan Goldwasser <jogold@users.noreply.github.com>
Date: Mon, 6 Jul 2020 17:23:05 +0200
Subject: [PATCH 05/22] fix(lambda-nodejs): maximum call stack size exceeded
 with relative entry file path (#8907)

Use absolute paths in `findUp()` to avoid this.

Fixes #8902


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts    | 14 +++++++++-----
 .../@aws-cdk/aws-lambda-nodejs/test/util.test.ts   |  6 ++++++
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts
index 6b3f7f8173a02..9bdab8776c86c 100644
--- a/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts
+++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts
@@ -54,12 +54,16 @@ export function nodeMajorVersion(): number {
  * Find a file by walking up parent directories
  */
 export function findUp(name: string, directory: string = process.cwd()): string | undefined {
-  const { root } = path.parse(directory);
-  if (directory === root && !fs.existsSync(path.join(directory, name))) {
-    return undefined;
-  }
+  const absoluteDirectory = path.resolve(directory);
+
   if (fs.existsSync(path.join(directory, name))) {
     return directory;
   }
-  return findUp(name, path.dirname(directory));
+
+  const { root } = path.parse(absoluteDirectory);
+  if (absoluteDirectory === root) {
+    return undefined;
+  }
+
+  return findUp(name, path.dirname(absoluteDirectory));
 }
diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts
index 026f2bcb519e3..a85b0064cef85 100644
--- a/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts
+++ b/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts
@@ -10,4 +10,10 @@ test('findUp', () => {
 
   // Starting at a specific path
   expect(findUp('util.test.ts', path.join(__dirname, 'integ-handlers'))).toMatch(/aws-lambda-nodejs\/test$/);
+
+  // Non existing file starting at a non existing relative path
+  expect(findUp('not-to-be-found.txt', 'non-existing/relative/path')).toBe(undefined);
+
+  // Starting at a relative path
+  expect(findUp('util.test.ts', 'test/integ-handlers')).toMatch(/aws-lambda-nodejs\/test$/);
 });

From 755648a7d3e318dc63fafed2c75e720f1e9a56ed Mon Sep 17 00:00:00 2001
From: Shiv Lakshminarayan <shivlaks@amazon.com>
Date: Mon, 6 Jul 2020 08:42:48 -0700
Subject: [PATCH 06/22] chore(stepfunctions): mark the task class as deprecated
 (#8829)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts
index 1d275d3fb25f8..29a1d6845aeb9 100644
--- a/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts
+++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts
@@ -11,6 +11,8 @@ import { renderJsonPath, State } from './state';
 
 /**
  * Props that are common to all tasks
+ *
+ * @deprecated - replaced by service integration specific classes (i.e. LambdaInvoke, SnsPublish)
  */
 export interface TaskProps {
   /**
@@ -98,6 +100,8 @@ export interface TaskProps {
  *
  * For some resource types, more specific subclasses of Task may be available
  * which are more convenient to use.
+ *
+ * @deprecated - replaced by service integration specific classes (i.e. LambdaInvoke, SnsPublish)
  */
 export class Task extends State implements INextable {
   public readonly endStates: INextable[];

From c1d4e0fecbdf716eb55578ad5721a0ead4b306e2 Mon Sep 17 00:00:00 2001
From: Matt Morgan <elthrasher@gmail.com>
Date: Mon, 6 Jul 2020 13:15:48 -0400
Subject: [PATCH 07/22] fix(core): asset bundling fails with BuildKit (#8911)

Docker buildkit has different output from the legacy builder unless you pass the -q flag in which case the output is always just the image tag. This PR works for both build versions.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/core/lib/bundling.ts       | 4 ++--
 packages/@aws-cdk/core/test/test.bundling.ts | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/@aws-cdk/core/lib/bundling.ts b/packages/@aws-cdk/core/lib/bundling.ts
index e078153386c6e..6ec85c43af22e 100644
--- a/packages/@aws-cdk/core/lib/bundling.ts
+++ b/packages/@aws-cdk/core/lib/bundling.ts
@@ -78,14 +78,14 @@ export class BundlingDockerImage {
     const buildArgs = options.buildArgs || {};
 
     const dockerArgs: string[] = [
-      'build',
+      'build', '-q',
       ...flatten(Object.entries(buildArgs).map(([k, v]) => ['--build-arg', `${k}=${v}`])),
       path,
     ];
 
     const docker = dockerExec(dockerArgs);
 
-    const match = docker.stdout.toString().match(/Successfully built ([a-z0-9]+)/);
+    const match = docker.stdout.toString().match(/sha256:([a-z0-9]+)/);
 
     if (!match) {
       throw new Error('Failed to extract image ID from Docker build output');
diff --git a/packages/@aws-cdk/core/test/test.bundling.ts b/packages/@aws-cdk/core/test/test.bundling.ts
index 558765010ccfe..500401101d22d 100644
--- a/packages/@aws-cdk/core/test/test.bundling.ts
+++ b/packages/@aws-cdk/core/test/test.bundling.ts
@@ -49,7 +49,7 @@ export = {
     const spawnSyncStub = sinon.stub(child_process, 'spawnSync').returns({
       status: 0,
       stderr: Buffer.from('stderr'),
-      stdout: Buffer.from(`Successfully built ${imageId}`),
+      stdout: Buffer.from(`sha256:${imageId}`),
       pid: 123,
       output: ['stdout', 'stderr'],
       signal: null,
@@ -63,7 +63,7 @@ export = {
     image._run();
 
     test.ok(spawnSyncStub.firstCall.calledWith('docker', [
-      'build',
+      'build', '-q',
       '--build-arg', 'TEST_ARG=cdk-test',
       'docker-path',
     ]));

From 841060d6adde4ea6d58e008f85cc155b8c3a3768 Mon Sep 17 00:00:00 2001
From: Romain Marcadier <rmuller@amazon.com>
Date: Mon, 6 Jul 2020 19:51:32 +0200
Subject: [PATCH 08/22] fix(config): cannot scope a custom rule without
 configurationChanges on (#8738)

While CloudFormation allows to specify the scope for a custom ConfigRule
without necessarily specifying `configurationChanges: true`, this was
not allowed by the corresponding construct.

This removed the offending guard, and replaced the test that verified
the throwing behavior with a regression test that validates this
configuration is allowed.


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-config/lib/rule.ts      |  20 +---
 .../test/integ.scoped-rule.expected.json      | 109 ++++++++++++++++++
 .../aws-config/test/integ.scoped-rule.ts      |  22 ++++
 .../@aws-cdk/aws-config/test/test.rule.ts     |   4 +-
 4 files changed, 139 insertions(+), 16 deletions(-)
 create mode 100644 packages/@aws-cdk/aws-config/test/integ.scoped-rule.expected.json
 create mode 100644 packages/@aws-cdk/aws-config/test/integ.scoped-rule.ts

diff --git a/packages/@aws-cdk/aws-config/lib/rule.ts b/packages/@aws-cdk/aws-config/lib/rule.ts
index 0659fefa8089b..12dd86b8ab1bd 100644
--- a/packages/@aws-cdk/aws-config/lib/rule.ts
+++ b/packages/@aws-cdk/aws-config/lib/rule.ts
@@ -122,10 +122,10 @@ abstract class RuleNew extends RuleBase {
    * @param identifier the resource identifier
    */
   public scopeToResource(type: string, identifier?: string) {
-    this.scopeTo({
+    this.scope = {
       complianceResourceId: identifier,
       complianceResourceTypes: [type],
-    });
+    };
   }
 
   /**
@@ -136,9 +136,9 @@ abstract class RuleNew extends RuleBase {
    * @param types resource types
    */
   public scopeToResources(...types: string[]) {
-    this.scopeTo({
+    this.scope = {
       complianceResourceTypes: types,
-    });
+    };
   }
 
   /**
@@ -148,18 +148,10 @@ abstract class RuleNew extends RuleBase {
    * @param value the tag value
    */
   public scopeToTag(key: string, value?: string) {
-    this.scopeTo({
+    this.scope = {
       tagKey: key,
       tagValue: value,
-    });
-  }
-
-  private scopeTo(scope: CfnConfigRule.ScopeProperty) {
-    if (!this.isManaged && !this.isCustomWithChanges) {
-      throw new Error('Cannot scope rule when `configurationChanges` is set to false.');
-    }
-
-    this.scope = scope;
+    };
   }
 }
 
diff --git a/packages/@aws-cdk/aws-config/test/integ.scoped-rule.expected.json b/packages/@aws-cdk/aws-config/test/integ.scoped-rule.expected.json
new file mode 100644
index 0000000000000..99d314d0c45af
--- /dev/null
+++ b/packages/@aws-cdk/aws-config/test/integ.scoped-rule.expected.json
@@ -0,0 +1,109 @@
+{
+  "Resources": {
+    "CustomFunctionServiceRoleD3F73B79": {
+      "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"
+              ]
+            ]
+          },
+          {
+            "Fn::Join": [
+              "",
+              [
+                "arn:",
+                {
+                  "Ref": "AWS::Partition"
+                },
+                ":iam::aws:policy/service-role/AWSConfigRulesExecutionRole"
+              ]
+            ]
+          }
+        ]
+      }
+    },
+    "CustomFunctionBADD59E7": {
+      "Type": "AWS::Lambda::Function",
+      "Properties": {
+        "Code": {
+          "ZipFile": "exports.handler = (event) => console.log(event);"
+        },
+        "Handler": "index.handler",
+        "Role": {
+          "Fn::GetAtt": [
+            "CustomFunctionServiceRoleD3F73B79",
+            "Arn"
+          ]
+        },
+        "Runtime": "nodejs10.x"
+      },
+      "DependsOn": [
+        "CustomFunctionServiceRoleD3F73B79"
+      ]
+    },
+    "CustomFunctionPermission41887A5E": {
+      "Type": "AWS::Lambda::Permission",
+      "Properties": {
+        "Action": "lambda:InvokeFunction",
+        "FunctionName": {
+          "Fn::GetAtt": [
+            "CustomFunctionBADD59E7",
+            "Arn"
+          ]
+        },
+        "Principal": "config.amazonaws.com"
+      }
+    },
+    "Custom8166710A": {
+      "Type": "AWS::Config::ConfigRule",
+      "Properties": {
+        "Source": {
+          "Owner": "CUSTOM_LAMBDA",
+          "SourceDetails": [
+            {
+              "EventSource": "aws.config",
+              "MessageType": "ScheduledNotification"
+            }
+          ],
+          "SourceIdentifier": {
+            "Fn::GetAtt": [
+              "CustomFunctionBADD59E7",
+              "Arn"
+            ]
+          }
+        },
+        "Scope": {
+          "ComplianceResourceTypes": [
+            "AWS::EC2::Instance"
+          ]
+        }
+      },
+      "DependsOn": [
+        "CustomFunctionPermission41887A5E",
+        "CustomFunctionBADD59E7",
+        "CustomFunctionServiceRoleD3F73B79"
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-config/test/integ.scoped-rule.ts b/packages/@aws-cdk/aws-config/test/integ.scoped-rule.ts
new file mode 100644
index 0000000000000..aee8392f402f0
--- /dev/null
+++ b/packages/@aws-cdk/aws-config/test/integ.scoped-rule.ts
@@ -0,0 +1,22 @@
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as cdk from '@aws-cdk/core';
+import * as config from '../lib';
+
+const app = new cdk.App();
+
+const stack = new cdk.Stack(app, 'aws-cdk-config-rule-scoped-integ');
+
+const fn = new lambda.Function(stack, 'CustomFunction', {
+  code: lambda.AssetCode.fromInline('exports.handler = (event) => console.log(event);'),
+  handler: 'index.handler',
+  runtime: lambda.Runtime.NODEJS_10_X,
+});
+
+const customRule = new config.CustomRule(stack, 'Custom', {
+  lambdaFunction: fn,
+  periodic: true,
+});
+
+customRule.scopeToResource('AWS::EC2::Instance');
+
+app.synth();
diff --git a/packages/@aws-cdk/aws-config/test/test.rule.ts b/packages/@aws-cdk/aws-config/test/test.rule.ts
index 0f19a826de6d2..c13dacf2c8a3f 100644
--- a/packages/@aws-cdk/aws-config/test/test.rule.ts
+++ b/packages/@aws-cdk/aws-config/test/test.rule.ts
@@ -204,7 +204,7 @@ export = {
     test.done();
   },
 
-  'throws when scoping a custom rule without configuration changes'(test: Test) {
+  'allows scoping a custom rule without configurationChanges enabled'(test: Test) {
     // GIVEN
     const stack = new cdk.Stack();
     const fn = new lambda.Function(stack, 'Function', {
@@ -220,7 +220,7 @@ export = {
     });
 
     // THEN
-    test.throws(() => rule.scopeToResource('resource'), /`configurationChanges`/);
+    test.doesNotThrow(() => rule.scopeToResource('resource'));
 
     test.done();
   },

From 7d47cfb39ba40a223ccc511e5706f471b9225c52 Mon Sep 17 00:00:00 2001
From: Hyeonsoo David Lee <hyeonsoo.david.lee@gmail.com>
Date: Tue, 7 Jul 2020 03:28:07 +0900
Subject: [PATCH 09/22] fix(rds): proxy for db cluster fails with model
 validation error (#8896)

fixes #8885
follows #8476

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-rds/lib/proxy.ts        | 22 +++++++---
 .../aws-rds/test/integ.proxy.expected.json    |  3 +-
 packages/@aws-cdk/aws-rds/test/test.proxy.ts  | 44 +++++++++++++++----
 3 files changed, 53 insertions(+), 16 deletions(-)

diff --git a/packages/@aws-cdk/aws-rds/lib/proxy.ts b/packages/@aws-cdk/aws-rds/lib/proxy.ts
index 4e8112ed9ffbc..9733056d8ed3b 100644
--- a/packages/@aws-cdk/aws-rds/lib/proxy.ts
+++ b/packages/@aws-cdk/aws-rds/lib/proxy.ts
@@ -408,20 +408,30 @@ export class DatabaseProxy extends cdk.Resource
     this.endpoint = this.resource.attrEndpoint;
 
     let dbInstanceIdentifiers: string[] | undefined;
-    if (bindResult.dbClusters) {
-      // support for only instances of a single cluster
-      dbInstanceIdentifiers = bindResult.dbClusters[0].instanceIdentifiers;
-    } else if (bindResult.dbInstances) {
+    if (bindResult.dbInstances) {
       // support for only single instance
       dbInstanceIdentifiers = [ bindResult.dbInstances[0].instanceIdentifier ];
     }
 
-    new CfnDBProxyTargetGroup(this, 'ProxyTargetGroup', {
+    let dbClusterIdentifiers: string[] | undefined;
+    if (bindResult.dbClusters) {
+      dbClusterIdentifiers = bindResult.dbClusters.map((c) => c.clusterIdentifier);
+    }
+
+    if (!!dbInstanceIdentifiers && !!dbClusterIdentifiers) {
+      throw new Error('Cannot specify both dbInstanceIdentifiers and dbClusterIdentifiers');
+    }
+
+    const proxyTargetGroup = new CfnDBProxyTargetGroup(this, 'ProxyTargetGroup', {
       dbProxyName: this.dbProxyName,
       dbInstanceIdentifiers,
-      dbClusterIdentifiers: bindResult.dbClusters?.map((c) => c.clusterIdentifier),
+      dbClusterIdentifiers,
       connectionPoolConfigurationInfo: toConnectionPoolConfigurationInfo(props),
     });
+
+    // Currently(2020-07-04), this property must be set to default.
+    // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbproxytargetgroup.html#TargetGroupName-fn::getatt
+    proxyTargetGroup.addOverride('Properties.TargetGroupName', 'default');
   }
 
   /**
diff --git a/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json b/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json
index d02477bf1a86a..10e7b2afb02f8 100644
--- a/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json
+++ b/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json
@@ -554,7 +554,8 @@
           {
             "Ref": "dbInstance4076B1EC"
           }
-        ]
+        ],
+        "TargetGroupName": "default"
       }
     }
   }
diff --git a/packages/@aws-cdk/aws-rds/test/test.proxy.ts b/packages/@aws-cdk/aws-rds/test/test.proxy.ts
index f0b6bfbf0c91f..2ec6d93f6eb0a 100644
--- a/packages/@aws-cdk/aws-rds/test/test.proxy.ts
+++ b/packages/@aws-cdk/aws-rds/test/test.proxy.ts
@@ -1,4 +1,4 @@
-import { expect, haveResource, ResourcePart } from '@aws-cdk/assert';
+import { ABSENT, expect, haveResource, ResourcePart } from '@aws-cdk/assert';
 import * as ec2 from '@aws-cdk/aws-ec2';
 import * as cdk from '@aws-cdk/core';
 import { Test } from 'nodeunit';
@@ -67,6 +67,7 @@ export = {
             Ref: 'InstanceC1063A87',
           },
         ],
+        TargetGroupName: 'default',
       },
     }, ResourcePart.CompleteDefinition));
 
@@ -140,17 +141,42 @@ export = {
             Ref: 'DatabaseB269D8BB',
           },
         ],
-        DBInstanceIdentifiers: [
-          {
-            Ref: 'DatabaseInstance1844F58FD',
-          },
-          {
-            Ref: 'DatabaseInstance2AA380DEE',
-          },
-        ],
+        TargetGroupName: 'default',
       },
     }, ResourcePart.CompleteDefinition));
 
     test.done();
   },
+
+  'Cannot specify both dbInstanceIdentifiers and dbClusterIdentifiers'(test: Test) {
+    // GIVEN
+    const stack = new cdk.Stack();
+    const vpc = new ec2.Vpc(stack, 'VPC');
+    const cluster = new rds.DatabaseCluster(stack, 'Database', {
+      engine: rds.DatabaseClusterEngine.AURORA_POSTGRESQL,
+      engineVersion: '10.7',
+      masterUser: {
+        username: 'admin',
+      },
+      instanceProps: {
+        instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL),
+        vpc,
+      },
+    });
+
+    // WHEN
+    test.doesNotThrow(() => {
+      new rds.DatabaseProxy(stack, 'Proxy', {
+        proxyTarget: rds.ProxyTarget.fromCluster(cluster),
+        secret: cluster.secret!,
+        vpc,
+      });
+    }, /Cannot specify both dbInstanceIdentifiers and dbClusterIdentifiers/);
+
+    expect(stack).to(haveResource('AWS::RDS::DBProxyTargetGroup', {
+      DBInstanceIdentifiers: ABSENT,
+    }, ResourcePart.Properties));
+
+    test.done();
+  },
 };

From d76077b7e57a2af1a40d5ed87c9ba8150ebc744d Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2020 20:55:12 +0000
Subject: [PATCH 10/22] chore(deps): bump aws-sdk from 2.709.0 to 2.710.0
 (#8915)

Bumps [aws-sdk](https://github.com/aws/aws-sdk-js) from 2.709.0 to 2.710.0.
- [Release notes](https://github.com/aws/aws-sdk-js/releases)
- [Changelog](https://github.com/aws/aws-sdk-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js/compare/v2.709.0...v2.710.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
---
 packages/@aws-cdk/aws-cloudfront/package.json |   2 +-
 packages/@aws-cdk/aws-cloudtrail/package.json |   2 +-
 packages/@aws-cdk/aws-codebuild/package.json  |   2 +-
 packages/@aws-cdk/aws-codecommit/package.json |   2 +-
 packages/@aws-cdk/aws-dynamodb/package.json   |   2 +-
 packages/@aws-cdk/aws-eks/package.json        |   2 +-
 .../@aws-cdk/aws-events-targets/package.json  |   2 +-
 packages/@aws-cdk/aws-lambda/package.json     |   2 +-
 packages/@aws-cdk/aws-route53/package.json    |   2 +-
 packages/@aws-cdk/aws-sqs/package.json        |   2 +-
 .../@aws-cdk/custom-resources/package.json    |   2 +-
 packages/aws-cdk/package.json                 |   2 +-
 packages/cdk-assets/package.json              |   2 +-
 yarn.lock                                     | 200 ++----------------
 14 files changed, 32 insertions(+), 194 deletions(-)

diff --git a/packages/@aws-cdk/aws-cloudfront/package.json b/packages/@aws-cdk/aws-cloudfront/package.json
index 8c2f655f0ad81..27f6835b0f021 100644
--- a/packages/@aws-cdk/aws-cloudfront/package.json
+++ b/packages/@aws-cdk/aws-cloudfront/package.json
@@ -64,7 +64,7 @@
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
     "@types/nodeunit": "^0.0.31",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json
index 0582cc87ac301..f024b97b7b532 100644
--- a/packages/@aws-cdk/aws-cloudtrail/package.json
+++ b/packages/@aws-cdk/aws-cloudtrail/package.json
@@ -64,7 +64,7 @@
   "license": "Apache-2.0",
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json
index fe25059df3db7..4c67386790f41 100644
--- a/packages/@aws-cdk/aws-codebuild/package.json
+++ b/packages/@aws-cdk/aws-codebuild/package.json
@@ -70,7 +70,7 @@
     "@aws-cdk/aws-sns": "0.0.0",
     "@aws-cdk/aws-sqs": "0.0.0",
     "@types/nodeunit": "^0.0.31",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json
index 89a7688dd7b4e..ecc3e43d5f6a6 100644
--- a/packages/@aws-cdk/aws-codecommit/package.json
+++ b/packages/@aws-cdk/aws-codecommit/package.json
@@ -70,7 +70,7 @@
     "@aws-cdk/assert": "0.0.0",
     "@aws-cdk/aws-sns": "0.0.0",
     "@types/nodeunit": "^0.0.31",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json
index 7be8d55f282bb..14ed1894e0ad4 100644
--- a/packages/@aws-cdk/aws-dynamodb/package.json
+++ b/packages/@aws-cdk/aws-dynamodb/package.json
@@ -65,7 +65,7 @@
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
     "@types/jest": "^26.0.3",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "aws-sdk-mock": "^5.1.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json
index 17233a7c7689b..af3f49fd3627c 100644
--- a/packages/@aws-cdk/aws-eks/package.json
+++ b/packages/@aws-cdk/aws-eks/package.json
@@ -65,7 +65,7 @@
     "@aws-cdk/assert": "0.0.0",
     "@types/nodeunit": "^0.0.31",
     "@types/yaml": "1.2.0",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json
index 2463cf78299c4..d22709f9f8f16 100644
--- a/packages/@aws-cdk/aws-events-targets/package.json
+++ b/packages/@aws-cdk/aws-events-targets/package.json
@@ -68,7 +68,7 @@
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
     "@aws-cdk/aws-codecommit": "0.0.0",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "aws-sdk-mock": "^5.1.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json
index 73416597604ba..4748593961596 100644
--- a/packages/@aws-cdk/aws-lambda/package.json
+++ b/packages/@aws-cdk/aws-lambda/package.json
@@ -71,7 +71,7 @@
     "@types/lodash": "^4.14.157",
     "@types/nodeunit": "^0.0.31",
     "@types/sinon": "^9.0.4",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "aws-sdk-mock": "^5.1.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json
index c7a580349cace..40f53d8bbee5d 100644
--- a/packages/@aws-cdk/aws-route53/package.json
+++ b/packages/@aws-cdk/aws-route53/package.json
@@ -64,7 +64,7 @@
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
     "@types/nodeunit": "^0.0.31",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json
index 0fbba9faa25f9..d5b79ede55998 100644
--- a/packages/@aws-cdk/aws-sqs/package.json
+++ b/packages/@aws-cdk/aws-sqs/package.json
@@ -65,7 +65,7 @@
     "@aws-cdk/assert": "0.0.0",
     "@aws-cdk/aws-s3": "0.0.0",
     "@types/nodeunit": "^0.0.31",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json
index b02ea6ad17d04..fb368e9fe72c1 100644
--- a/packages/@aws-cdk/custom-resources/package.json
+++ b/packages/@aws-cdk/custom-resources/package.json
@@ -73,7 +73,7 @@
     "@types/aws-lambda": "^8.10.39",
     "@types/fs-extra": "^8.1.0",
     "@types/sinon": "^9.0.4",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "aws-sdk-mock": "^5.1.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json
index 01bf65407bc26..bca680bdc7e8b 100644
--- a/packages/aws-cdk/package.json
+++ b/packages/aws-cdk/package.json
@@ -71,7 +71,7 @@
     "@aws-cdk/cx-api": "0.0.0",
     "@aws-cdk/region-info": "0.0.0",
     "archiver": "^4.0.1",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "camelcase": "^6.0.0",
     "cdk-assets": "0.0.0",
     "colors": "^1.4.0",
diff --git a/packages/cdk-assets/package.json b/packages/cdk-assets/package.json
index e3b70a62290a1..01283e8eb5778 100644
--- a/packages/cdk-assets/package.json
+++ b/packages/cdk-assets/package.json
@@ -47,7 +47,7 @@
     "@aws-cdk/cloud-assembly-schema": "0.0.0",
     "@aws-cdk/cx-api": "0.0.0",
     "archiver": "^4.0.1",
-    "aws-sdk": "^2.709.0",
+    "aws-sdk": "^2.710.0",
     "glob": "^7.1.6",
     "yargs": "^15.3.1"
   },
diff --git a/yarn.lock b/yarn.lock
index 4a0c668c6b587..86c3bebad3024 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1923,11 +1923,6 @@ anymatch@^3.0.3:
     normalize-path "^3.0.0"
     picomatch "^2.0.4"
 
-app-root-path@^2.2.1:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a"
-  integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==
-
 append-transform@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab"
@@ -2046,7 +2041,7 @@ array-ify@^1.0.0:
   resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece"
   integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=
 
-array-includes@^3.0.3, array-includes@^3.1.1:
+array-includes@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348"
   integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==
@@ -2072,7 +2067,7 @@ array-unique@^0.3.2:
   resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
   integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
 
-array.prototype.flat@^1.2.1, array.prototype.flat@^1.2.3:
+array.prototype.flat@^1.2.3:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b"
   integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==
@@ -2156,7 +2151,7 @@ available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2:
   dependencies:
     array-filter "^1.0.0"
 
-aws-sdk-mock@^5.0.0, aws-sdk-mock@^5.1.0:
+aws-sdk-mock@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/aws-sdk-mock/-/aws-sdk-mock-5.1.0.tgz#6f2c0bd670d7f378c906a8dd806f812124db71aa"
   integrity sha512-Wa5eCSo8HX0Snqb7FdBylaXMmfrAWoWZ+d7MFhiYsgHPvNvMEGjV945FF2qqE1U0Tolr1ALzik1fcwgaOhqUWQ==
@@ -2165,40 +2160,10 @@ aws-sdk-mock@^5.0.0, aws-sdk-mock@^5.1.0:
     sinon "^9.0.1"
     traverse "^0.6.6"
 
-aws-sdk@^2.596.0:
-  version "2.685.0"
-  resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.685.0.tgz#ba5add21e98cc785b3c05ceb9f3fcb8ab046aa8a"
-  integrity sha512-mAOj7b4PuXRxIZkNdSkBWZ28lS2wYUY7O9u33nH9a7BawlttMNbxOgE/wDCPMrTLfj+RLQx0jvoIYj8BKCTRFw==
-  dependencies:
-    buffer "4.9.1"
-    events "1.1.1"
-    ieee754 "1.1.13"
-    jmespath "0.15.0"
-    querystring "0.2.0"
-    sax "1.2.1"
-    url "0.10.3"
-    uuid "3.3.2"
-    xml2js "0.4.19"
-
-aws-sdk@^2.637.0:
-  version "2.681.0"
-  resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.681.0.tgz#09eeedb5ca49813dfc637908abe408ae114a6824"
-  integrity sha512-/p8CDJ7LZvB1i4WrJrb32FUbbPdiZFZSN6FI2lv7s/scKypmuv/iJ9kpx6QWSWQZ72kJ3Njk/0o7GuVlw0jHXw==
-  dependencies:
-    buffer "4.9.1"
-    events "1.1.1"
-    ieee754 "1.1.13"
-    jmespath "0.15.0"
-    querystring "0.2.0"
-    sax "1.2.1"
-    url "0.10.3"
-    uuid "3.3.2"
-    xml2js "0.4.19"
-
-aws-sdk@^2.709.0:
-  version "2.709.0"
-  resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.709.0.tgz#33b0c0fe8b9420c65610545394be047ac2d93c8f"
-  integrity sha512-F3sKXsCiutj9RglVXdqb/XJ3Ko3G+pX081Nf1YjVJpLydwE2v16FGxrLqE5pqyWMDeUf5nZHnBoMuOYD8ip+Kw==
+aws-sdk@^2.637.0, aws-sdk@^2.710.0:
+  version "2.710.0"
+  resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.710.0.tgz#82c086587679382f80dfad743da7f0582fcd381b"
+  integrity sha512-GQTPH0DzJMpvvtZ3VO+grkKVdL/nqjWsIfcVf1c3oedvEjW24wSXQEs6KWAGbpG2jbHsYKH7kZ4XXuq428LVAw==
   dependencies:
     buffer "4.9.2"
     events "1.1.1"
@@ -2411,15 +2376,6 @@ buffer-from@1.x, buffer-from@^1.0.0:
   resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
   integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
 
-buffer@4.9.1:
-  version "4.9.1"
-  resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
-  integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=
-  dependencies:
-    base64-js "^1.0.2"
-    ieee754 "^1.1.4"
-    isarray "^1.0.0"
-
 buffer@4.9.2:
   version "4.9.2"
   resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
@@ -3726,16 +3682,6 @@ dot-prop@^4.2.0:
   dependencies:
     is-obj "^1.0.0"
 
-dotenv-json@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/dotenv-json/-/dotenv-json-1.0.0.tgz#fc7f672aafea04bed33818733b9f94662332815c"
-  integrity sha512-jAssr+6r4nKhKRudQ0HOzMskOFFi9+ubXWwmrSGJFgTvpjyPXCXsCsYbjif6mXp7uxA7xY3/LGaiTQukZzSbOQ==
-
-dotenv@^8.0.0:
-  version "8.2.0"
-  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
-  integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
-
 dotgitignore@2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/dotgitignore/-/dotgitignore-2.1.0.tgz#a4b15a4e4ef3cf383598aaf1dfa4a04bcc089b7b"
@@ -3918,12 +3864,7 @@ escodegen@1.x.x, escodegen@^1.11.1:
   optionalDependencies:
     source-map "~0.6.1"
 
-eslint-config-standard@^14.1.0:
-  version "14.1.1"
-  resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz#830a8e44e7aef7de67464979ad06b406026c56ea"
-  integrity sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==
-
-eslint-import-resolver-node@^0.3.2, eslint-import-resolver-node@^0.3.3:
+eslint-import-resolver-node@^0.3.3:
   version "0.3.3"
   resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404"
   integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==
@@ -3950,7 +3891,7 @@ eslint-import-resolver-typescript@^2.0.0:
     tiny-glob "^0.2.6"
     tsconfig-paths "^3.9.0"
 
-eslint-module-utils@^2.4.1, eslint-module-utils@^2.6.0:
+eslint-module-utils@^2.6.0:
   version "2.6.0"
   resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6"
   integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==
@@ -3958,32 +3899,6 @@ eslint-module-utils@^2.4.1, eslint-module-utils@^2.6.0:
     debug "^2.6.9"
     pkg-dir "^2.0.0"
 
-eslint-plugin-es@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz#0f5f5da5f18aa21989feebe8a73eadefb3432976"
-  integrity sha512-f6fceVtg27BR02EYnBhgWLFQfK6bN4Ll0nQFrBHOlCsAyxeZkn0NHns5O0YZOPrV1B3ramd6cgFwaoFLcSkwEQ==
-  dependencies:
-    eslint-utils "^1.4.2"
-    regexpp "^3.0.0"
-
-eslint-plugin-import@^2.19.1:
-  version "2.20.2"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz#91fc3807ce08be4837141272c8b99073906e588d"
-  integrity sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg==
-  dependencies:
-    array-includes "^3.0.3"
-    array.prototype.flat "^1.2.1"
-    contains-path "^0.1.0"
-    debug "^2.6.9"
-    doctrine "1.5.0"
-    eslint-import-resolver-node "^0.3.2"
-    eslint-module-utils "^2.4.1"
-    has "^1.0.3"
-    minimatch "^3.0.4"
-    object.values "^1.1.0"
-    read-pkg-up "^2.0.0"
-    resolve "^1.12.0"
-
 eslint-plugin-import@^2.22.0:
   version "2.22.0"
   resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e"
@@ -4003,28 +3918,6 @@ eslint-plugin-import@^2.22.0:
     resolve "^1.17.0"
     tsconfig-paths "^3.9.0"
 
-eslint-plugin-node@^10.0.0:
-  version "10.0.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz#fd1adbc7a300cf7eb6ac55cf4b0b6fc6e577f5a6"
-  integrity sha512-1CSyM/QCjs6PXaT18+zuAXsjXGIGo5Rw630rSKwokSs2jrYURQc4R5JZpoanNCqwNmepg+0eZ9L7YiRUJb8jiQ==
-  dependencies:
-    eslint-plugin-es "^2.0.0"
-    eslint-utils "^1.4.2"
-    ignore "^5.1.1"
-    minimatch "^3.0.4"
-    resolve "^1.10.1"
-    semver "^6.1.0"
-
-eslint-plugin-promise@^4.2.1:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a"
-  integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==
-
-eslint-plugin-standard@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz#ff0519f7ffaff114f76d1bd7c3996eef0f6e20b4"
-  integrity sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==
-
 eslint-scope@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9"
@@ -4033,7 +3926,7 @@ eslint-scope@^5.0.0:
     esrecurse "^4.1.0"
     estraverse "^4.1.1"
 
-eslint-utils@^1.4.2, eslint-utils@^1.4.3:
+eslint-utils@^1.4.3:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f"
   integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==
@@ -5101,11 +4994,6 @@ ignore@^4.0.3, ignore@^4.0.6:
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
   integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
 
-ignore@^5.1.1:
-  version "5.1.6"
-  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.6.tgz#643194ad4bf2712f37852e386b6998eff0db2106"
-  integrity sha512-cgXgkypZBcCnOgSihyeqbo6gjIaIyDqPQB7Ra4vhE9m6kigdGoQDMHjviFhRZo3IMlRy6yElosoviMs5YxZXUA==
-
 immediate@~3.0.5:
   version "3.0.6"
   resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
@@ -6068,7 +5956,7 @@ jest-worker@^25.5.0:
     merge-stream "^2.0.0"
     supports-color "^7.0.0"
 
-jest@^25.4.0, jest@^25.5.0, jest@^25.5.2, jest@^25.5.3, jest@^25.5.4:
+jest@^25.4.0, jest@^25.5.2, jest@^25.5.3, jest@^25.5.4:
   version "25.5.4"
   resolved "https://registry.yarnpkg.com/jest/-/jest-25.5.4.tgz#f21107b6489cfe32b076ce2adcadee3587acb9db"
   integrity sha512-hHFJROBTqZahnO+X+PMtT6G2/ztqAZJveGqz//FnWWHurizkD05PQGzRZOhF3XP6z7SJmL+5tCfW8qV06JypwQ==
@@ -6363,24 +6251,6 @@ kleur@^3.0.3:
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
   integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
 
-lambda-leak@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/lambda-leak/-/lambda-leak-2.0.0.tgz#771985d3628487f6e885afae2b54510dcfb2cd7e"
-  integrity sha1-dxmF02KEh/boha+uK1RRDc+yzX4=
-
-lambda-tester@^3.6.0:
-  version "3.6.0"
-  resolved "https://registry.yarnpkg.com/lambda-tester/-/lambda-tester-3.6.0.tgz#ceb7d4f4f0da768487a05cff37dcd088508b5247"
-  integrity sha512-F2ZTGWCLyIR95o/jWK46V/WnOCFAEUG/m/V7/CLhPJ7PCM+pror1rZ6ujP3TkItSGxUfpJi0kqwidw+M/nEqWw==
-  dependencies:
-    app-root-path "^2.2.1"
-    dotenv "^8.0.0"
-    dotenv-json "^1.0.0"
-    lambda-leak "^2.0.0"
-    semver "^6.1.1"
-    uuid "^3.3.2"
-    vandium-utils "^1.1.1"
-
 lazystream@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4"
@@ -6986,7 +6856,7 @@ mkdirp@*, mkdirp@1.x:
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
 
-mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1:
+mkdirp@^0.5.0, mkdirp@^0.5.1:
   version "0.5.5"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
   integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -7117,17 +6987,6 @@ nise@^4.0.1:
     just-extend "^4.0.2"
     path-to-regexp "^1.7.0"
 
-nock@^11.7.0:
-  version "11.9.1"
-  resolved "https://registry.yarnpkg.com/nock/-/nock-11.9.1.tgz#2b026c5beb6d0dbcb41e7e4cefa671bc36db9c61"
-  integrity sha512-U5wPctaY4/ar2JJ5Jg4wJxlbBfayxgKbiAeGh+a1kk6Pwnc2ZEuKviLyDSG6t0uXl56q7AALIxoM6FJrBSsVXA==
-  dependencies:
-    debug "^4.1.0"
-    json-stringify-safe "^5.0.1"
-    lodash "^4.17.13"
-    mkdirp "^0.5.0"
-    propagate "^2.0.0"
-
 nock@^13.0.2:
   version "13.0.2"
   resolved "https://registry.yarnpkg.com/nock/-/nock-13.0.2.tgz#3e50f88348edbb90cce1bbbf0a3ea6a068993983"
@@ -7466,7 +7325,7 @@ object.pick@^1.3.0:
   dependencies:
     isobject "^3.0.1"
 
-object.values@^1.1.0, object.values@^1.1.1:
+object.values@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
   integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
@@ -8498,7 +8357,7 @@ resolve@1.1.7:
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
   integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
 
-resolve@^1.1.6, resolve@^1.10.1, resolve@^1.17.0:
+resolve@^1.1.6, resolve@^1.17.0:
   version "1.17.0"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
   integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
@@ -8656,11 +8515,6 @@ semver-intersect@^1.4.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
   integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
 
-semver@6.x, semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
-  version "6.3.0"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
-  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
-
 semver@7.1.1:
   version "7.1.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.1.tgz#29104598a197d6cbe4733eeecbe968f7b43a9667"
@@ -8671,6 +8525,11 @@ semver@7.x, semver@^7.2.2, semver@^7.3.2:
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
   integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
 
+semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
 set-blocking@^2.0.0, set-blocking@~2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
@@ -9644,22 +9503,6 @@ trivial-deferred@^1.0.1:
   resolved "https://registry.yarnpkg.com/trivial-deferred/-/trivial-deferred-1.0.1.tgz#376d4d29d951d6368a6f7a0ae85c2f4d5e0658f3"
   integrity sha1-N21NKdlR1jaKb3oK6FwvTV4GWPM=
 
-ts-jest@^25.3.1:
-  version "25.5.1"
-  resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-25.5.1.tgz#2913afd08f28385d54f2f4e828be4d261f4337c7"
-  integrity sha512-kHEUlZMK8fn8vkxDjwbHlxXRB9dHYpyzqKIGDNxbzs+Rz+ssNDSDNusEK8Fk/sDd4xE6iKoQLfFkFVaskmTJyw==
-  dependencies:
-    bs-logger "0.x"
-    buffer-from "1.x"
-    fast-json-stable-stringify "2.x"
-    json5 "2.x"
-    lodash.memoize "4.x"
-    make-error "1.x"
-    micromatch "4.x"
-    mkdirp "0.x"
-    semver "6.x"
-    yargs-parser "18.x"
-
 ts-jest@^26.1.1:
   version "26.1.1"
   resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.1.1.tgz#b98569b8a4d4025d966b3d40c81986dd1c510f8d"
@@ -10020,11 +9863,6 @@ validate-npm-package-name@^3.0.0:
   dependencies:
     builtins "^1.0.3"
 
-vandium-utils@^1.1.1:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/vandium-utils/-/vandium-utils-1.2.0.tgz#44735de4b7641a05de59ebe945f174e582db4f59"
-  integrity sha1-RHNd5LdkGgXeWevpRfF05YLbT1k=
-
 verror@1.10.0:
   version "1.10.0"
   resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"

From 8529387cb901fd1fea9e0ee1af1284de3ad98ce7 Mon Sep 17 00:00:00 2001
From: Pahud Hsieh <pahudnet@gmail.com>
Date: Tue, 7 Jul 2020 05:14:18 +0800
Subject: [PATCH 11/22] feat(lambda): efs filesystems (#8602)

feat(lambda): Add EFS Filesystem support

This PR adds `filesystemConfigs` construct property for `lambda.Function` and allows lambda functions to mount Amazon EFS Filesystems with the Amazon EFS Access Points.

Close #8595

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-efs/lib/access-point.ts |   6 +
 .../@aws-cdk/aws-efs/lib/efs-file-system.ts   |  18 +-
 packages/@aws-cdk/aws-lambda/README.md        |  40 +
 .../@aws-cdk/aws-lambda/lib/filesystem.ts     |  85 ++
 packages/@aws-cdk/aws-lambda/lib/function.ts  |  44 +-
 packages/@aws-cdk/aws-lambda/lib/index.ts     |   1 +
 packages/@aws-cdk/aws-lambda/package.json     |   2 +
 .../integ.lambda.filesystem.expected.json     | 808 ++++++++++++++++++
 .../test/integ.lambda.filesystem.ts           |  68 ++
 .../@aws-cdk/aws-lambda/test/test.function.ts |  59 +-
 10 files changed, 1126 insertions(+), 5 deletions(-)
 create mode 100644 packages/@aws-cdk/aws-lambda/lib/filesystem.ts
 create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.expected.json
 create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.ts

diff --git a/packages/@aws-cdk/aws-efs/lib/access-point.ts b/packages/@aws-cdk/aws-efs/lib/access-point.ts
index 6fd4b6e927bce..04399991cddb6 100644
--- a/packages/@aws-cdk/aws-efs/lib/access-point.ts
+++ b/packages/@aws-cdk/aws-efs/lib/access-point.ts
@@ -139,6 +139,11 @@ export class AccessPoint extends Resource implements IAccessPoint {
    */
   public readonly accessPointId: string;
 
+  /**
+   * The filesystem of the access point
+   */
+  public readonly fileSystem: IFileSystem;
+
   constructor(scope: Construct, id: string, props: AccessPointProps) {
     super(scope, id);
 
@@ -165,5 +170,6 @@ export class AccessPoint extends Resource implements IAccessPoint {
       resource: 'access-point',
       resourceName: this.accessPointId,
     });
+    this.fileSystem = props.fileSystem;
   }
 }
diff --git a/packages/@aws-cdk/aws-efs/lib/efs-file-system.ts b/packages/@aws-cdk/aws-efs/lib/efs-file-system.ts
index 35c6ef381a8d5..942be37ca9a3f 100644
--- a/packages/@aws-cdk/aws-efs/lib/efs-file-system.ts
+++ b/packages/@aws-cdk/aws-efs/lib/efs-file-system.ts
@@ -1,6 +1,6 @@
 import * as ec2 from '@aws-cdk/aws-ec2';
 import * as kms from '@aws-cdk/aws-kms';
-import { Construct, IResource, RemovalPolicy, Resource, Size, Tag } from '@aws-cdk/core';
+import { ConcreteDependable, Construct, IDependable, IResource, RemovalPolicy, Resource, Size, Tag } from '@aws-cdk/core';
 import { AccessPoint, AccessPointOptions } from './access-point';
 import { CfnFileSystem, CfnMountTarget } from './efs.generated';
 
@@ -83,6 +83,12 @@ export interface IFileSystem extends ec2.IConnectable, IResource {
    * @attribute
    */
   readonly fileSystemId: string;
+
+  /**
+   * Dependable that can be depended upon to ensure the mount targets of the filesystem are ready
+   */
+  readonly mountTargetsAvailable: IDependable;
+
 }
 
 /**
@@ -205,6 +211,7 @@ export class FileSystem extends Resource implements IFileSystem {
         securityGroups: [attrs.securityGroup],
         defaultPort: ec2.Port.tcp(FileSystem.DEFAULT_PORT),
       });
+      public readonly mountTargetsAvailable = new ConcreteDependable();
     }
 
     return new Import(scope, id);
@@ -225,6 +232,10 @@ export class FileSystem extends Resource implements IFileSystem {
    */
   public readonly fileSystemId: string;
 
+  public readonly mountTargetsAvailable: IDependable;
+
+  private readonly _mountTargetsAvailable = new ConcreteDependable();
+
   /**
    * Constructor for creating a new EFS FileSystem.
    */
@@ -263,15 +274,18 @@ export class FileSystem extends Resource implements IFileSystem {
 
     // We now have to create the mount target for each of the mentioned subnet
     let mountTargetCount = 0;
+    this.mountTargetsAvailable = [];
     subnets.subnetIds.forEach((subnetId: string) => {
-      new CfnMountTarget(this,
+      const mountTarget = new CfnMountTarget(this,
         'EfsMountTarget' + (++mountTargetCount),
         {
           fileSystemId: this.fileSystemId,
           securityGroups: Array.of(securityGroup.securityGroupId),
           subnetId,
         });
+      this._mountTargetsAvailable.add(mountTarget);
     });
+    this.mountTargetsAvailable = this._mountTargetsAvailable;
   }
 
   /**
diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md
index fb7283b2643d8..55ada8d6da307 100644
--- a/packages/@aws-cdk/aws-lambda/README.md
+++ b/packages/@aws-cdk/aws-lambda/README.md
@@ -277,6 +277,46 @@ correct log retention period (never expire, by default).
 *Further note* that, if the log group already exists and the `logRetention` is not set, the custom resource will reset
 the log retention to never expire even if it was configured with a different value.
 
+### FileSystem Access
+
+You can configure a function to mount an Amazon Elastic File System (Amazon EFS) to a
+directory in your runtime environment with the `filesystem` property. To access Amaozn EFS
+from lambda function, the Amazon EFS access point will be required. 
+
+The following sample allows the lambda function to mount the Amazon EFS access point to `/mnt/msg` in the runtime environment and access the filesystem with the POSIX identity defined in `posixUser`.
+
+```ts
+// create a new Amaozn EFS filesystem
+const fileSystem = new efs.FileSystem(stack, 'Efs', { vpc });
+
+// create a new access point from the filesystem
+const accessPoint = fileSystem.addAccessPoint('AccessPoint', {
+  // set /export/lambda as the root of the access point
+  path: '/export/lambda',
+  // as /export/lambda does not exist in a new efs filesystem, the efs will create the directory with the following createAcl
+  createAcl: {
+    ownerUid: '1001',
+    ownerGid: '1001',
+    permissions: '750',
+  },
+  // enforce the POSIX identity so lambda function will access with this identity
+  posixUser: {
+    uid: '1001',
+    gid: '1001',
+  },
+});
+
+const fn = new lambda.Function(stack, 'MyLambda', {
+  code,
+  handler,
+  runtime,
+  vpc,
+  // mount the access point to /mnt/msg in the lambda runtime enironment
+  filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, '/mnt/msg'),
+});
+```
+
+
 ### Singleton Function
 
 The `SingletonFunction` construct is a way to guarantee that a lambda function will be guaranteed to be part of the stack,
diff --git a/packages/@aws-cdk/aws-lambda/lib/filesystem.ts b/packages/@aws-cdk/aws-lambda/lib/filesystem.ts
new file mode 100644
index 0000000000000..108c7ea4116f7
--- /dev/null
+++ b/packages/@aws-cdk/aws-lambda/lib/filesystem.ts
@@ -0,0 +1,85 @@
+import { Connections } from '@aws-cdk/aws-ec2';
+import * as efs from '@aws-cdk/aws-efs';
+import * as iam from '@aws-cdk/aws-iam';
+import { IDependable, Stack } from '@aws-cdk/core';
+
+/**
+ * FileSystem configurations for the Lambda function
+ * @experimental
+ */
+export interface FileSystemConfig {
+  /**
+   * mount path in the lambda runtime environment
+   */
+  readonly localMountPath: string;
+
+  /**
+   * ARN of the access point
+   */
+  readonly arn: string;
+
+  /**
+   * array of IDependable that lambda function depends on
+   *
+   * @default - no dependency
+   */
+  readonly dependency?: IDependable[]
+
+  /**
+   * connections object used to allow ingress traffic from lambda function
+   *
+   * @default - no connections required to add extra ingress rules for Lambda function
+   */
+  readonly connections?: Connections;
+
+  /**
+   * additional IAM policies required for the lambda function
+   *
+   * @default - no additional policies required
+   */
+  readonly policies?: iam.PolicyStatement[];
+}
+
+/**
+ * Represents the filesystem for the Lambda function
+ * @experimental
+ */
+export class FileSystem {
+  /**
+   * mount the filesystem from Amazon EFS
+   * @param ap the Amazon EFS access point
+   * @param mountPath the target path in the lambda runtime environment
+   */
+  public static fromEfsAccessPoint(ap: efs.AccessPoint, mountPath: string): FileSystem {
+    return new FileSystem({
+      localMountPath: mountPath,
+      arn: ap.accessPointArn,
+      dependency: [ ap.fileSystem.mountTargetsAvailable ],
+      connections: ap.fileSystem.connections,
+      policies: [
+        new iam.PolicyStatement({
+          actions: [ 'elasticfilesystem:ClientMount' ],
+          resources: [ '*' ],
+          conditions: {
+            StringEquals: {
+              'elasticfilesystem:AccessPointArn': ap.accessPointArn,
+            },
+          },
+        }),
+        new iam.PolicyStatement({
+          actions: ['elasticfilesystem:ClientWrite'],
+          resources: [ Stack.of(ap).formatArn({
+            service: 'elasticfilesystem',
+            resource: 'file-system',
+            resourceName: ap.fileSystem.fileSystemId,
+          }) ],
+        }),
+      ],
+    });
+  }
+
+  /**
+   * @param config the FileSystem configurations for the Lambda function
+   */
+  protected constructor(public readonly config: FileSystemConfig) { }
+}
diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts
index d99d1b1ce8377..dbd77a2a9d793 100644
--- a/packages/@aws-cdk/aws-lambda/lib/function.ts
+++ b/packages/@aws-cdk/aws-lambda/lib/function.ts
@@ -7,6 +7,7 @@ import { CfnResource, Construct, Duration, Fn, Lazy, Stack } from '@aws-cdk/core
 import { Code, CodeConfig } from './code';
 import { EventInvokeConfigOptions } from './event-invoke-config';
 import { IEventSource } from './event-source';
+import { FileSystem } from './filesystem';
 import { FunctionAttributes, FunctionBase, IFunction } from './function-base';
 import { calculateFunctionHash, trimFromStart } from './function-hash';
 import { Version, VersionOptions } from './lambda-version';
@@ -274,6 +275,13 @@ export interface FunctionProps extends FunctionOptions {
    * the handler.
    */
   readonly handler: string;
+
+  /**
+   * The filesystem configuration for the lambda function
+   *
+   * @default - will not mount any filesystem
+   */
+  readonly filesystem?: FileSystem;
 }
 
 /**
@@ -495,6 +503,16 @@ export class Function extends FunctionBase {
     });
     this.grantPrincipal = this.role;
 
+    // add additonal managed policies when necessary
+    if (props.filesystem) {
+      const config = props.filesystem.config;
+      if (config.policies) {
+        config.policies.forEach(p => {
+          this.role?.addToPolicy(p);
+        });
+      }
+    }
+
     for (const statement of (props.initialPolicy || [])) {
       this.role.addToPolicy(statement);
     }
@@ -570,6 +588,22 @@ export class Function extends FunctionBase {
     }
 
     this.currentVersionOptions = props.currentVersionOptions;
+
+    if (props.filesystem) {
+      const config = props.filesystem.config;
+      if (config.dependency) {
+        this.node.addDependency(...config.dependency);
+      }
+
+      resource.addPropertyOverride('FileSystemConfigs',
+        [
+          {
+            LocalMountPath: config.localMountPath,
+            Arn: config.arn,
+          },
+        ],
+      );
+    }
   }
 
   /**
@@ -701,7 +735,7 @@ export class Function extends FunctionBase {
     // sort environment so the hash of the function used to create
     // `currentVersion` is not affected by key order (this is how lambda does
     // it).
-    const variables: { [key: string]: string } = { };
+    const variables: { [key: string]: string } = {};
     for (const key of Object.keys(this.environment).sort()) {
       variables[key] = this.environment[key];
     }
@@ -745,6 +779,12 @@ export class Function extends FunctionBase {
 
     this._connections = new ec2.Connections({ securityGroups });
 
+    if (props.filesystem) {
+      if (props.filesystem.config.connections) {
+        props.filesystem.config.connections.allowDefaultPortFrom(this);
+      }
+    }
+
     // Pick subnets, make sure they're not Public. Routing through an IGW
     // won't work because the ENIs don't get a Public IP.
     // Why are we not simply forcing vpcSubnets? Because you might still be choosing
@@ -841,4 +881,4 @@ export function verifyCodeConfig(code: CodeConfig, runtime: Runtime) {
   if (code.inlineCode && !runtime.supportsInlineCode) {
     throw new Error(`Inline source not allowed for ${runtime.name}`);
   }
-}
\ No newline at end of file
+}
diff --git a/packages/@aws-cdk/aws-lambda/lib/index.ts b/packages/@aws-cdk/aws-lambda/lib/index.ts
index b494e924c604a..b1d676e234a9b 100644
--- a/packages/@aws-cdk/aws-lambda/lib/index.ts
+++ b/packages/@aws-cdk/aws-lambda/lib/index.ts
@@ -6,6 +6,7 @@ export * from './layers';
 export * from './permission';
 export * from './runtime';
 export * from './code';
+export * from './filesystem';
 export * from './lambda-version';
 export * from './singleton-lambda';
 export * from './event-source';
diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json
index 4748593961596..115b656e62759 100644
--- a/packages/@aws-cdk/aws-lambda/package.json
+++ b/packages/@aws-cdk/aws-lambda/package.json
@@ -85,6 +85,7 @@
   "dependencies": {
     "@aws-cdk/aws-cloudwatch": "0.0.0",
     "@aws-cdk/aws-ec2": "0.0.0",
+    "@aws-cdk/aws-efs": "0.0.0",
     "@aws-cdk/aws-events": "0.0.0",
     "@aws-cdk/aws-iam": "0.0.0",
     "@aws-cdk/aws-logs": "0.0.0",
@@ -99,6 +100,7 @@
   "peerDependencies": {
     "@aws-cdk/aws-cloudwatch": "0.0.0",
     "@aws-cdk/aws-ec2": "0.0.0",
+    "@aws-cdk/aws-efs": "0.0.0",
     "@aws-cdk/aws-events": "0.0.0",
     "@aws-cdk/aws-iam": "0.0.0",
     "@aws-cdk/aws-logs": "0.0.0",
diff --git a/packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.expected.json
new file mode 100644
index 0000000000000..ff7a04f8b7fd5
--- /dev/null
+++ b/packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.expected.json
@@ -0,0 +1,808 @@
+{
+  "Resources": {
+    "Vpc8378EB38": {
+      "Type": "AWS::EC2::VPC",
+      "Properties": {
+        "CidrBlock": "10.0.0.0/16",
+        "EnableDnsHostnames": true,
+        "EnableDnsSupport": true,
+        "InstanceTenancy": "default",
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Vpc"
+          }
+        ]
+      }
+    },
+    "VpcPublicSubnet1Subnet5C2D37C4": {
+      "Type": "AWS::EC2::Subnet",
+      "Properties": {
+        "CidrBlock": "10.0.0.0/19",
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "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-lambda-1/Vpc/PublicSubnet1"
+          }
+        ]
+      }
+    },
+    "VpcPublicSubnet1RouteTable6C95E38E": {
+      "Type": "AWS::EC2::RouteTable",
+      "Properties": {
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Vpc/PublicSubnet1"
+          }
+        ]
+      }
+    },
+    "VpcPublicSubnet1RouteTableAssociation97140677": {
+      "Type": "AWS::EC2::SubnetRouteTableAssociation",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPublicSubnet1RouteTable6C95E38E"
+        },
+        "SubnetId": {
+          "Ref": "VpcPublicSubnet1Subnet5C2D37C4"
+        }
+      }
+    },
+    "VpcPublicSubnet1DefaultRoute3DA9E72A": {
+      "Type": "AWS::EC2::Route",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPublicSubnet1RouteTable6C95E38E"
+        },
+        "DestinationCidrBlock": "0.0.0.0/0",
+        "GatewayId": {
+          "Ref": "VpcIGWD7BA715C"
+        }
+      },
+      "DependsOn": [
+        "VpcVPCGWBF912B6E"
+      ]
+    },
+    "VpcPublicSubnet1EIPD7E02669": {
+      "Type": "AWS::EC2::EIP",
+      "Properties": {
+        "Domain": "vpc",
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Vpc/PublicSubnet1"
+          }
+        ]
+      }
+    },
+    "VpcPublicSubnet1NATGateway4D7517AA": {
+      "Type": "AWS::EC2::NatGateway",
+      "Properties": {
+        "AllocationId": {
+          "Fn::GetAtt": [
+            "VpcPublicSubnet1EIPD7E02669",
+            "AllocationId"
+          ]
+        },
+        "SubnetId": {
+          "Ref": "VpcPublicSubnet1Subnet5C2D37C4"
+        },
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Vpc/PublicSubnet1"
+          }
+        ]
+      }
+    },
+    "VpcPublicSubnet2Subnet691E08A3": {
+      "Type": "AWS::EC2::Subnet",
+      "Properties": {
+        "CidrBlock": "10.0.32.0/19",
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "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-lambda-1/Vpc/PublicSubnet2"
+          }
+        ]
+      }
+    },
+    "VpcPublicSubnet2RouteTable94F7E489": {
+      "Type": "AWS::EC2::RouteTable",
+      "Properties": {
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Vpc/PublicSubnet2"
+          }
+        ]
+      }
+    },
+    "VpcPublicSubnet2RouteTableAssociationDD5762D8": {
+      "Type": "AWS::EC2::SubnetRouteTableAssociation",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPublicSubnet2RouteTable94F7E489"
+        },
+        "SubnetId": {
+          "Ref": "VpcPublicSubnet2Subnet691E08A3"
+        }
+      }
+    },
+    "VpcPublicSubnet2DefaultRoute97F91067": {
+      "Type": "AWS::EC2::Route",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPublicSubnet2RouteTable94F7E489"
+        },
+        "DestinationCidrBlock": "0.0.0.0/0",
+        "GatewayId": {
+          "Ref": "VpcIGWD7BA715C"
+        }
+      },
+      "DependsOn": [
+        "VpcVPCGWBF912B6E"
+      ]
+    },
+    "VpcPublicSubnet3SubnetBE12F0B6": {
+      "Type": "AWS::EC2::Subnet",
+      "Properties": {
+        "CidrBlock": "10.0.64.0/19",
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "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-lambda-1/Vpc/PublicSubnet3"
+          }
+        ]
+      }
+    },
+    "VpcPublicSubnet3RouteTable93458DBB": {
+      "Type": "AWS::EC2::RouteTable",
+      "Properties": {
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Vpc/PublicSubnet3"
+          }
+        ]
+      }
+    },
+    "VpcPublicSubnet3RouteTableAssociation1F1EDF02": {
+      "Type": "AWS::EC2::SubnetRouteTableAssociation",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPublicSubnet3RouteTable93458DBB"
+        },
+        "SubnetId": {
+          "Ref": "VpcPublicSubnet3SubnetBE12F0B6"
+        }
+      }
+    },
+    "VpcPublicSubnet3DefaultRoute4697774F": {
+      "Type": "AWS::EC2::Route",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPublicSubnet3RouteTable93458DBB"
+        },
+        "DestinationCidrBlock": "0.0.0.0/0",
+        "GatewayId": {
+          "Ref": "VpcIGWD7BA715C"
+        }
+      },
+      "DependsOn": [
+        "VpcVPCGWBF912B6E"
+      ]
+    },
+    "VpcPrivateSubnet1Subnet536B997A": {
+      "Type": "AWS::EC2::Subnet",
+      "Properties": {
+        "CidrBlock": "10.0.96.0/19",
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "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-lambda-1/Vpc/PrivateSubnet1"
+          }
+        ]
+      }
+    },
+    "VpcPrivateSubnet1RouteTableB2C5B500": {
+      "Type": "AWS::EC2::RouteTable",
+      "Properties": {
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Vpc/PrivateSubnet1"
+          }
+        ]
+      }
+    },
+    "VpcPrivateSubnet1RouteTableAssociation70C59FA6": {
+      "Type": "AWS::EC2::SubnetRouteTableAssociation",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPrivateSubnet1RouteTableB2C5B500"
+        },
+        "SubnetId": {
+          "Ref": "VpcPrivateSubnet1Subnet536B997A"
+        }
+      }
+    },
+    "VpcPrivateSubnet1DefaultRouteBE02A9ED": {
+      "Type": "AWS::EC2::Route",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPrivateSubnet1RouteTableB2C5B500"
+        },
+        "DestinationCidrBlock": "0.0.0.0/0",
+        "NatGatewayId": {
+          "Ref": "VpcPublicSubnet1NATGateway4D7517AA"
+        }
+      }
+    },
+    "VpcPrivateSubnet2Subnet3788AAA1": {
+      "Type": "AWS::EC2::Subnet",
+      "Properties": {
+        "CidrBlock": "10.0.128.0/19",
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "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-lambda-1/Vpc/PrivateSubnet2"
+          }
+        ]
+      }
+    },
+    "VpcPrivateSubnet2RouteTableA678073B": {
+      "Type": "AWS::EC2::RouteTable",
+      "Properties": {
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Vpc/PrivateSubnet2"
+          }
+        ]
+      }
+    },
+    "VpcPrivateSubnet2RouteTableAssociationA89CAD56": {
+      "Type": "AWS::EC2::SubnetRouteTableAssociation",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPrivateSubnet2RouteTableA678073B"
+        },
+        "SubnetId": {
+          "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+        }
+      }
+    },
+    "VpcPrivateSubnet2DefaultRoute060D2087": {
+      "Type": "AWS::EC2::Route",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPrivateSubnet2RouteTableA678073B"
+        },
+        "DestinationCidrBlock": "0.0.0.0/0",
+        "NatGatewayId": {
+          "Ref": "VpcPublicSubnet1NATGateway4D7517AA"
+        }
+      }
+    },
+    "VpcPrivateSubnet3SubnetF258B56E": {
+      "Type": "AWS::EC2::Subnet",
+      "Properties": {
+        "CidrBlock": "10.0.160.0/19",
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "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-lambda-1/Vpc/PrivateSubnet3"
+          }
+        ]
+      }
+    },
+    "VpcPrivateSubnet3RouteTableD98824C7": {
+      "Type": "AWS::EC2::RouteTable",
+      "Properties": {
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Vpc/PrivateSubnet3"
+          }
+        ]
+      }
+    },
+    "VpcPrivateSubnet3RouteTableAssociation16BDDC43": {
+      "Type": "AWS::EC2::SubnetRouteTableAssociation",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPrivateSubnet3RouteTableD98824C7"
+        },
+        "SubnetId": {
+          "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+        }
+      }
+    },
+    "VpcPrivateSubnet3DefaultRoute94B74F0D": {
+      "Type": "AWS::EC2::Route",
+      "Properties": {
+        "RouteTableId": {
+          "Ref": "VpcPrivateSubnet3RouteTableD98824C7"
+        },
+        "DestinationCidrBlock": "0.0.0.0/0",
+        "NatGatewayId": {
+          "Ref": "VpcPublicSubnet1NATGateway4D7517AA"
+        }
+      }
+    },
+    "VpcIGWD7BA715C": {
+      "Type": "AWS::EC2::InternetGateway",
+      "Properties": {
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Vpc"
+          }
+        ]
+      }
+    },
+    "VpcVPCGWBF912B6E": {
+      "Type": "AWS::EC2::VPCGatewayAttachment",
+      "Properties": {
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        },
+        "InternetGatewayId": {
+          "Ref": "VpcIGWD7BA715C"
+        }
+      }
+    },
+    "Efs9E8BF36B": {
+      "Type": "AWS::EFS::FileSystem",
+      "Properties": {
+        "FileSystemTags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Efs"
+          }
+        ]
+      },
+      "UpdateReplacePolicy": "Delete",
+      "DeletionPolicy": "Delete"
+    },
+    "EfsEfsSecurityGroup6F40EA3B": {
+      "Type": "AWS::EC2::SecurityGroup",
+      "Properties": {
+        "GroupDescription": "aws-cdk-lambda-1/Efs/EfsSecurityGroup",
+        "SecurityGroupEgress": [
+          {
+            "CidrIp": "0.0.0.0/0",
+            "Description": "Allow all outbound traffic by default",
+            "IpProtocol": "-1"
+          }
+        ],
+        "Tags": [
+          {
+            "Key": "Name",
+            "Value": "aws-cdk-lambda-1/Efs"
+          }
+        ],
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        }
+      }
+    },
+    "EfsEfsSecurityGroupfromawscdklambda1MyLambdaSecurityGroup86B085EE20490D9864A8": {
+      "Type": "AWS::EC2::SecurityGroupIngress",
+      "Properties": {
+        "IpProtocol": "tcp",
+        "Description": "from awscdklambda1MyLambdaSecurityGroup86B085EE:2049",
+        "FromPort": 2049,
+        "GroupId": {
+          "Fn::GetAtt": [
+            "EfsEfsSecurityGroup6F40EA3B",
+            "GroupId"
+          ]
+        },
+        "SourceSecurityGroupId": {
+          "Fn::GetAtt": [
+            "MyLambdaSecurityGroup1E71A818",
+            "GroupId"
+          ]
+        },
+        "ToPort": 2049
+      }
+    },
+    "EfsEfsMountTarget195B2DD2E": {
+      "Type": "AWS::EFS::MountTarget",
+      "Properties": {
+        "FileSystemId": {
+          "Ref": "Efs9E8BF36B"
+        },
+        "SecurityGroups": [
+          {
+            "Fn::GetAtt": [
+              "EfsEfsSecurityGroup6F40EA3B",
+              "GroupId"
+            ]
+          }
+        ],
+        "SubnetId": {
+          "Ref": "VpcPrivateSubnet1Subnet536B997A"
+        }
+      }
+    },
+    "EfsEfsMountTarget2315C927F": {
+      "Type": "AWS::EFS::MountTarget",
+      "Properties": {
+        "FileSystemId": {
+          "Ref": "Efs9E8BF36B"
+        },
+        "SecurityGroups": [
+          {
+            "Fn::GetAtt": [
+              "EfsEfsSecurityGroup6F40EA3B",
+              "GroupId"
+            ]
+          }
+        ],
+        "SubnetId": {
+          "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+        }
+      }
+    },
+    "EfsEfsMountTarget36646B9A0": {
+      "Type": "AWS::EFS::MountTarget",
+      "Properties": {
+        "FileSystemId": {
+          "Ref": "Efs9E8BF36B"
+        },
+        "SecurityGroups": [
+          {
+            "Fn::GetAtt": [
+              "EfsEfsSecurityGroup6F40EA3B",
+              "GroupId"
+            ]
+          }
+        ],
+        "SubnetId": {
+          "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+        }
+      }
+    },
+    "EfsAccessPointE419FED9": {
+      "Type": "AWS::EFS::AccessPoint",
+      "Properties": {
+        "FileSystemId": {
+          "Ref": "Efs9E8BF36B"
+        },
+        "PosixUser": {
+          "Gid": "1001",
+          "Uid": "1001"
+        },
+        "RootDirectory": {
+          "CreationInfo": {
+            "OwnerGid": "1001",
+            "OwnerUid": "1001",
+            "Permissions": "750"
+          },
+          "Path": "/export/lambda"
+        }
+      }
+    },
+    "MyLambdaServiceRole4539ECB6": {
+      "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"
+              ]
+            ]
+          },
+          {
+            "Fn::Join": [
+              "",
+              [
+                "arn:",
+                {
+                  "Ref": "AWS::Partition"
+                },
+                ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
+              ]
+            ]
+          }
+        ]
+      },
+      "DependsOn": [
+        "EfsEfsMountTarget195B2DD2E",
+        "EfsEfsMountTarget2315C927F",
+        "EfsEfsMountTarget36646B9A0"
+      ]
+    },
+    "MyLambdaServiceRoleDefaultPolicy5BBC6F68": {
+      "Type": "AWS::IAM::Policy",
+      "Properties": {
+        "PolicyDocument": {
+          "Statement": [
+            {
+              "Action": "elasticfilesystem:ClientMount",
+              "Condition": {
+                "StringEquals": {
+                  "elasticfilesystem:AccessPointArn": {
+                    "Fn::Join": [
+                      "",
+                      [
+                        "arn:",
+                        {
+                          "Ref": "AWS::Partition"
+                        },
+                        ":elasticfilesystem:",
+                        {
+                          "Ref": "AWS::Region"
+                        },
+                        ":",
+                        {
+                          "Ref": "AWS::AccountId"
+                        },
+                        ":access-point/",
+                        {
+                          "Ref": "EfsAccessPointE419FED9"
+                        }
+                      ]
+                    ]
+                  }
+                }
+              },
+              "Effect": "Allow",
+              "Resource": "*"
+            },
+            {
+              "Action": "elasticfilesystem:ClientWrite",
+              "Effect": "Allow",
+              "Resource": {
+                "Fn::Join": [
+                  "",
+                  [
+                    "arn:",
+                    {
+                      "Ref": "AWS::Partition"
+                    },
+                    ":elasticfilesystem:",
+                    {
+                      "Ref": "AWS::Region"
+                    },
+                    ":",
+                    {
+                      "Ref": "AWS::AccountId"
+                    },
+                    ":file-system/",
+                    {
+                      "Ref": "Efs9E8BF36B"
+                    }
+                  ]
+                ]
+              }
+            }
+          ],
+          "Version": "2012-10-17"
+        },
+        "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68",
+        "Roles": [
+          {
+            "Ref": "MyLambdaServiceRole4539ECB6"
+          }
+        ]
+      },
+      "DependsOn": [
+        "EfsEfsMountTarget195B2DD2E",
+        "EfsEfsMountTarget2315C927F",
+        "EfsEfsMountTarget36646B9A0"
+      ]
+    },
+    "MyLambdaSecurityGroup1E71A818": {
+      "Type": "AWS::EC2::SecurityGroup",
+      "Properties": {
+        "GroupDescription": "Automatic security group for Lambda Function awscdklambda1MyLambda82056696",
+        "SecurityGroupEgress": [
+          {
+            "CidrIp": "0.0.0.0/0",
+            "Description": "Allow all outbound traffic by default",
+            "IpProtocol": "-1"
+          }
+        ],
+        "VpcId": {
+          "Ref": "Vpc8378EB38"
+        }
+      },
+      "DependsOn": [
+        "EfsEfsMountTarget195B2DD2E",
+        "EfsEfsMountTarget2315C927F",
+        "EfsEfsMountTarget36646B9A0"
+      ]
+    },
+    "MyLambdaCCE802FB": {
+      "Type": "AWS::Lambda::Function",
+      "Properties": {
+        "Code": {
+          "ZipFile": "\nimport json\nimport os\nimport string\nimport random\nimport datetime\n\nMSG_FILE_PATH = '/mnt/msg/content'\n\ndef randomString(stringLength=10):\n  letters = string.ascii_lowercase\n  return ''.join(random.choice(letters) for i in range(stringLength))\n\ndef lambda_handler(event, context):\n  with open(MSG_FILE_PATH, 'a') as f:\n      f.write(f\"{datetime.datetime.utcnow():%Y-%m-%d-%H:%M:%S} \" + randomString(5) + ' ')\n\n  file = open(MSG_FILE_PATH, \"r\")\n  file_content = file.read()\n  file.close()\n\n  return {\n    'statusCode': 200,\n    'body': str(file_content)\n  }\n  "
+        },
+        "Handler": "index.lambda_handler",
+        "Role": {
+          "Fn::GetAtt": [
+            "MyLambdaServiceRole4539ECB6",
+            "Arn"
+          ]
+        },
+        "Runtime": "python3.7",
+        "VpcConfig": {
+          "SecurityGroupIds": [
+            {
+              "Fn::GetAtt": [
+                "MyLambdaSecurityGroup1E71A818",
+                "GroupId"
+              ]
+            }
+          ],
+          "SubnetIds": [
+            {
+              "Ref": "VpcPrivateSubnet1Subnet536B997A"
+            },
+            {
+              "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+            },
+            {
+              "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+            }
+          ]
+        },
+        "FileSystemConfigs": [
+          {
+            "LocalMountPath": "/mnt/msg",
+            "Arn": {
+              "Fn::Join": [
+                "",
+                [
+                  "arn:",
+                  {
+                    "Ref": "AWS::Partition"
+                  },
+                  ":elasticfilesystem:",
+                  {
+                    "Ref": "AWS::Region"
+                  },
+                  ":",
+                  {
+                    "Ref": "AWS::AccountId"
+                  },
+                  ":access-point/",
+                  {
+                    "Ref": "EfsAccessPointE419FED9"
+                  }
+                ]
+              ]
+            }
+          }
+        ]
+      },
+      "DependsOn": [
+        "EfsEfsMountTarget195B2DD2E",
+        "EfsEfsMountTarget2315C927F",
+        "EfsEfsMountTarget36646B9A0",
+        "MyLambdaServiceRoleDefaultPolicy5BBC6F68",
+        "MyLambdaServiceRole4539ECB6"
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.ts b/packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.ts
new file mode 100644
index 0000000000000..da6515233e770
--- /dev/null
+++ b/packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.ts
@@ -0,0 +1,68 @@
+import * as ec2 from '@aws-cdk/aws-ec2';
+import * as efs from '@aws-cdk/aws-efs';
+import * as cdk from '@aws-cdk/core';
+import * as lambda from '../lib';
+
+const app = new cdk.App();
+
+const stack = new cdk.Stack(app, 'aws-cdk-lambda-1');
+
+const vpc = new ec2.Vpc(stack, 'Vpc', {
+  maxAzs: 3,
+  natGateways: 1,
+});
+
+const fileSystem = new efs.FileSystem(stack, 'Efs', {
+  vpc,
+  removalPolicy: cdk.RemovalPolicy.DESTROY,
+});
+
+// create an access point and expose the root of the filesystem
+const accessPoint = fileSystem.addAccessPoint('AccessPoint', {
+  createAcl: {
+    ownerGid: '1001',
+    ownerUid: '1001',
+    permissions: '750',
+  },
+  path: '/export/lambda',
+  posixUser: {
+    gid: '1001',
+    uid: '1001',
+  },
+});
+
+// this function will mount the access point to '/mnt/msg' and write content onto /mnt/msg/content
+new lambda.Function(stack, 'MyLambda', {
+  code: new lambda.InlineCode(`
+import json
+import os
+import string
+import random
+import datetime
+
+MSG_FILE_PATH = '/mnt/msg/content'
+
+def randomString(stringLength=10):
+  letters = string.ascii_lowercase
+  return ''.join(random.choice(letters) for i in range(stringLength))
+
+def lambda_handler(event, context):
+  with open(MSG_FILE_PATH, 'a') as f:
+      f.write(f"{datetime.datetime.utcnow():%Y-%m-%d-%H:%M:%S} " + randomString(5) + ' ')
+
+  file = open(MSG_FILE_PATH, "r")
+  file_content = file.read()
+  file.close()
+
+  return {
+    'statusCode': 200,
+    'body': str(file_content)
+  }
+  `),
+  handler: 'index.lambda_handler',
+  runtime: lambda.Runtime.PYTHON_3_7,
+  vpc,
+  filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, '/mnt/msg'),
+});
+
+app.synth();
diff --git a/packages/@aws-cdk/aws-lambda/test/test.function.ts b/packages/@aws-cdk/aws-lambda/test/test.function.ts
index ccdc4fd8ec250..ee4a003b7ad5b 100644
--- a/packages/@aws-cdk/aws-lambda/test/test.function.ts
+++ b/packages/@aws-cdk/aws-lambda/test/test.function.ts
@@ -1,4 +1,6 @@
-import { expect, haveOutput } from '@aws-cdk/assert';
+import { expect, haveOutput, haveResource } from '@aws-cdk/assert';
+import * as ec2 from '@aws-cdk/aws-ec2';
+import * as efs from '@aws-cdk/aws-efs';
 import * as logs from '@aws-cdk/aws-logs';
 import * as s3 from '@aws-cdk/aws-s3';
 import * as sqs from '@aws-cdk/aws-sqs';
@@ -231,4 +233,59 @@ export = testCase({
     },
   },
 
+  'filesystem': {
+
+    'mount efs filesystem'(test: Test) {
+      // GIVEN
+      const stack = new cdk.Stack();
+      const vpc = new ec2.Vpc(stack, 'Vpc', {
+        maxAzs: 3,
+        natGateways: 1,
+      });
+
+      const fs = new efs.FileSystem(stack, 'Efs', {
+        vpc,
+      });
+      const accessPoint = fs.addAccessPoint('AccessPoint');
+      // WHEN
+      new lambda.Function(stack, 'MyFunction', {
+        handler: 'foo',
+        runtime: lambda.Runtime.NODEJS_12_X,
+        code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')),
+        filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, '/mnt/msg'),
+      });
+
+      // THEN
+      expect(stack).to(haveResource('AWS::Lambda::Function', {
+        FileSystemConfigs: [
+          {
+            Arn: {
+              'Fn::Join': [
+                '',
+                [
+                  'arn:',
+                  {
+                    Ref: 'AWS::Partition',
+                  },
+                  ':elasticfilesystem:',
+                  {
+                    Ref: 'AWS::Region',
+                  },
+                  ':',
+                  {
+                    Ref: 'AWS::AccountId',
+                  },
+                  ':access-point/',
+                  {
+                    Ref: 'EfsAccessPointE419FED9',
+                  },
+                ],
+              ],
+            },
+            LocalMountPath: '/mnt/msg',
+          }],
+      }));
+      test.done();
+    },
+  },
 });

From 2961ef9afc8a5d1e399d9ec2df7fd58d8c896ff3 Mon Sep 17 00:00:00 2001
From: Bryan Pan <bryanpan342@gmail.com>
Date: Mon, 6 Jul 2020 16:20:58 -0700
Subject: [PATCH 12/22] docs(route53): hostedZone import functions
 pre-requisites and usage causing confusion (#8830)

Wrote up some documentation stuff but happy to change it to be more clear on the differences between `HostedZone.fromLookup`, `HostedZone.fromHostedZoneAttributes`, and `HostedZone.fromHostedZoneId`.

Fixes #5547

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-route53/README.md       | 34 +++++++++++++++----
 .../@aws-cdk/aws-route53/lib/hosted-zone.ts   | 13 +++++++
 2 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/packages/@aws-cdk/aws-route53/README.md b/packages/@aws-cdk/aws-route53/README.md
index a3eec5f9f4977..cb88a5a8f16d8 100644
--- a/packages/@aws-cdk/aws-route53/README.md
+++ b/packages/@aws-cdk/aws-route53/README.md
@@ -83,7 +83,29 @@ Constructs are available for A, AAAA, CAA, CNAME, MX, NS, SRV and TXT records.
 Use the `CaaAmazonRecord` construct to easily restrict certificate authorities
 allowed to issue certificates for a domain to Amazon only.
 
-### Adding records to existing hosted zones
+### Imports
+
+If you don't know the ID of the Hosted Zone to import, you can use the 
+`HostedZone.fromLookup`:
+
+```ts
+HostedZone.fromLookup(this, 'MyZone', {
+  domainName: 'example.com'
+});
+```
+
+`HostedZone.fromLookup` requires an environment to be configured. Check
+out the [documentation](https://docs.aws.amazon.com/cdk/latest/guide/environments.html) for more documentation and examples. CDK 
+automatically looks into your `~/.aws/config` file for the `[default]` profile.
+If you want to specify a different account run `cdk deploy --profile [profile]`.
+
+```ts
+new MyDevStack(app, 'dev', { 
+  env: { 
+    account: process.env.CDK_DEFAULT_ACCOUNT, 
+    region: process.env.CDK_DEFAULT_REGION 
+}});
+```
 
 If you know the ID and Name of a Hosted Zone, you can import it directly:
 
@@ -94,11 +116,11 @@ const zone = HostedZone.fromHostedZoneAttributes(this, 'MyZone', {
 });
 ```
 
-If you don't know the ID of a Hosted Zone, you can use the `HostedZone.fromLookup`
-to discover and import it:
+Alternatively, use the `HostedZone.fromHostedZoneId` to import hosted zones if
+you know the ID and the retrieval for the `zoneName` is undesirable.
 
 ```ts
-HostedZone.fromLookup(this, 'MyZone', {
-  domainName: 'example.com'
+const zone = HostedZone.fromHostedZoneId(this, 'MyZone', {
+  hostedZoneId: 'ZOJJZC49E0EPZ',
 });
-```
+```
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts
index f7cb345f8cc73..17dc716ff0f7b 100644
--- a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts
+++ b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts
@@ -59,6 +59,8 @@ export class HostedZone extends Resource implements IHostedZone {
   /**
    * Import a Route 53 hosted zone defined either outside the CDK, or in a different CDK stack
    *
+   * Use when hosted zone ID is known. Hosted zone name becomes unavailable through this query.
+   *
    * @param scope the parent Construct for this Construct
    * @param id  the logical name of this Construct
    * @param hostedZoneId the ID of the hosted zone to import
@@ -79,6 +81,12 @@ export class HostedZone extends Resource implements IHostedZone {
 
   /**
    * Imports a hosted zone from another stack.
+   *
+   * Use when both hosted zone ID and hosted zone name are known.
+   *
+   * @param scope the parent Construct for this Construct
+   * @param id  the logical name of this Construct
+   * @param attrs the HostedZoneAttributes (hosted zone ID and hosted zone name)
    */
   public static fromHostedZoneAttributes(scope: Construct, id: string, attrs: HostedZoneAttributes): IHostedZone {
     class Import extends Resource implements IHostedZone {
@@ -94,6 +102,11 @@ export class HostedZone extends Resource implements IHostedZone {
 
   /**
    * Lookup a hosted zone in the current account/region based on query parameters.
+   * Requires environment, you must specify env for the stack.
+   *
+   * Use to easily query hosted zones.
+   *
+   * @see https://docs.aws.amazon.com/cdk/latest/guide/environments.html
    */
   public static fromLookup(scope: Construct, id: string, query: HostedZoneProviderProps): IHostedZone {
     const DEFAULT_HOSTED_ZONE: HostedZoneContextResponse = {

From 38d84149bae213d0e285d5192265043a8c0de1aa Mon Sep 17 00:00:00 2001
From: Neta Nir <neta1nir@gmail.com>
Date: Mon, 6 Jul 2020 23:15:11 -0700
Subject: [PATCH 13/22] feat(autoscaling): allow setting autoscaling group name
 (#8853)

* feat(autoscaling): allow setting autoscaling group name

Co-authored-by: Neta Nir <neta@amazon.com>
---
 packages/@aws-cdk/aws-autoscaling/.gitignore  |  2 ++
 packages/@aws-cdk/aws-autoscaling/.npmignore  |  3 +-
 .../@aws-cdk/aws-autoscaling/jest.config.js   |  2 ++
 .../aws-autoscaling/lib/auto-scaling-group.ts | 15 ++++++++--
 .../@aws-cdk/aws-autoscaling/package.json     |  6 ++--
 ...ng-group.ts => auto-scaling-group.test.ts} | 29 +++++++++++++++----
 .../aws-autoscaling/test/cron.test.ts         |  9 ++++++
 ...fecyclehooks.ts => lifecyclehooks.test.ts} |  6 ++--
 .../test/{test.scaling.ts => scaling.test.ts} |  6 ++--
 ...led-action.ts => scheduled-action.test.ts} |  6 ++--
 .../aws-autoscaling/test/test.cron.ts         | 14 ---------
 11 files changed, 64 insertions(+), 34 deletions(-)
 create mode 100644 packages/@aws-cdk/aws-autoscaling/jest.config.js
 rename packages/@aws-cdk/aws-autoscaling/test/{test.auto-scaling-group.ts => auto-scaling-group.test.ts} (97%)
 create mode 100644 packages/@aws-cdk/aws-autoscaling/test/cron.test.ts
 rename packages/@aws-cdk/aws-autoscaling/test/{test.lifecyclehooks.ts => lifecyclehooks.test.ts} (97%)
 rename packages/@aws-cdk/aws-autoscaling/test/{test.scaling.ts => scaling.test.ts} (98%)
 rename packages/@aws-cdk/aws-autoscaling/test/{test.scheduled-action.ts => scheduled-action.test.ts} (97%)
 delete mode 100644 packages/@aws-cdk/aws-autoscaling/test/test.cron.ts

diff --git a/packages/@aws-cdk/aws-autoscaling/.gitignore b/packages/@aws-cdk/aws-autoscaling/.gitignore
index d7a42f6f3de38..bc3bea4d08402 100644
--- a/packages/@aws-cdk/aws-autoscaling/.gitignore
+++ b/packages/@aws-cdk/aws-autoscaling/.gitignore
@@ -14,3 +14,5 @@ nyc.config.js
 .LAST_PACKAGE
 *.snk
 !.eslintrc.js
+
+!jest.config.js
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-autoscaling/.npmignore b/packages/@aws-cdk/aws-autoscaling/.npmignore
index fe4df9a06d9a9..fba0982f5ee45 100644
--- a/packages/@aws-cdk/aws-autoscaling/.npmignore
+++ b/packages/@aws-cdk/aws-autoscaling/.npmignore
@@ -21,4 +21,5 @@ tsconfig.json
 .eslintrc.js
 
 # exclude cdk artifacts
-**/cdk.out
\ No newline at end of file
+**/cdk.out
+jest.config.js
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-autoscaling/jest.config.js b/packages/@aws-cdk/aws-autoscaling/jest.config.js
new file mode 100644
index 0000000000000..cd664e1d069e5
--- /dev/null
+++ b/packages/@aws-cdk/aws-autoscaling/jest.config.js
@@ -0,0 +1,2 @@
+const baseConfig = require('../../../tools/cdk-build-tools/config/jest.config');
+module.exports = baseConfig;
diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts
index 4007b60c7ab3a..04c63308cc2aa 100644
--- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts
+++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts
@@ -234,6 +234,14 @@ export interface CommonAutoScalingGroupProps {
    * @default - Monitoring.DETAILED
    */
   readonly instanceMonitoring?: Monitoring;
+
+  /**
+   * The name of the Auto Scaling group. This name must be unique per Region per account.
+   *
+   * @default - Auto generated by CloudFormation
+   */
+  readonly autoScalingGroupName?: string;
+
 }
 
 /**
@@ -480,7 +488,9 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements
   private readonly notifications: NotificationConfiguration[] = [];
 
   constructor(scope: Construct, id: string, props: AutoScalingGroupProps) {
-    super(scope, id);
+    super(scope, id, {
+      physicalName: props.autoScalingGroupName,
+    });
 
     this.securityGroup = props.securityGroup || new ec2.SecurityGroup(this, 'InstanceSecurityGroup', {
       vpc: props.vpc,
@@ -576,6 +586,7 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements
 
     const { subnetIds, hasPublic } = props.vpc.selectSubnets(props.vpcSubnets);
     const asgProps: CfnAutoScalingGroupProps = {
+      autoScalingGroupName: this.physicalName,
       cooldown: props.cooldown !== undefined ? props.cooldown.toSeconds().toString() : undefined,
       minSize: Tokenization.stringifyNumber(minCapacity),
       maxSize: Tokenization.stringifyNumber(maxCapacity),
@@ -596,7 +607,7 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements
 
     this.autoScalingGroup = new CfnAutoScalingGroup(this, 'ASG', asgProps);
     this.osType = imageConfig.osType;
-    this.autoScalingGroupName = this.autoScalingGroup.ref;
+    this.autoScalingGroupName = this.getResourceNameAttribute(this.autoScalingGroup.ref),
     this.autoScalingGroupArn = Stack.of(this).formatArn({
       service: 'autoscaling',
       resource: 'autoScalingGroup:*:autoScalingGroupName',
diff --git a/packages/@aws-cdk/aws-autoscaling/package.json b/packages/@aws-cdk/aws-autoscaling/package.json
index a7090a8e2284f..42362006391c9 100644
--- a/packages/@aws-cdk/aws-autoscaling/package.json
+++ b/packages/@aws-cdk/aws-autoscaling/package.json
@@ -47,7 +47,8 @@
     "compat": "cdk-compat"
   },
   "cdk-build": {
-    "cloudformation": "AWS::AutoScaling"
+    "cloudformation": "AWS::AutoScaling",
+    "jest": true
   },
   "keywords": [
     "aws",
@@ -64,11 +65,10 @@
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
     "@aws-cdk/cx-api": "0.0.0",
-    "@types/nodeunit": "^0.0.31",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
-    "nodeunit": "^0.11.3",
+    "nodeunit-shim": "0.0.0",
     "pkglint": "0.0.0",
     "@aws-cdk/cloud-assembly-schema": "0.0.0"
   },
diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts
similarity index 97%
rename from packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts
rename to packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts
index 05ac3029b6e4c..2b0b906f4b892 100644
--- a/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts
+++ b/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts
@@ -5,12 +5,12 @@ import * as iam from '@aws-cdk/aws-iam';
 import * as sns from '@aws-cdk/aws-sns';
 import * as cxschema from '@aws-cdk/cloud-assembly-schema';
 import * as cdk from '@aws-cdk/core';
-import { Test } from 'nodeunit';
+import { nodeunitShim, Test } from 'nodeunit-shim';
 import * as autoscaling from '../lib';
 
 // tslint:disable:object-literal-key-quotes
 
-export = {
+nodeunitShim({
   'default fleet'(test: Test) {
     const stack = getTestStack();
     const vpc = mockVpc(stack);
@@ -695,7 +695,7 @@ export = {
     });
 
     // THEN
-    test.same(asg.role, importedRole);
+    test.equal(asg.role, importedRole);
     expect(stack).to(haveResource('AWS::IAM::InstanceProfile', {
       'Roles': ['HelloDude'],
     }));
@@ -1105,7 +1105,7 @@ export = {
           topic,
         }],
       });
-    }, 'Can not set notificationsTopic and notifications, notificationsTopic is deprected use notifications instead');
+    }, 'Cannot set \'notificationsTopic\' and \'notifications\', \'notificationsTopic\' is deprecated use \'notifications\' instead');
     test.done();
   },
 
@@ -1226,7 +1226,7 @@ export = {
     test.deepEqual(Object.values(autoscaling.ScalingEvent).length - 1, autoscaling.ScalingEvents.ALL._types.length);
     test.done();
   },
-};
+});
 
 function mockVpc(stack: cdk.Stack) {
   return ec2.Vpc.fromVpcAttributes(stack, 'MyVpc', {
@@ -1238,6 +1238,25 @@ function mockVpc(stack: cdk.Stack) {
   });
 }
 
+test('Can set autoScalingGroupName', () => {
+  // GIVEN
+  const stack = new cdk.Stack();
+  const vpc = mockVpc(stack);
+
+  // WHEN
+  new autoscaling.AutoScalingGroup(stack, 'MyASG', {
+    instanceType: ec2.InstanceType.of(ec2.InstanceClass.M4, ec2.InstanceSize.MICRO),
+    machineImage: new ec2.AmazonLinuxImage(),
+    vpc,
+    autoScalingGroupName: 'MyAsg',
+  });
+
+  // THEN
+  expect(stack).to(haveResourceLike('AWS::AutoScaling::AutoScalingGroup',  {
+    AutoScalingGroupName: 'MyAsg',
+  }));
+});
+
 function mockSecurityGroup(stack: cdk.Stack) {
   return ec2.SecurityGroup.fromSecurityGroupId(stack, 'MySG', 'most-secure');
 }
diff --git a/packages/@aws-cdk/aws-autoscaling/test/cron.test.ts b/packages/@aws-cdk/aws-autoscaling/test/cron.test.ts
new file mode 100644
index 0000000000000..ab47a9b60a56a
--- /dev/null
+++ b/packages/@aws-cdk/aws-autoscaling/test/cron.test.ts
@@ -0,0 +1,9 @@
+import * as autoscaling from '../lib';
+
+test('test utc cron, hour only', () => {
+  expect(autoscaling.Schedule.cron({ hour: '18', minute: '0' }).expressionString).toEqual('0 18 * * *');
+});
+
+test('test utc cron, hour and minute', () => {
+  expect(autoscaling.Schedule.cron({ hour: '18', minute: '24' }).expressionString).toEqual('24 18 * * *');
+});
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.lifecyclehooks.ts b/packages/@aws-cdk/aws-autoscaling/test/lifecyclehooks.test.ts
similarity index 97%
rename from packages/@aws-cdk/aws-autoscaling/test/test.lifecyclehooks.ts
rename to packages/@aws-cdk/aws-autoscaling/test/lifecyclehooks.test.ts
index 5b002ad102c2b..afea5c3fb1b44 100644
--- a/packages/@aws-cdk/aws-autoscaling/test/test.lifecyclehooks.ts
+++ b/packages/@aws-cdk/aws-autoscaling/test/lifecyclehooks.test.ts
@@ -2,10 +2,10 @@ import { expect, haveResource, ResourcePart } from '@aws-cdk/assert';
 import * as ec2 from '@aws-cdk/aws-ec2';
 import * as iam from '@aws-cdk/aws-iam';
 import * as cdk from '@aws-cdk/core';
-import { Test } from 'nodeunit';
+import { nodeunitShim, Test } from 'nodeunit-shim';
 import * as autoscaling from '../lib';
 
-export = {
+nodeunitShim({
   'we can add a lifecycle hook to an ASG'(test: Test) {
     // GIVEN
     const stack = new cdk.Stack();
@@ -68,7 +68,7 @@ export = {
 
     test.done();
   },
-};
+});
 
 class FakeNotificationTarget implements autoscaling.ILifecycleHookTarget {
   public bind(_scope: cdk.Construct, lifecycleHook: autoscaling.ILifecycleHook): autoscaling.LifecycleHookTargetConfig {
diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.scaling.ts b/packages/@aws-cdk/aws-autoscaling/test/scaling.test.ts
similarity index 98%
rename from packages/@aws-cdk/aws-autoscaling/test/test.scaling.ts
rename to packages/@aws-cdk/aws-autoscaling/test/scaling.test.ts
index 6168e215cfd43..a7386aa7afdb5 100644
--- a/packages/@aws-cdk/aws-autoscaling/test/test.scaling.ts
+++ b/packages/@aws-cdk/aws-autoscaling/test/scaling.test.ts
@@ -3,10 +3,10 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
 import * as ec2 from '@aws-cdk/aws-ec2';
 import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2';
 import * as cdk from '@aws-cdk/core';
-import { Test } from 'nodeunit';
+import { nodeunitShim, Test } from 'nodeunit-shim';
 import * as autoscaling from '../lib';
 
-export = {
+nodeunitShim({
   'target tracking policies': {
     'cpu utilization'(test: Test) {
       // GIVEN
@@ -221,7 +221,7 @@ export = {
 
     test.done();
   },
-};
+});
 
 class ASGFixture extends cdk.Construct {
   public readonly vpc: ec2.Vpc;
diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts b/packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts
similarity index 97%
rename from packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts
rename to packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts
index 05dd627b6302b..73684d88a5320 100644
--- a/packages/@aws-cdk/aws-autoscaling/test/test.scheduled-action.ts
+++ b/packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts
@@ -1,10 +1,10 @@
 import { expect, haveResource, MatchStyle } from '@aws-cdk/assert';
 import * as ec2 from '@aws-cdk/aws-ec2';
 import * as cdk from '@aws-cdk/core';
-import { Test } from 'nodeunit';
+import { nodeunitShim, Test } from 'nodeunit-shim';
 import * as autoscaling from '../lib';
 
-export = {
+nodeunitShim({
   'can schedule an action'(test: Test) {
     // GIVEN
     const stack = new cdk.Stack();
@@ -105,7 +105,7 @@ export = {
 
     test.done();
   },
-};
+});
 
 function makeAutoScalingGroup(scope: cdk.Construct) {
   const vpc = new ec2.Vpc(scope, 'VPC');
diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.cron.ts b/packages/@aws-cdk/aws-autoscaling/test/test.cron.ts
deleted file mode 100644
index cfba6b3f8977f..0000000000000
--- a/packages/@aws-cdk/aws-autoscaling/test/test.cron.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Test } from 'nodeunit';
-import * as autoscaling from '../lib';
-
-export = {
-  'test utc cron, hour only'(test: Test) {
-    test.equals(autoscaling.Schedule.cron({ hour: '18', minute: '0' }).expressionString, '0 18 * * *');
-    test.done();
-  },
-
-  'test utc cron, hour and minute'(test: Test) {
-    test.equals(autoscaling.Schedule.cron({ hour: '18', minute: '24' }).expressionString, '24 18 * * *');
-    test.done();
-  },
-};

From 4221874c415f748c4674d5b9ea06a50e773a8030 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2020 06:39:58 +0000
Subject: [PATCH 14/22] chore(deps): bump @typescript-eslint/eslint-plugin from
 3.5.0 to 3.6.0 (#8917)

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 3.5.0 to 3.6.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v3.6.0/packages/eslint-plugin)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
---
 tools/cdk-build-tools/package.json |  2 +-
 yarn.lock                          | 50 +++++++++++++++---------------
 2 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json
index 68e05b186710f..6f57ca81f4a64 100644
--- a/tools/cdk-build-tools/package.json
+++ b/tools/cdk-build-tools/package.json
@@ -39,7 +39,7 @@
     "pkglint": "0.0.0"
   },
   "dependencies": {
-    "@typescript-eslint/eslint-plugin": "^3.5.0",
+    "@typescript-eslint/eslint-plugin": "^3.6.0",
     "@typescript-eslint/parser": "^2.19.2",
     "awslint": "0.0.0",
     "colors": "^1.4.0",
diff --git a/yarn.lock b/yarn.lock
index 86c3bebad3024..7ec153df3ffd6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1669,12 +1669,12 @@
   resolved "https://registry.yarnpkg.com/@types/yarnpkg__lockfile/-/yarnpkg__lockfile-1.1.3.tgz#38fb31d82ed07dea87df6bd565721d11979fd761"
   integrity sha512-mhdQq10tYpiNncMkg1vovCud5jQm+rWeRVz6fxjCJlY6uhDlAn9GnMSmBa2DQwqPf/jS5YR0K/xChDEh1jdOQg==
 
-"@typescript-eslint/eslint-plugin@^3.5.0":
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.5.0.tgz#e7736e0808b5fb947a5f9dd949ae6736a7226b84"
-  integrity sha512-m4erZ8AkSjoIUOf8s4k2V1xdL2c1Vy0D3dN6/jC9d7+nEqjY3gxXCkgi3gW/GAxPaA4hV8biaCoTVdQmfAeTCQ==
+"@typescript-eslint/eslint-plugin@^3.6.0":
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz#ba2b6cae478b8fca3f2e58ff1313e4198eea2d8a"
+  integrity sha512-ubHlHVt1lsPQB/CZdEov9XuOFhNG9YRC//kuiS1cMQI6Bs1SsqKrEmZnpgRwthGR09/kEDtr9MywlqXyyYd8GA==
   dependencies:
-    "@typescript-eslint/experimental-utils" "3.5.0"
+    "@typescript-eslint/experimental-utils" "3.6.0"
     debug "^4.1.1"
     functional-red-black-tree "^1.0.1"
     regexpp "^3.0.0"
@@ -1691,14 +1691,14 @@
     eslint-scope "^5.0.0"
     eslint-utils "^2.0.0"
 
-"@typescript-eslint/experimental-utils@3.5.0":
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.5.0.tgz#d09f9ffb890d1b15a7ffa9975fae92eee05597c4"
-  integrity sha512-zGNOrVi5Wz0jcjUnFZ6QUD0MCox5hBuVwemGCew2qJzUX5xPoyR+0EzS5qD5qQXL/vnQ8Eu+nv03tpeFRwLrDg==
+"@typescript-eslint/experimental-utils@3.6.0":
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.0.tgz#0138152d66e3e53a6340f606793fb257bf2d76a1"
+  integrity sha512-4Vdf2hvYMUnTdkCNZu+yYlFtL2v+N2R7JOynIOkFbPjf9o9wQvRwRkzUdWlFd2YiiUwJLbuuLnl5civNg5ykOQ==
   dependencies:
     "@types/json-schema" "^7.0.3"
-    "@typescript-eslint/types" "3.5.0"
-    "@typescript-eslint/typescript-estree" "3.5.0"
+    "@typescript-eslint/types" "3.6.0"
+    "@typescript-eslint/typescript-estree" "3.6.0"
     eslint-scope "^5.0.0"
     eslint-utils "^2.0.0"
 
@@ -1712,10 +1712,10 @@
     "@typescript-eslint/typescript-estree" "2.28.0"
     eslint-visitor-keys "^1.1.0"
 
-"@typescript-eslint/types@3.5.0":
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.5.0.tgz#4e3d2a2272268d8ec3e3e4a37152a64956682639"
-  integrity sha512-Dreqb5idi66VVs1QkbAwVeDmdJG+sDtofJtKwKCZXIaBsINuCN7Jv5eDIHrS0hFMMiOvPH9UuOs4splW0iZe4Q==
+"@typescript-eslint/types@3.6.0":
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.6.0.tgz#4bd6eee55d2f9d35a4b36c4804be1880bf68f7bc"
+  integrity sha512-JwVj74ohUSt0ZPG+LZ7hb95fW8DFOqBuR6gE7qzq55KDI3BepqsCtHfBIoa0+Xi1AI7fq5nCu2VQL8z4eYftqg==
 
 "@typescript-eslint/typescript-estree@2.28.0":
   version "2.28.0"
@@ -1730,13 +1730,13 @@
     semver "^6.3.0"
     tsutils "^3.17.1"
 
-"@typescript-eslint/typescript-estree@3.5.0":
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.5.0.tgz#dfc895db21a381b84f24c2a719f5bf9c600dcfdc"
-  integrity sha512-Na71ezI6QP5WVR4EHxwcBJgYiD+Sre9BZO5iJK2QhrmRPo/42+b0no/HZIrdD1sjghzlYv7t+7Jis05M1uMxQg==
+"@typescript-eslint/typescript-estree@3.6.0":
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.0.tgz#9b4cab43f1192b64ff51530815b8919f166ce177"
+  integrity sha512-G57NDSABHjvob7zVV09ehWyD1K6/YUKjz5+AufObFyjNO4DVmKejj47MHjVHHlZZKgmpJD2yyH9lfCXHrPITFg==
   dependencies:
-    "@typescript-eslint/types" "3.5.0"
-    "@typescript-eslint/visitor-keys" "3.5.0"
+    "@typescript-eslint/types" "3.6.0"
+    "@typescript-eslint/visitor-keys" "3.6.0"
     debug "^4.1.1"
     glob "^7.1.6"
     is-glob "^4.0.1"
@@ -1744,10 +1744,10 @@
     semver "^7.3.2"
     tsutils "^3.17.1"
 
-"@typescript-eslint/visitor-keys@3.5.0":
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.5.0.tgz#73c1ea2582f814735e4afdc1cf6f5e3af78db60a"
-  integrity sha512-7cTp9rcX2sz9Z+zua9MCOX4cqp5rYyFD5o8LlbSpXrMTXoRdngTtotRZEkm8+FNMHPWYFhitFK+qt/brK8BVJQ==
+"@typescript-eslint/visitor-keys@3.6.0":
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.0.tgz#44185eb0cc47651034faa95c5e2e8b314ecebb26"
+  integrity sha512-p1izllL2Ubwunite0ITjubuMQRBGgjdVYwyG7lXPX8GbrA6qF0uwSRz9MnXZaHMxID4948gX0Ez8v9tUDi/KfQ==
   dependencies:
     eslint-visitor-keys "^1.1.0"
 

From ae2378cc2a537277025c9104bc43a4cc68318650 Mon Sep 17 00:00:00 2001
From: Shiv Lakshminarayan <shivlaks@amazon.com>
Date: Tue, 7 Jul 2020 00:01:22 -0700
Subject: [PATCH 15/22] feat(stepfunctions): stepfunctions and
 stepfunctions-tasks modules are now stable! (#8912)

Closes #6489

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-stepfunctions-tasks/README.md    | 4 +---
 packages/@aws-cdk/aws-stepfunctions-tasks/package.json | 7 ++-----
 packages/@aws-cdk/aws-stepfunctions/README.md          | 6 +-----
 packages/@aws-cdk/aws-stepfunctions/package.json       | 4 ++--
 4 files changed, 6 insertions(+), 15 deletions(-)

diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md
index eb391390b2056..305530f60d0fe 100644
--- a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md
+++ b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md
@@ -2,9 +2,7 @@
 <!--BEGIN STABILITY BANNER-->
 ---
 
-![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge)
-
-> The APIs of higher level constructs in this module are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version. These are not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package.
+![cdk-constructs: Stable](https://img.shields.io/badge/cdk--constructs-stable-success.svg?style=for-the-badge)
 
 ---
 <!--END STABILITY BANNER-->
diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/package.json b/packages/@aws-cdk/aws-stepfunctions-tasks/package.json
index f2dcbba5acc77..6196dd98e9a72 100644
--- a/packages/@aws-cdk/aws-stepfunctions-tasks/package.json
+++ b/packages/@aws-cdk/aws-stepfunctions-tasks/package.json
@@ -111,11 +111,8 @@
   "engines": {
     "node": ">= 10.13.0 <13 || >=13.7.0"
   },
-  "stability": "experimental",
-  "maturity": "experimental",
-  "awslint": {
-    "exclude": []
-  },
+  "stability": "stable",
+  "maturity": "stable",
   "awscdkio": {
     "announce": false
   },
diff --git a/packages/@aws-cdk/aws-stepfunctions/README.md b/packages/@aws-cdk/aws-stepfunctions/README.md
index d072abfc66063..7a74e563b4cff 100644
--- a/packages/@aws-cdk/aws-stepfunctions/README.md
+++ b/packages/@aws-cdk/aws-stepfunctions/README.md
@@ -4,11 +4,7 @@
 
 ![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge)
 
-> All classes with the `Cfn` prefix in this module ([CFN Resources](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib)) are always stable and safe to use.
-
-![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge)
-
-> The APIs of higher level constructs in this module are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version. These are not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package.
+![cdk-constructs: Stable](https://img.shields.io/badge/cdk--constructs-stable-success.svg?style=for-the-badge)
 
 ---
 <!--END STABILITY BANNER-->
diff --git a/packages/@aws-cdk/aws-stepfunctions/package.json b/packages/@aws-cdk/aws-stepfunctions/package.json
index bae715668bf4b..ca5cb32a388c7 100644
--- a/packages/@aws-cdk/aws-stepfunctions/package.json
+++ b/packages/@aws-cdk/aws-stepfunctions/package.json
@@ -95,8 +95,8 @@
       "no-unused-type:@aws-cdk/aws-stepfunctions.ServiceIntegrationPattern"
     ]
   },
-  "stability": "experimental",
-  "maturity": "experimental",
+  "stability": "stable",
+  "maturity": "stable",
   "awscdkio": {
     "announce": false
   }

From 25048f68856c5edbd92a0db7bc21d7c8a6ec797d Mon Sep 17 00:00:00 2001
From: AWS CDK Team <aws-cdk@amazon.com>
Date: Tue, 7 Jul 2020 11:17:27 +0000
Subject: [PATCH 16/22] chore(release): 1.50.0

---
 CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++
 lerna.json   |  2 +-
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4d95ed89c4daa..ce0459cfaf7c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,39 @@
 
 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.50.0](https://github.com/aws/aws-cdk/compare/v1.49.1...v1.50.0) (2020-07-07)
+
+
+### ⚠ BREAKING CHANGES
+
+* **eks:** `version` is now a mandatory property
+
+### Features
+
+* **apigatewayv2:** http api - custom domain & stage mapping ([#8027](https://github.com/aws/aws-cdk/issues/8027)) ([5e43348](https://github.com/aws/aws-cdk/commit/5e43348ecdb6a8da865bb0db22c4782b6fa4bc96)), closes [#7847](https://github.com/aws/aws-cdk/issues/7847)
+* **autoscaling:** allow setting autoscaling group name ([#8853](https://github.com/aws/aws-cdk/issues/8853)) ([38d8414](https://github.com/aws/aws-cdk/commit/38d84149bae213d0e285d5192265043a8c0de1aa))
+* **cfn-include:** add support for retrieving Output objects from the template ([#8821](https://github.com/aws/aws-cdk/issues/8821)) ([0b09bbb](https://github.com/aws/aws-cdk/commit/0b09bbb1d43192db71f682ff4f3ad125eb231d91)), closes [#8820](https://github.com/aws/aws-cdk/issues/8820)
+* **custom-resources:** include handler log group in error messages ([#8839](https://github.com/aws/aws-cdk/issues/8839)) ([8e055d4](https://github.com/aws/aws-cdk/commit/8e055d449808f97436b92b6d6e57f8053e289653))
+* **eks:** document how to add a manifest from url ([#8802](https://github.com/aws/aws-cdk/issues/8802)) ([b5acfaa](https://github.com/aws/aws-cdk/commit/b5acfaac89351ff6285acfdb36145bccca4b6b65)), closes [#8340](https://github.com/aws/aws-cdk/issues/8340)
+* **eks:** support cluster version pinning ([#8889](https://github.com/aws/aws-cdk/issues/8889)) ([a732d14](https://github.com/aws/aws-cdk/commit/a732d149ff33f6958b83d539ba3429a025dcd631)), closes [#7762](https://github.com/aws/aws-cdk/issues/7762)
+* **lambda:** efs filesystems ([#8602](https://github.com/aws/aws-cdk/issues/8602)) ([8529387](https://github.com/aws/aws-cdk/commit/8529387cb901fd1fea9e0ee1af1284de3ad98ce7)), closes [#8595](https://github.com/aws/aws-cdk/issues/8595)
+* **lambda-nodejs:** allow jsx and tsx entry files ([#8892](https://github.com/aws/aws-cdk/issues/8892)) ([4ba20fd](https://github.com/aws/aws-cdk/commit/4ba20fd2f1579034483683995fac1e18e97a1b12))
+* **s3-deployment:** prune - keep missing files on destination bucket ([#8263](https://github.com/aws/aws-cdk/issues/8263)) ([57914c7](https://github.com/aws/aws-cdk/commit/57914c7f430b69ae54c9d2d9fac4da1092b45b42)), closes [#953](https://github.com/aws/aws-cdk/issues/953)
+* **stepfunctions:** stepfunctions and stepfunctions-tasks modules are now stable! ([#8912](https://github.com/aws/aws-cdk/issues/8912)) ([ae2378c](https://github.com/aws/aws-cdk/commit/ae2378cc2a537277025c9104bc43a4cc68318650)), closes [#6489](https://github.com/aws/aws-cdk/issues/6489)
+* **stepfunctions-tasks:** task for invoking a Step Functions activity worker ([#8840](https://github.com/aws/aws-cdk/issues/8840)) ([021533c](https://github.com/aws/aws-cdk/commit/021533caa8f4e515299d1f0cdaadd9f625d6f64d))
+
+
+### Bug Fixes
+
+* **apigateway:** Lambda integration for imported functions ([#8870](https://github.com/aws/aws-cdk/issues/8870)) ([8420f96](https://github.com/aws/aws-cdk/commit/8420f96ffd6201656e908d6d7f61cdbbc3331cc1)), closes [#8869](https://github.com/aws/aws-cdk/issues/8869)
+* **config:** cannot scope a custom rule without configurationChanges on ([#8738](https://github.com/aws/aws-cdk/issues/8738)) ([841060d](https://github.com/aws/aws-cdk/commit/841060d6adde4ea6d58e008f85cc155b8c3a3768))
+* **core:** asset bundling fails with BuildKit ([#8911](https://github.com/aws/aws-cdk/issues/8911)) ([c1d4e0f](https://github.com/aws/aws-cdk/commit/c1d4e0fecbdf716eb55578ad5721a0ead4b306e2))
+* **eks:** incorrect enableDockerBridge value when enabled ([#8895](https://github.com/aws/aws-cdk/issues/8895)) ([ea0552a](https://github.com/aws/aws-cdk/commit/ea0552a4378d61cffd14483896abadad7afa5a0a)), closes [#5786](https://github.com/aws/aws-cdk/issues/5786)
+* **eks:** kubectl resources fail before fargate profiles are created ([#8859](https://github.com/aws/aws-cdk/issues/8859)) ([4fad9bc](https://github.com/aws/aws-cdk/commit/4fad9bcbd75702e89eea02a140aa010f8952329a)), closes [#8854](https://github.com/aws/aws-cdk/issues/8854) [#8574](https://github.com/aws/aws-cdk/issues/8574)
+* **eks:** missing nodegroup identity in aws-auth after awsAuth.addMasterRole ([#8901](https://github.com/aws/aws-cdk/issues/8901)) ([a9c66f7](https://github.com/aws/aws-cdk/commit/a9c66f780b233ce3c25e12f39e3b1122636411b3)), closes [#7595](https://github.com/aws/aws-cdk/issues/7595)
+* **lambda-nodejs:** maximum call stack size exceeded with relative entry file path ([#8907](https://github.com/aws/aws-cdk/issues/8907)) ([c585e18](https://github.com/aws/aws-cdk/commit/c585e1873e437341ac1b90afbe85a9cb9e6dc2d6)), closes [#8902](https://github.com/aws/aws-cdk/issues/8902)
+* **rds:** proxy for db cluster fails with model validation error ([#8896](https://github.com/aws/aws-cdk/issues/8896)) ([7d47cfb](https://github.com/aws/aws-cdk/commit/7d47cfb39ba40a223ccc511e5706f471b9225c52)), closes [#8885](https://github.com/aws/aws-cdk/issues/8885) [#8476](https://github.com/aws/aws-cdk/issues/8476)
+
 ## [1.49.1](https://github.com/aws/aws-cdk/compare/v1.49.0...v1.49.1) (2020-07-02)
 
 ### Bug Fixes
diff --git a/lerna.json b/lerna.json
index 2babe6847f5c3..57f402c732b90 100644
--- a/lerna.json
+++ b/lerna.json
@@ -10,5 +10,5 @@
     "tools/*"
   ],
   "rejectCycles": "true",
-  "version": "1.49.1"
+  "version": "1.50.0"
 }

From b25fc072a9758e3349e980de4e190fd018a7315e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20=C5=81akomy?= <tomasz.lakomy@olx.pl>
Date: Tue, 7 Jul 2020 17:49:37 +0200
Subject: [PATCH 17/22] docs(lambda): fix typo in README (#8924)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

While taking a look at changes in https://github.com/aws/aws-cdk/pull/8602/files I've noticed a small typo so I figured - why not fix just fix it 🎉

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-lambda/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md
index 55ada8d6da307..4ca6c84b320a2 100644
--- a/packages/@aws-cdk/aws-lambda/README.md
+++ b/packages/@aws-cdk/aws-lambda/README.md
@@ -280,7 +280,7 @@ the log retention to never expire even if it was configured with a different val
 ### FileSystem Access
 
 You can configure a function to mount an Amazon Elastic File System (Amazon EFS) to a
-directory in your runtime environment with the `filesystem` property. To access Amaozn EFS
+directory in your runtime environment with the `filesystem` property. To access Amazon EFS
 from lambda function, the Amazon EFS access point will be required. 
 
 The following sample allows the lambda function to mount the Amazon EFS access point to `/mnt/msg` in the runtime environment and access the filesystem with the POSIX identity defined in `posixUser`.

From be1094b4f4ef1eb194333faaf804db610535fea1 Mon Sep 17 00:00:00 2001
From: Nikolai Onken <nikolai@nikolaionken.com>
Date: Tue, 7 Jul 2020 21:56:41 +0200
Subject: [PATCH 18/22] fix(codepipeline): set correct header assignment in S3
 deployment cache control (#8864)

The `max-age` and `s-maxage` header properties were assigned using a `:` instead of `=`. This change fixes the assignment to be according to the spec. Also added missing test for the `s-maxage` property.

Fixes #8774


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 .../@aws-cdk/aws-codepipeline-actions/lib/s3/deploy-action.ts | 4 ++--
 .../test/integ.pipeline-s3-deploy.expected.json               | 2 +-
 .../aws-codepipeline-actions/test/s3/test.s3-deploy-action.ts | 3 ++-
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/deploy-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/deploy-action.ts
index 7168719bde5f6..0258c5077a2ae 100644
--- a/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/deploy-action.ts
+++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/deploy-action.ts
@@ -30,9 +30,9 @@ export class CacheControl {
   /** The 'proxy-revalidate' cache control directive. */
   public static proxyRevalidate() { return new CacheControl('proxy-revalidate'); }
   /** The 'max-age' cache control directive. */
-  public static maxAge(t: Duration) { return new CacheControl(`max-age: ${t.toSeconds()}`); }
+  public static maxAge(t: Duration) { return new CacheControl(`max-age=${t.toSeconds()}`); }
   /** The 's-max-age' cache control directive. */
-  public static sMaxAge(t: Duration) { return new CacheControl(`s-maxage: ${t.toSeconds()}`); }
+  public static sMaxAge(t: Duration) { return new CacheControl(`s-maxage=${t.toSeconds()}`); }
   /**
    * Allows you to create an arbitrary cache control directive,
    * in case our support is missing a method for a particular directive.
diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json
index 366c152690322..63f2734f64275 100644
--- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json
+++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json
@@ -319,7 +319,7 @@
                   },
                   "Extract": "true",
                   "CannedACL": "public-read",
-                  "CacheControl": "public, max-age: 43200"
+                  "CacheControl": "public, max-age=43200"
                 },
                 "InputArtifacts": [
                   {
diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/s3/test.s3-deploy-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/s3/test.s3-deploy-action.ts
index dec66faad9ed6..37ad397848a51 100644
--- a/packages/@aws-cdk/aws-codepipeline-actions/test/s3/test.s3-deploy-action.ts
+++ b/packages/@aws-cdk/aws-codepipeline-actions/test/s3/test.s3-deploy-action.ts
@@ -108,6 +108,7 @@ export = {
         cacheControl: [
           cpactions.CacheControl.setPublic(),
           cpactions.CacheControl.maxAge(Duration.hours(12)),
+          cpactions.CacheControl.sMaxAge(Duration.hours(12)),
         ],
       });
 
@@ -118,7 +119,7 @@ export = {
             'Actions': [
               {
                 'Configuration': {
-                  'CacheControl': 'public, max-age: 43200',
+                  'CacheControl': 'public, max-age=43200, s-maxage=43200',
                 },
               },
             ],

From fdba41abe4a6cc5cc8e24bbc10c2ec9086a050fc Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2020 20:56:27 +0000
Subject: [PATCH 19/22] chore(deps): bump aws-sdk from 2.710.0 to 2.711.0
 (#8930)

Bumps [aws-sdk](https://github.com/aws/aws-sdk-js) from 2.710.0 to 2.711.0.
- [Release notes](https://github.com/aws/aws-sdk-js/releases)
- [Changelog](https://github.com/aws/aws-sdk-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js/compare/v2.710.0...v2.711.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
---
 packages/@aws-cdk/aws-cloudfront/package.json     | 2 +-
 packages/@aws-cdk/aws-cloudtrail/package.json     | 2 +-
 packages/@aws-cdk/aws-codebuild/package.json      | 2 +-
 packages/@aws-cdk/aws-codecommit/package.json     | 2 +-
 packages/@aws-cdk/aws-dynamodb/package.json       | 2 +-
 packages/@aws-cdk/aws-eks/package.json            | 2 +-
 packages/@aws-cdk/aws-events-targets/package.json | 2 +-
 packages/@aws-cdk/aws-lambda/package.json         | 2 +-
 packages/@aws-cdk/aws-route53/package.json        | 2 +-
 packages/@aws-cdk/aws-sqs/package.json            | 2 +-
 packages/@aws-cdk/custom-resources/package.json   | 2 +-
 packages/aws-cdk/package.json                     | 2 +-
 packages/cdk-assets/package.json                  | 2 +-
 yarn.lock                                         | 8 ++++----
 14 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/packages/@aws-cdk/aws-cloudfront/package.json b/packages/@aws-cdk/aws-cloudfront/package.json
index 27f6835b0f021..0ac31be17d436 100644
--- a/packages/@aws-cdk/aws-cloudfront/package.json
+++ b/packages/@aws-cdk/aws-cloudfront/package.json
@@ -64,7 +64,7 @@
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
     "@types/nodeunit": "^0.0.31",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json
index f024b97b7b532..741a28cd1a1dc 100644
--- a/packages/@aws-cdk/aws-cloudtrail/package.json
+++ b/packages/@aws-cdk/aws-cloudtrail/package.json
@@ -64,7 +64,7 @@
   "license": "Apache-2.0",
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json
index 4c67386790f41..ec2b8f7e41334 100644
--- a/packages/@aws-cdk/aws-codebuild/package.json
+++ b/packages/@aws-cdk/aws-codebuild/package.json
@@ -70,7 +70,7 @@
     "@aws-cdk/aws-sns": "0.0.0",
     "@aws-cdk/aws-sqs": "0.0.0",
     "@types/nodeunit": "^0.0.31",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json
index ecc3e43d5f6a6..604dbb6317382 100644
--- a/packages/@aws-cdk/aws-codecommit/package.json
+++ b/packages/@aws-cdk/aws-codecommit/package.json
@@ -70,7 +70,7 @@
     "@aws-cdk/assert": "0.0.0",
     "@aws-cdk/aws-sns": "0.0.0",
     "@types/nodeunit": "^0.0.31",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json
index 14ed1894e0ad4..07f3492a7ef4f 100644
--- a/packages/@aws-cdk/aws-dynamodb/package.json
+++ b/packages/@aws-cdk/aws-dynamodb/package.json
@@ -65,7 +65,7 @@
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
     "@types/jest": "^26.0.3",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "aws-sdk-mock": "^5.1.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json
index af3f49fd3627c..ded5a08310a3c 100644
--- a/packages/@aws-cdk/aws-eks/package.json
+++ b/packages/@aws-cdk/aws-eks/package.json
@@ -65,7 +65,7 @@
     "@aws-cdk/assert": "0.0.0",
     "@types/nodeunit": "^0.0.31",
     "@types/yaml": "1.2.0",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json
index d22709f9f8f16..b6409db580dd6 100644
--- a/packages/@aws-cdk/aws-events-targets/package.json
+++ b/packages/@aws-cdk/aws-events-targets/package.json
@@ -68,7 +68,7 @@
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
     "@aws-cdk/aws-codecommit": "0.0.0",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "aws-sdk-mock": "^5.1.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json
index 115b656e62759..a2da45a45e1a7 100644
--- a/packages/@aws-cdk/aws-lambda/package.json
+++ b/packages/@aws-cdk/aws-lambda/package.json
@@ -71,7 +71,7 @@
     "@types/lodash": "^4.14.157",
     "@types/nodeunit": "^0.0.31",
     "@types/sinon": "^9.0.4",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "aws-sdk-mock": "^5.1.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json
index 40f53d8bbee5d..70abfe6dcd65b 100644
--- a/packages/@aws-cdk/aws-route53/package.json
+++ b/packages/@aws-cdk/aws-route53/package.json
@@ -64,7 +64,7 @@
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
     "@types/nodeunit": "^0.0.31",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json
index d5b79ede55998..58921594b5c6c 100644
--- a/packages/@aws-cdk/aws-sqs/package.json
+++ b/packages/@aws-cdk/aws-sqs/package.json
@@ -65,7 +65,7 @@
     "@aws-cdk/assert": "0.0.0",
     "@aws-cdk/aws-s3": "0.0.0",
     "@types/nodeunit": "^0.0.31",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json
index fb368e9fe72c1..205211854907c 100644
--- a/packages/@aws-cdk/custom-resources/package.json
+++ b/packages/@aws-cdk/custom-resources/package.json
@@ -73,7 +73,7 @@
     "@types/aws-lambda": "^8.10.39",
     "@types/fs-extra": "^8.1.0",
     "@types/sinon": "^9.0.4",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "aws-sdk-mock": "^5.1.0",
     "cdk-build-tools": "0.0.0",
     "cdk-integ-tools": "0.0.0",
diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json
index bca680bdc7e8b..419daa5a2f315 100644
--- a/packages/aws-cdk/package.json
+++ b/packages/aws-cdk/package.json
@@ -71,7 +71,7 @@
     "@aws-cdk/cx-api": "0.0.0",
     "@aws-cdk/region-info": "0.0.0",
     "archiver": "^4.0.1",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "camelcase": "^6.0.0",
     "cdk-assets": "0.0.0",
     "colors": "^1.4.0",
diff --git a/packages/cdk-assets/package.json b/packages/cdk-assets/package.json
index 01283e8eb5778..8f0dda60cf730 100644
--- a/packages/cdk-assets/package.json
+++ b/packages/cdk-assets/package.json
@@ -47,7 +47,7 @@
     "@aws-cdk/cloud-assembly-schema": "0.0.0",
     "@aws-cdk/cx-api": "0.0.0",
     "archiver": "^4.0.1",
-    "aws-sdk": "^2.710.0",
+    "aws-sdk": "^2.711.0",
     "glob": "^7.1.6",
     "yargs": "^15.3.1"
   },
diff --git a/yarn.lock b/yarn.lock
index 7ec153df3ffd6..f1f6190427536 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2160,10 +2160,10 @@ aws-sdk-mock@^5.1.0:
     sinon "^9.0.1"
     traverse "^0.6.6"
 
-aws-sdk@^2.637.0, aws-sdk@^2.710.0:
-  version "2.710.0"
-  resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.710.0.tgz#82c086587679382f80dfad743da7f0582fcd381b"
-  integrity sha512-GQTPH0DzJMpvvtZ3VO+grkKVdL/nqjWsIfcVf1c3oedvEjW24wSXQEs6KWAGbpG2jbHsYKH7kZ4XXuq428LVAw==
+aws-sdk@^2.637.0, aws-sdk@^2.711.0:
+  version "2.711.0"
+  resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.711.0.tgz#e4223b676d05a22dc21e17b2de74036e8405704c"
+  integrity sha512-u2dt1k7vBE5sIzzMNRB+xCSHJ8vNmqeF8/KALdzoFtbBSxM6zKl8My3aV24rNiEC2lEA0PWTQEe9130hulOr2Q==
   dependencies:
     buffer "4.9.2"
     events "1.1.1"

From 64e3d888a66da84c066298564ad2875cb93bfd27 Mon Sep 17 00:00:00 2001
From: NickBlow <nick.blow@live.com>
Date: Wed, 8 Jul 2020 04:11:36 +0100
Subject: [PATCH 20/22] fix(appmesh): Update enums for appmesh (#8716)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 .../aws-appmesh/lib/shared-interfaces.ts      |  2 ++
 .../@aws-cdk/aws-appmesh/lib/virtual-node.ts  |  4 ++++
 packages/@aws-cdk/aws-appmesh/package.json    |  4 +++-
 .../aws-appmesh/test/test.health-check.ts     | 20 +++++++++++++++++++
 4 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts
index 34f2001c62e36..c89d9b9f6635b 100644
--- a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts
+++ b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts
@@ -6,6 +6,8 @@ import { Duration } from '@aws-cdk/core';
 export enum Protocol {
   HTTP = 'http',
   TCP = 'tcp',
+  HTTP2 = 'http2',
+  GRPC = 'grpc',
 }
 
 /**
diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts
index 63c97392473c0..f7a2548a48e7b 100644
--- a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts
+++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts
@@ -167,6 +167,10 @@ function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVir
     throw new Error('The path property cannot be set with Protocol.TCP');
   }
 
+  if (hc.protocol === Protocol.GRPC && hc.path) {
+    throw new Error('The path property cannot be set with Protocol.GRPC');
+  }
+
   const healthCheck: CfnVirtualNode.HealthCheckProperty = {
     healthyThreshold: hc.healthyThreshold || 2,
     intervalMillis: (hc.interval || cdk.Duration.seconds(5)).toMilliseconds(), // min
diff --git a/packages/@aws-cdk/aws-appmesh/package.json b/packages/@aws-cdk/aws-appmesh/package.json
index 3bbc70c2941d8..d917e0ffccb98 100644
--- a/packages/@aws-cdk/aws-appmesh/package.json
+++ b/packages/@aws-cdk/aws-appmesh/package.json
@@ -134,7 +134,9 @@
       "props-default-doc:@aws-cdk/aws-appmesh.VirtualRouterAttributes.meshName",
       "props-default-doc:@aws-cdk/aws-appmesh.VirtualRouterAttributes.virtualRouterArn",
       "props-default-doc:@aws-cdk/aws-appmesh.VirtualRouterAttributes.virtualRouterName",
-      "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.HTTP"
+      "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.HTTP",
+      "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.HTTP2",
+      "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.GRPC"
     ]
   },
   "stability": "experimental",
diff --git a/packages/@aws-cdk/aws-appmesh/test/test.health-check.ts b/packages/@aws-cdk/aws-appmesh/test/test.health-check.ts
index 9f877fd26dd6a..a89ba66afb3db 100644
--- a/packages/@aws-cdk/aws-appmesh/test/test.health-check.ts
+++ b/packages/@aws-cdk/aws-appmesh/test/test.health-check.ts
@@ -128,4 +128,24 @@ export = {
 
     test.done();
   },
+
+  'throws if path and Protocol.GRPC'(test: Test) {
+    // GIVEN
+    const stack = new cdk.Stack();
+
+    // WHEN
+    const toThrow = (protocol: appmesh.Protocol) => getNode(stack).addListeners({
+      healthCheck: {
+        protocol,
+        path: '/',
+      },
+    });
+
+    // THEN
+    test.doesNotThrow(() => toThrow(appmesh.Protocol.HTTP));
+    test.throws(() => toThrow(appmesh.Protocol.GRPC), /The path property cannot be set with Protocol.GRPC/);
+
+    test.done();
+  },
+
 };

From 61e7094bed83b74014d517910b395e7051afd0ca Mon Sep 17 00:00:00 2001
From: Pankaj Yadav <nirvanapankaj12@gmail.com>
Date: Wed, 8 Jul 2020 09:01:44 +0530
Subject: [PATCH 21/22] feat(appsync) allow using imported table as data source
 (#8790)

As of now if your table is created in another stack and you want to use it as data source in graphql api. It is not allowed.
With these changes you can import existing table as data source.
fixes #8618

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-appsync/README.md       |   1 +
 .../@aws-cdk/aws-appsync/lib/graphqlapi.ts    |   6 +-
 .../test/integ.graphql.expected.json          | 143 +++++++++++++++++-
 .../aws-appsync/test/integ.graphql.ts         |  15 ++
 .../@aws-cdk/aws-appsync/test/schema.graphql  |   7 +
 5 files changed, 168 insertions(+), 4 deletions(-)

diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md
index 378a5a4c8bd7d..e815444352288 100644
--- a/packages/@aws-cdk/aws-appsync/README.md
+++ b/packages/@aws-cdk/aws-appsync/README.md
@@ -111,6 +111,7 @@ export class ApiStack extends Stack {
         type: AttributeType.STRING,
       },
     });
+    // If your table is already created you can also use use import table and use it as data source.
     const customerDS = api.addDynamoDbDataSource('Customer', 'The customer data source', customerTable);
     customerDS.createResolver({
       typeName: 'Query',
diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts
index bbc92625c5749..d45468dde9bdd 100644
--- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts
+++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts
@@ -1,5 +1,5 @@
 import { IUserPool } from '@aws-cdk/aws-cognito';
-import { Table } from '@aws-cdk/aws-dynamodb';
+import { ITable } from '@aws-cdk/aws-dynamodb';
 import {
   IGrantable,
   IPrincipal,
@@ -393,7 +393,7 @@ export class GraphQLApi extends Construct {
   public addDynamoDbDataSource(
     name: string,
     description: string,
-    table: Table,
+    table: ITable,
   ): DynamoDbDataSource {
     return new DynamoDbDataSource(this, `${name}DS`, {
       api: this,
@@ -730,7 +730,7 @@ export interface DynamoDbDataSourceProps extends BackedDataSourceProps {
    * The DynamoDB table backing this data source
    * [disable-awslint:ref-via-interface]
    */
-  readonly table: Table;
+  readonly table: ITable;
   /**
    * Specify whether this DS is read only or has read and write permissions to the DynamoDB table
    *
diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json
index 9fe51c84d0b28..41da6184b5089 100644
--- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json
+++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json
@@ -106,7 +106,7 @@
         "ApiId": {
           "Fn::GetAtt": ["ApiF70053CD", "ApiId"]
         },
-        "Definition": "type ServiceVersion {\n    version: String!\n}\n\ntype Customer {\n    id: String!\n    name: String!\n}\n\ninput SaveCustomerInput {\n    name: String!\n}\n\ntype Order {\n    customer: String!\n    order: String!\n}\n\ntype Query {\n    getServiceVersion: ServiceVersion\n    getCustomers: [Customer]\n    getCustomer(id: String): Customer\n    getCustomerOrdersEq(customer: String): Order\n    getCustomerOrdersLt(customer: String): Order\n    getCustomerOrdersLe(customer: String): Order\n    getCustomerOrdersGt(customer: String): Order\n    getCustomerOrdersGe(customer: String): Order\n    getCustomerOrdersFilter(customer: String, order: String): Order\n    getCustomerOrdersBetween(customer: String, order1: String, order2: String): Order\n}\n\ninput FirstOrderInput {\n    product: String!\n    quantity: Int!\n}\n\ntype Mutation {\n    addCustomer(customer: SaveCustomerInput!): Customer\n    saveCustomer(id: String!, customer: SaveCustomerInput!): Customer\n    removeCustomer(id: String!): Customer\n    saveCustomerWithFirstOrder(customer: SaveCustomerInput!, order: FirstOrderInput!, referral: String): Order\n    doPostOnAws: String!\n}\n"
+        "Definition": "type ServiceVersion {\n    version: String!\n}\n\ntype Customer {\n    id: String!\n    name: String!\n}\n\ninput SaveCustomerInput {\n    name: String!\n}\n\ntype Order {\n    customer: String!\n    order: String!\n}\n\ntype Payment {\n    id: String!\n    amount: String!\n}\n\ntype Query {\n    getServiceVersion: ServiceVersion\n    getCustomers: [Customer]\n    getCustomer(id: String): Customer\n    getCustomerOrdersEq(customer: String): Order\n    getCustomerOrdersLt(customer: String): Order\n    getCustomerOrdersLe(customer: String): Order\n    getCustomerOrdersGt(customer: String): Order\n    getCustomerOrdersGe(customer: String): Order\n    getCustomerOrdersFilter(customer: String, order: String): Order\n    getCustomerOrdersBetween(customer: String, order1: String, order2: String): Order\n    getPayment(id: String): Payment\n}\n\ninput FirstOrderInput {\n    product: String!\n    quantity: Int!\n}\n\ntype Mutation {\n    addCustomer(customer: SaveCustomerInput!): Customer\n    saveCustomer(id: String!, customer: SaveCustomerInput!): Customer\n    removeCustomer(id: String!): Customer\n    saveCustomerWithFirstOrder(customer: SaveCustomerInput!, order: FirstOrderInput!, referral: String): Order\n    savePayment(payment: Payment): Payment\n    doPostOnAws: String!\n}\n"
       }
     },
     "ApiNoneDSB4E6495F": {
@@ -591,6 +591,147 @@
         "ApiSchema510EECD7"
       ]
     },
+    "ApiPaymentDSServiceRole7A857DD9": {
+      "Type": "AWS::IAM::Role",
+      "Properties": {
+        "AssumeRolePolicyDocument": {
+          "Statement": [
+            {
+              "Action": "sts:AssumeRole",
+              "Effect": "Allow",
+              "Principal": {
+                "Service": "appsync.amazonaws.com"
+              }
+            }
+          ],
+          "Version": "2012-10-17"
+        }
+      }
+    },
+    "ApiPaymentDSServiceRoleDefaultPolicy1BE875C5": {
+      "Type": "AWS::IAM::Policy",
+      "Properties": {
+        "PolicyDocument": {
+          "Statement": [
+            {
+              "Action": [
+                "dynamodb:BatchGetItem",
+                "dynamodb:GetRecords",
+                "dynamodb:GetShardIterator",
+                "dynamodb:Query",
+                "dynamodb:GetItem",
+                "dynamodb:Scan",
+                "dynamodb:BatchWriteItem",
+                "dynamodb:PutItem",
+                "dynamodb:UpdateItem",
+                "dynamodb:DeleteItem"
+              ],
+              "Effect": "Allow",
+              "Resource": [
+                {
+                  "Fn::Join": [
+                    "",
+                    [
+                      "arn:",
+                      {
+                        "Ref": "AWS::Partition"
+                      },
+                      ":dynamodb:",
+                      {
+                        "Ref": "AWS::Region"
+                      },
+                      ":",
+                      {
+                        "Ref": "AWS::AccountId"
+                      },
+                      ":table/PaymentTable"
+                    ]
+                  ]
+                },
+                {
+                  "Ref": "AWS::NoValue"
+                }
+              ]
+            }
+          ],
+          "Version": "2012-10-17"
+        },
+        "PolicyName": "ApiPaymentDSServiceRoleDefaultPolicy1BE875C5",
+        "Roles": [
+          {
+            "Ref": "ApiPaymentDSServiceRole7A857DD9"
+          }
+        ]
+      }
+    },
+    "ApiPaymentDS69022256": {
+      "Type": "AWS::AppSync::DataSource",
+      "Properties": {
+        "ApiId": {
+          "Fn::GetAtt": [
+            "ApiF70053CD",
+            "ApiId"
+          ]
+        },
+        "Name": "Payment",
+        "Type": "AMAZON_DYNAMODB",
+        "Description": "The payment data source",
+        "DynamoDBConfig": {
+          "AwsRegion": {
+            "Ref": "AWS::Region"
+          },
+          "TableName": "PaymentTable"
+        },
+        "ServiceRoleArn": {
+          "Fn::GetAtt": [
+            "ApiPaymentDSServiceRole7A857DD9",
+            "Arn"
+          ]
+        }
+      }
+    },
+    "ApiPaymentDSQuerygetPaymentResolver25686F48": {
+      "Type": "AWS::AppSync::Resolver",
+      "Properties": {
+        "ApiId": {
+          "Fn::GetAtt": [
+            "ApiF70053CD",
+            "ApiId"
+          ]
+        },
+        "FieldName": "getPayment",
+        "TypeName": "Query",
+        "DataSourceName": "Payment",
+        "Kind": "UNIT",
+        "RequestMappingTemplate": "{\"version\": \"2017-02-28\", \"operation\": \"GetItem\", \"key\": {\"id\": $util.dynamodb.toDynamoDBJson($ctx.args.id)}}",
+        "ResponseMappingTemplate": "$util.toJson($ctx.result)"
+      },
+      "DependsOn": [
+        "ApiPaymentDS69022256",
+        "ApiSchema510EECD7"
+      ]
+    },
+    "ApiPaymentDSMutationsavePaymentResolver08FBC62D": {
+      "Type": "AWS::AppSync::Resolver",
+      "Properties": {
+        "ApiId": {
+          "Fn::GetAtt": [
+            "ApiF70053CD",
+            "ApiId"
+          ]
+        },
+        "FieldName": "savePayment",
+        "TypeName": "Mutation",
+        "DataSourceName": "Payment",
+        "Kind": "UNIT",
+        "RequestMappingTemplate": "\n      #set($input = $ctx.args.payment)\n      \n      {\n        \"version\": \"2017-02-28\",\n        \"operation\": \"PutItem\",\n        \"key\" : {\n      \"id\" : $util.dynamodb.toDynamoDBJson($ctx.args.id)\n    },\n        \"attributeValues\": $util.dynamodb.toMapValuesJson($input)\n      }",
+        "ResponseMappingTemplate": "$util.toJson($ctx.result)"
+      },
+      "DependsOn": [
+        "ApiPaymentDS69022256",
+        "ApiSchema510EECD7"
+      ]
+    },
     "ApihttpDSServiceRole8B5C9457": {
       "Type": "AWS::IAM::Role",
       "Properties": {
diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts b/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts
index fb2be9eeac531..f7da8537c351e 100644
--- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts
+++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.ts
@@ -71,9 +71,11 @@ const orderTable = new Table(stack, 'OrderTable', {
   },
   removalPolicy: RemovalPolicy.DESTROY,
 });
+const paymentTable =  Table.fromTableName(stack, 'PaymentTable', 'PaymentTable');
 
 const customerDS = api.addDynamoDbDataSource('Customer', 'The customer data source', customerTable);
 const orderDS = api.addDynamoDbDataSource('Order', 'The order data source', orderTable);
+const paymentDS = api.addDynamoDbDataSource('Payment', 'The payment data source', paymentTable);
 
 customerDS.createResolver({
   typeName: 'Query',
@@ -148,6 +150,19 @@ orderDS.createResolver({
   responseMappingTemplate: MappingTemplate.dynamoDbResultList(),
 });
 
+paymentDS.createResolver({
+  typeName: 'Query',
+  fieldName: 'getPayment',
+  requestMappingTemplate: MappingTemplate.dynamoDbGetItem('id', 'id'),
+  responseMappingTemplate: MappingTemplate.dynamoDbResultItem(),
+});
+paymentDS.createResolver({
+  typeName: 'Mutation',
+  fieldName: 'savePayment',
+  requestMappingTemplate: MappingTemplate.dynamoDbPutItem(PrimaryKey.partition('id').is('id'), Values.projecting('payment')),
+  responseMappingTemplate: MappingTemplate.dynamoDbResultItem(),
+});
+
 const httpDS = api.addHttpDataSource('http', 'The http data source', 'https://aws.amazon.com/');
 
 httpDS.createResolver({
diff --git a/packages/@aws-cdk/aws-appsync/test/schema.graphql b/packages/@aws-cdk/aws-appsync/test/schema.graphql
index 24af9a154ec59..09a539d883df9 100644
--- a/packages/@aws-cdk/aws-appsync/test/schema.graphql
+++ b/packages/@aws-cdk/aws-appsync/test/schema.graphql
@@ -16,6 +16,11 @@ type Order {
     order: String!
 }
 
+type Payment {
+    id: String!
+    amount: String!
+}
+
 type Query {
     getServiceVersion: ServiceVersion
     getCustomers: [Customer]
@@ -27,6 +32,7 @@ type Query {
     getCustomerOrdersGe(customer: String): Order
     getCustomerOrdersFilter(customer: String, order: String): Order
     getCustomerOrdersBetween(customer: String, order1: String, order2: String): Order
+    getPayment(id: String): Payment
 }
 
 input FirstOrderInput {
@@ -39,5 +45,6 @@ type Mutation {
     saveCustomer(id: String!, customer: SaveCustomerInput!): Customer
     removeCustomer(id: String!): Customer
     saveCustomerWithFirstOrder(customer: SaveCustomerInput!, order: FirstOrderInput!, referral: String): Order
+    savePayment(payment: Payment): Payment
     doPostOnAws: String!
 }

From 84b923fb853d674e0a07f4296f2b23800d139366 Mon Sep 17 00:00:00 2001
From: Hyeonsoo David Lee <hyeonsoo.david.lee@gmail.com>
Date: Wed, 8 Jul 2020 18:47:18 +0900
Subject: [PATCH 22/22] feat(cloudfront): Add connectionAttempts,
 connectionTimeout in origin configuration (#8573)

Closes #8572

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 packages/@aws-cdk/aws-cloudfront/README.md    |  24 ++-
 .../aws-cloudfront/lib/web_distribution.ts    |  34 +++-
 ...eg.cloudfront-bucket-logging.expected.json |   4 +
 .../integ.cloudfront-custom-s3.expected.json  |   2 +
 .../integ.cloudfront-custom.expected.json     |   2 +
 .../integ.cloudfront-empty-root.expected.json |   2 +
 ....cloudfront-geo-restrictions.expected.json |   2 +
 ...teg.cloudfront-ipv6-disabled.expected.json |   2 +
 ...loudfront-lambda-association.expected.json |   2 +
 .../test/integ.cloudfront-s3.expected.json    |   2 +
 ...g.cloudfront-security-policy.expected.json |   2 +
 .../test/integ.cloudfront.expected.json       |   2 +
 .../aws-cloudfront/test/test.basic.ts         | 177 ++++++++++++++++++
 ...nteg.cloudfront-alias-target.expected.json |   2 +
 ...bucket-deployment-cloudfront.expected.json |   2 +
 15 files changed, 257 insertions(+), 4 deletions(-)

diff --git a/packages/@aws-cdk/aws-cloudfront/README.md b/packages/@aws-cdk/aws-cloudfront/README.md
index b0ae2f6f79ba3..04488d3670742 100644
--- a/packages/@aws-cdk/aws-cloudfront/README.md
+++ b/packages/@aws-cdk/aws-cloudfront/README.md
@@ -80,4 +80,26 @@ new cloudfront.CloudFrontWebDistribution(stack, 'MyDistribution', {
    //...
     geoRestriction: GeoRestriction.whitelist('US', 'UK')
 });
-```
\ No newline at end of file
+```
+
+### Connection behaviors between CloudFront and your origin.
+
+CloudFront provides you even more control over the connection behaviors between CloudFront and your origin. You can now configure the number of connection attempts CloudFront will make to your origin and the origin connection timeout for each attempt.
+
+See [Origin Connection Attempts](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#origin-connection-attempts)
+
+See [Origin Connection Timeout](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#origin-connection-timeout)
+
+Example usage:
+
+```ts
+const distribution = new CloudFrontWebDistribution(this, 'MyDistribution', {
+    originConfigs: [
+        {
+            ...,
+            connectionAttempts: 3,
+            connectionTimeout: cdk.Duration.seconds(10),
+        }
+    ]
+});
+```
diff --git a/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts
index ab5eda0eb0834..f6c9c0fe89019 100644
--- a/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts
+++ b/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts
@@ -140,15 +140,31 @@ export interface LoggingConfiguration {
  * One or the other must be passed, and it is invalid to pass both in the same SourceConfiguration.
  */
 export interface SourceConfiguration {
+  /**
+   * The number of times that CloudFront attempts to connect to the origin.
+   * You can specify 1, 2, or 3 as the number of attempts.
+   *
+   * @default 3
+   */
+  readonly connectionAttempts?: number;
+
+  /**
+   * The number of seconds that CloudFront waits when trying to establish a connection to the origin.
+   * You can specify a number of seconds between 1 and 10 (inclusive).
+   *
+   * @default cdk.Duration.seconds(10)
+   */
+  readonly connectionTimeout?: cdk.Duration;
+
   /**
    * An s3 origin source - if you're using s3 for your assets
    */
-  readonly s3OriginSource?: S3OriginConfig
+  readonly s3OriginSource?: S3OriginConfig;
 
   /**
    * A custom origin source - for all non-s3 sources.
    */
-  readonly customOriginSource?: CustomOriginConfig,
+  readonly customOriginSource?: CustomOriginConfig;
 
   /**
    * The behaviors associated with this source.
@@ -161,7 +177,7 @@ export interface SourceConfiguration {
    *
    * @default /
    */
-  readonly originPath?: string,
+  readonly originPath?: string;
 
   /**
    * Any additional headers to pass to the origin
@@ -771,6 +787,16 @@ export class CloudFrontWebDistribution extends cdk.Construct implements IDistrib
         }
       }
 
+      const connectionAttempts = originConfig.connectionAttempts ?? 3;
+      if (connectionAttempts < 1 || 3 < connectionAttempts || !Number.isInteger(connectionAttempts)) {
+        throw new Error('connectionAttempts: You can specify 1, 2, or 3 as the number of attempts.');
+      }
+
+      const connectionTimeout = (originConfig.connectionTimeout || cdk.Duration.seconds(10)).toSeconds();
+      if (connectionTimeout < 1 || 10 < connectionTimeout || !Number.isInteger(connectionTimeout)) {
+        throw new Error('connectionTimeout: You can specify a number of seconds between 1 and 10 (inclusive).');
+      }
+
       const originProperty: CfnDistribution.OriginProperty = {
         id: originId,
         domainName: originConfig.s3OriginSource
@@ -791,6 +817,8 @@ export class CloudFrontWebDistribution extends cdk.Construct implements IDistrib
             originSslProtocols: originConfig.customOriginSource.allowedOriginSSLVersions || [OriginSslPolicy.TLS_V1_2],
           }
           : undefined,
+        connectionAttempts,
+        connectionTimeout,
       };
 
       for (const behavior of originConfig.behaviors) {
diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-bucket-logging.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-bucket-logging.expected.json
index 70780cca41033..36a334898a57f 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-bucket-logging.expected.json
+++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-bucket-logging.expected.json
@@ -44,6 +44,8 @@
           },
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "CustomOriginConfig": {
                 "HTTPPort": 80,
                 "HTTPSPort": 443,
@@ -114,6 +116,8 @@
           },
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "CustomOriginConfig": {
                 "HTTPPort": 80,
                 "HTTPSPort": 443,
diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-custom-s3.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-custom-s3.expected.json
index 954b13e1edc67..7699faf6df792 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-custom-s3.expected.json
+++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-custom-s3.expected.json
@@ -72,6 +72,8 @@
           "IPV6Enabled": true,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "CustomOriginConfig": {
                 "HTTPPort": 80,
                 "HTTPSPort": 443,
diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-custom.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-custom.expected.json
index 92071bb9ef237..5943d0d822d3c 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-custom.expected.json
+++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-custom.expected.json
@@ -29,6 +29,8 @@
           "IPV6Enabled": true,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "CustomOriginConfig": {
                 "HTTPPort": 80,
                 "HTTPSPort": 443,
diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-empty-root.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-empty-root.expected.json
index a5298a4968004..9b20011d6a9a1 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-empty-root.expected.json
+++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-empty-root.expected.json
@@ -29,6 +29,8 @@
           "IPV6Enabled": true,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "CustomOriginConfig": {
                 "HTTPPort": 80,
                 "HTTPSPort": 443,
diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-geo-restrictions.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-geo-restrictions.expected.json
index 25ae7addfd317..9307879bca924 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-geo-restrictions.expected.json
+++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-geo-restrictions.expected.json
@@ -34,6 +34,8 @@
           "IPV6Enabled": true,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "DomainName": {
                 "Fn::GetAtt": [
                   "Bucket83908E77",
diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-ipv6-disabled.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-ipv6-disabled.expected.json
index 5ffa5da872430..30bc881df48f3 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-ipv6-disabled.expected.json
+++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-ipv6-disabled.expected.json
@@ -34,6 +34,8 @@
           "IPV6Enabled": false,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "DomainName": {
                 "Fn::GetAtt": [
                   "Bucket83908E77",
diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-lambda-association.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-lambda-association.expected.json
index cce226bd3a090..eb45a5dd3192d 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-lambda-association.expected.json
+++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-lambda-association.expected.json
@@ -107,6 +107,8 @@
           "IPV6Enabled": true,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "DomainName": {
                 "Fn::GetAtt": [
                   "Bucket83908E77",
diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-s3.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-s3.expected.json
index b1ddafe584cf3..8f6357e72272e 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-s3.expected.json
+++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-s3.expected.json
@@ -102,6 +102,8 @@
           "IPV6Enabled": true,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "DomainName": {
                 "Fn::GetAtt": [
                   "Bucket83908E77",
diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-security-policy.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-security-policy.expected.json
index 861887bd54f48..068f2e3492856 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-security-policy.expected.json
+++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-security-policy.expected.json
@@ -32,6 +32,8 @@
           "IPV6Enabled": true,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "CustomOriginConfig": {
                 "HTTPPort": 80,
                 "HTTPSPort": 443,
diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront.expected.json
index a5f3615733d82..acadf8baf6866 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront.expected.json
+++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront.expected.json
@@ -34,6 +34,8 @@
           "IPV6Enabled": true,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "DomainName": {
                 "Fn::GetAtt": [
                   "Bucket83908E77",
diff --git a/packages/@aws-cdk/aws-cloudfront/test/test.basic.ts b/packages/@aws-cdk/aws-cloudfront/test/test.basic.ts
index 39366da64117d..6aeeea31c4cf9 100644
--- a/packages/@aws-cdk/aws-cloudfront/test/test.basic.ts
+++ b/packages/@aws-cdk/aws-cloudfront/test/test.basic.ts
@@ -82,6 +82,8 @@ export = {
                         'TLSv1.2',
                       ],
                     },
+                    'ConnectionAttempts': 3,
+                    'ConnectionTimeout': 10,
                     'DomainName': 'myorigin.com',
                     'Id': 'origin1',
                     'OriginCustomHeaders': [
@@ -139,6 +141,8 @@ export = {
               'DefaultRootObject': 'index.html',
               'Origins': [
                 {
+                  'ConnectionAttempts': 3,
+                  'ConnectionTimeout': 10,
                   'DomainName': {
                     'Fn::GetAtt': [
                       'Bucket83908E77',
@@ -215,6 +219,8 @@ export = {
               'DefaultRootObject': 'index.html',
               'Origins': [
                 {
+                  'ConnectionAttempts': 3,
+                  'ConnectionTimeout': 10,
                   'DomainName': {
                     'Fn::GetAtt': [
                       'Bucket83908E77',
@@ -294,6 +300,8 @@ export = {
               'DefaultRootObject': 'index.html',
               'Origins': [
                 {
+                  'ConnectionAttempts': 3,
+                  'ConnectionTimeout': 10,
                   'DomainName': {
                     'Fn::GetAtt': [
                       'Bucket83908E77',
@@ -370,6 +378,8 @@ export = {
               'DefaultRootObject': 'index.html',
               'Origins': [
                 {
+                  'ConnectionAttempts': 3,
+                  'ConnectionTimeout': 10,
                   'DomainName': {
                     'Fn::GetAtt': [
                       'Bucket83908E77',
@@ -905,6 +915,8 @@ export = {
                   'DefaultRootObject': 'index.html',
                   'Origins': [
                     {
+                      'ConnectionAttempts': 3,
+                      'ConnectionTimeout': 10,
                       'DomainName': {
                         'Fn::GetAtt': [
                           'Bucket83908E77',
@@ -979,6 +991,8 @@ export = {
                   'DefaultRootObject': 'index.html',
                   'Origins': [
                     {
+                      'ConnectionAttempts': 3,
+                      'ConnectionTimeout': 10,
                       'DomainName': {
                         'Fn::GetAtt': [
                           'Bucket83908E77',
@@ -1053,4 +1067,167 @@ export = {
       },
     },
   },
+
+  'Connection behaviors between CloudFront and your origin': {
+    'success': {
+      'connectionAttempts = 1'(test: Test) {
+        const stack = new cdk.Stack();
+        test.doesNotThrow(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionAttempts: 1,
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionAttempts: You can specify 1, 2, or 3 as the number of attempts.');
+        test.done();
+      },
+      '3 = connectionAttempts'(test: Test) {
+        const stack = new cdk.Stack();
+        test.doesNotThrow(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionAttempts: 3,
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionAttempts: You can specify 1, 2, or 3 as the number of attempts.');
+        test.done();
+      },
+      'connectionTimeout = 1'(test: Test) {
+        const stack = new cdk.Stack();
+        test.doesNotThrow(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionTimeout: cdk.Duration.seconds(1),
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionTimeout: You can specify a number of seconds between 1 and 10 (inclusive).');
+        test.done();
+      },
+      '10 = connectionTimeout'(test: Test) {
+        const stack = new cdk.Stack();
+        test.doesNotThrow(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionTimeout: cdk.Duration.seconds(10),
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionTimeout: You can specify a number of seconds between 1 and 10 (inclusive).');
+        test.done();
+      },
+    },
+    'errors': {
+      'connectionAttempts = 1.1'(test: Test) {
+        const stack = new cdk.Stack();
+        test.throws(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionAttempts: 1.1,
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionAttempts: You can specify 1, 2, or 3 as the number of attempts.');
+        test.done();
+      },
+      'connectionAttempts = -1'(test: Test) {
+        const stack = new cdk.Stack();
+        test.throws(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionAttempts: -1,
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionAttempts: You can specify 1, 2, or 3 as the number of attempts.');
+        test.done();
+      },
+      'connectionAttempts < 1'(test: Test) {
+        const stack = new cdk.Stack();
+        test.throws(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionAttempts: 0,
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionAttempts: You can specify 1, 2, or 3 as the number of attempts.');
+        test.done();
+      },
+      '3 < connectionAttempts'(test: Test) {
+        const stack = new cdk.Stack();
+        test.throws(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionAttempts: 4,
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionAttempts: You can specify 1, 2, or 3 as the number of attempts.');
+        test.done();
+      },
+      'connectionTimeout = 1.1'(test: Test) {
+        const stack = new cdk.Stack();
+        test.throws(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionTimeout: cdk.Duration.seconds(1.1),
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionTimeout: You can specify a number of seconds between 1 and 10 (inclusive).');
+        test.done();
+      },
+      'connectionTimeout = -1'(test: Test) {
+        const stack = new cdk.Stack();
+        test.throws(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionTimeout: cdk.Duration.seconds(-1),
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionTimeout: You can specify a number of seconds between 1 and 10 (inclusive).');
+        test.done();
+      },
+      'connectionTimeout < 1'(test: Test) {
+        const stack = new cdk.Stack();
+        test.throws(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionTimeout: cdk.Duration.seconds(0),
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionTimeout: You can specify a number of seconds between 1 and 10 (inclusive).');
+        test.done();
+      },
+      '10 < connectionTimeout'(test: Test) {
+        const stack = new cdk.Stack();
+        test.throws(() => {
+          new CloudFrontWebDistribution(stack, 'Distribution', {
+            originConfigs: [{
+              behaviors: [{ isDefaultBehavior: true }],
+              connectionTimeout: cdk.Duration.seconds(11),
+              customOriginSource: { domainName: 'myorigin.com' },
+            }],
+          });
+        }, 'connectionTimeout: You can specify a number of seconds between 1 and 10 (inclusive).');
+        test.done();
+      },
+    },
+  },
 };
diff --git a/packages/@aws-cdk/aws-route53-targets/test/integ.cloudfront-alias-target.expected.json b/packages/@aws-cdk/aws-route53-targets/test/integ.cloudfront-alias-target.expected.json
index 76bd0647bca15..7a91ba087aaab 100644
--- a/packages/@aws-cdk/aws-route53-targets/test/integ.cloudfront-alias-target.expected.json
+++ b/packages/@aws-cdk/aws-route53-targets/test/integ.cloudfront-alias-target.expected.json
@@ -59,6 +59,8 @@
           "IPV6Enabled": true,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "DomainName": {
                 "Fn::GetAtt": [
                   "Bucket83908E77",
diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json
index e5e0dfe714f00..7563384f9b9f0 100644
--- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json
+++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json
@@ -34,6 +34,8 @@
           "IPV6Enabled": true,
           "Origins": [
             {
+              "ConnectionAttempts": 3,
+              "ConnectionTimeout": 10,
               "DomainName": {
                 "Fn::GetAtt": [
                   "Destination3E3DC043D",