diff --git a/README.md b/README.md
index a634edd8a..e1435c422 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ AWS Solutions Constructs and the AWS CDK are independent teams and have differen
The AWS Solutions Constructs library is organized into several modules. They are named like this:
* __aws-xxx__: well architected pattern package for the indicated services. This package will contain constructs that contain multiple AWS CDK service modules to configure the given pattern.
-* __xxx__: packages that don't start "aws-" are core modules that are used to configure best practice defaults for services used within the pattern library.
+* __xxx__: packages that don't start "aws-" are core modules that are used to configure best practice defaults for services used within the pattern library. They are not intended to be accessed directly.
## Module Contents
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-fargate/package.json b/source/patterns/@aws-solutions-constructs/aws-alb-fargate/package.json
index d65d5d0bc..2cde88a59 100644
--- a/source/patterns/@aws-solutions-constructs/aws-alb-fargate/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-fargate/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/package.json
index 0e3a3d98d..5b871e7cd 100644
--- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-dynamodb/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-dynamodb/package.json
index a9d48f507..4543f580e 100644
--- a/source/patterns/@aws-solutions-constructs/aws-apigateway-dynamodb/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-dynamodb/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/package.json
index 1e5944310..a1899beb3 100755
--- a/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-kinesisstreams/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-kinesisstreams/package.json
index 9c1d15974..4d3dcbe6d 100644
--- a/source/patterns/@aws-solutions-constructs/aws-apigateway-kinesisstreams/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-kinesisstreams/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-lambda/package.json
index e7a457d36..1294e5980 100644
--- a/source/patterns/@aws-solutions-constructs/aws-apigateway-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-lambda/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/package.json
index 89b79b055..29cbab486 100644
--- a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/package.json
index bfe7d6b0c..4ffb0dba8 100644
--- a/source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/package.json
index 437143042..b329e07c6 100644
--- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/package.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/package.json
index 2218463a9..51deaaef2 100644
--- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/package.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/package.json
index 84b4c1e2f..9525ae34e 100644
--- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json
index 5ded170d5..dfad84b6d 100644
--- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-cognito-apigateway-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-cognito-apigateway-lambda/package.json
index 9eb15b3f1..50c1550a2 100644
--- a/source/patterns/@aws-solutions-constructs/aws-cognito-apigateway-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-cognito-apigateway-lambda/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/package.json b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/package.json
index 9f77d86d0..c73aed650 100644
--- a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda/package.json
index cad902f5a..26474d128 100644
--- a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/package.json
index 9026addd8..2259e328b 100644
--- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisstreams/package.json b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisstreams/package.json
index aa024e1ea..188591f95 100644
--- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisstreams/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisstreams/package.json
@@ -13,7 +13,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-eventbridge-lambda/package.json
index 2851081cb..9d952c96a 100644
--- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-lambda/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-sns/package.json b/source/patterns/@aws-solutions-constructs/aws-eventbridge-sns/package.json
index 256f3b240..956a31d0f 100644
--- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-sns/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-sns/package.json
@@ -13,7 +13,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-eventbridge-sqs/package.json
index 44a803d07..d8bc6bfbb 100644
--- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-sqs/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-sqs/package.json
@@ -13,7 +13,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-stepfunctions/package.json b/source/patterns/@aws-solutions-constructs/aws-eventbridge-stepfunctions/package.json
index 522538a62..ac2c7d171 100644
--- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-stepfunctions/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-stepfunctions/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-dynamodb/package.json b/source/patterns/@aws-solutions-constructs/aws-fargate-dynamodb/package.json
index cd25a4bd1..9fa3cc68b 100644
--- a/source/patterns/@aws-solutions-constructs/aws-fargate-dynamodb/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-fargate-dynamodb/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-eventbridge/package.json b/source/patterns/@aws-solutions-constructs/aws-fargate-eventbridge/package.json
index 30f77cb7c..d715fff51 100644
--- a/source/patterns/@aws-solutions-constructs/aws-fargate-eventbridge/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-fargate-eventbridge/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-kinesisstreams/package.json b/source/patterns/@aws-solutions-constructs/aws-fargate-kinesisstreams/package.json
index e8cf656b9..8ce4f820d 100644
--- a/source/patterns/@aws-solutions-constructs/aws-fargate-kinesisstreams/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-fargate-kinesisstreams/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/package.json b/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/package.json
index eb9556ce6..a0e587f93 100644
--- a/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-fargate-s3/package.json
index c5a349393..9d70034a3 100644
--- a/source/patterns/@aws-solutions-constructs/aws-fargate-s3/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-fargate-s3/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-secretsmanager/package.json b/source/patterns/@aws-solutions-constructs/aws-fargate-secretsmanager/package.json
index 4326b098d..625d78498 100644
--- a/source/patterns/@aws-solutions-constructs/aws-fargate-secretsmanager/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-fargate-secretsmanager/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-sns/package.json b/source/patterns/@aws-solutions-constructs/aws-fargate-sns/package.json
index 09dac2f63..4b572d83e 100644
--- a/source/patterns/@aws-solutions-constructs/aws-fargate-sns/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-fargate-sns/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-fargate-sqs/package.json
index 3c923ce82..843985247 100644
--- a/source/patterns/@aws-solutions-constructs/aws-fargate-sqs/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-fargate-sqs/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/package.json b/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/package.json
index 0aae4e452..ee94cf157 100644
--- a/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-stepfunctions/package.json b/source/patterns/@aws-solutions-constructs/aws-fargate-stepfunctions/package.json
index 3785f525d..bd2763629 100644
--- a/source/patterns/@aws-solutions-constructs/aws-fargate-stepfunctions/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-fargate-stepfunctions/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/package.json
index 4866d3090..131ee75b0 100644
--- a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisstreams/package.json b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisstreams/package.json
index 92848fa5a..fa6fc3eaf 100644
--- a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisstreams/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisstreams/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-lambda-dynamodb/package.json b/source/patterns/@aws-solutions-constructs/aws-iot-lambda-dynamodb/package.json
index 2352f07e0..fbd9ce412 100644
--- a/source/patterns/@aws-solutions-constructs/aws-iot-lambda-dynamodb/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-iot-lambda-dynamodb/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-iot-lambda/package.json
index 0b8b03760..9d98a9f16 100644
--- a/source/patterns/@aws-solutions-constructs/aws-iot-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-iot-lambda/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-iot-s3/package.json
index cf6345671..9fe0b15c5 100644
--- a/source/patterns/@aws-solutions-constructs/aws-iot-s3/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-iot-s3/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-iot-sqs/package.json
index bec6499a1..6bcc72d0f 100644
--- a/source/patterns/@aws-solutions-constructs/aws-iot-sqs/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-iot-sqs/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/package.json b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/package.json
index 6e3f2c3d6..21cde97d4 100644
--- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/package.json
index 147b7e1d6..526d592df 100644
--- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/package.json b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/package.json
index 7082dcd7d..700ebea9e 100644
--- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/package.json
index e515a4b9e..e38d88375 100644
--- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-lambda/package.json
index 13f758a58..18d769ced 100644
--- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-lambda/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-dynamodb/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-dynamodb/package.json
index 299cf549e..24bb6fa66 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-dynamodb/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-dynamodb/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticachememcached/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticachememcached/package.json
index 2d0aa801f..9097c462d 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticachememcached/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticachememcached/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/package.json
index 230a89131..89b682cf6 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/package.json
index 67133db65..a2a6a2c17 100755
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/.eslintignore b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/.eslintignore
new file mode 100644
index 000000000..340869a08
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/.eslintignore
@@ -0,0 +1,5 @@
+lib/*.js
+test/*.js
+*.d.ts
+coverage
+test/lambda/index.js
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/.gitignore b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/.gitignore
new file mode 100644
index 000000000..6773cabd2
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/.gitignore
@@ -0,0 +1,15 @@
+lib/*.js
+test/*.js
+*.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-lambda-kendra/.npmignore b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/.npmignore
new file mode 100644
index 000000000..f66791629
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/.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-lambda-kendra/README.md b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/README.md
new file mode 100644
index 000000000..8cae3fe5d
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/README.md
@@ -0,0 +1,132 @@
+# aws-lambda-kendra 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_lambda_kendra` |
+| ![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript | `@aws-solutions-constructs/aws-lambda-kendra` |
+| ![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java | `software.amazon.awsconstructs.services.lambdakendra` |
+
+## Overview
+
+This AWS Solutions Construct implements an AWS Lambda function and Amazon Kendra index with the least privileged permissions.
+
+Here is a minimal deployable pattern definition:
+
+Typescript
+
+```typescript
+import { Construct } from 'constructs';
+import { Stack, StackProps, Aws } from 'aws-cdk-lib';
+import { LambdaToKendra } from '@aws-solutions-constructs/aws-lambda-kendra';
+import * as lambda from "aws-cdk-lib/aws-lambda";
+import * as s3 from "aws-cdk-lib/aws-s3";
+
+const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+};
+
+new LambdaToKendra(this, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ kendraIndexProps: {},
+ kendraDataSourceProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: 'your-bucket-name',
+ }
+ }
+ ],
+});
+```
+
+Python
+
+```python
+TBD
+```
+
+Java
+
+```java
+TBD
+```
+
+## Pattern Construct Props
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| existingLambdaObj? | [`lambda.Function`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.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/v2/docs/aws-cdk-lib.aws_lambda.FunctionProps.html) | User provided props to override the default props for the Lambda function. |
+| kendraIndexProps? | [`kendra.CfnIndexProps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_kendra.CfnIndex.html) | Optional user provided props to override the default props for the Kendra index. Providing both these and existingKendraIndexObj is an error. |
+| kendraDataSourcesProps | [`CfnDataSourceProps[]`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_kendra.CfnDataSource.html) | A list of data sources that will provide data to the Kendra index. *At least 1 must be specified*. We will do majority of processing for some data sources (S3 crawler initially), but for others the props must be complete (e.g. proper roleArn, etc.) |
+| indexPermissions? | `string[]` | Optional - index permissions to grant to the Lambda function. One or more of the following may be specified: `Read`, `SubmitFeedback` and `Write`. Default is `["Read", "SubmitFeedback"]`. Read is all the operations IAM defines as Read and List. SubmitFeedback is only the SubmitFeedback action. Write is all the operations IAM defines as Write and Tag. This functionality may be overridden by providing a specific role arn in lambdaFunctionProps |
+| existingKendraIndexObj? | [`kendra.cfnIndex`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_kendra.CfnIndex.html) | An existing Kendra index to which the Lambda function will be granted access. Supplying along with kendraIndexProps or kendraDataSourceProps will throw an error. |
+| existingVpc? | [`ec2.IVpc`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.IVpc.html) | An optional, existing VPC into which this pattern should be deployed. When deployed in a VPC, the Lambda function will use ENIs in the VPC to access network resources. If an existing VPC is provided, the `deployVpc` property cannot be `true`. This uses `ec2.IVpc` to allow clients to supply VPCs that exist outside the stack using the [`ec2.Vpc.fromLookup()`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.Vpc.html#static-fromwbrlookupscope-id-options) method. |
+| vpcProps? | [`ec2.VpcProps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.VpcProps.html) | Optional user provided properties to override the default properties for the new VPC. `enableDnsHostnames`, `enableDnsSupport`, `natGateways` and `subnetConfiguration` are set by the pattern, so any values for those properties supplied here will be overridden. If `deployVpc` is not `true` then this property will be ignored. |
+| deployVpc? | `boolean` | Whether to create a new VPC based on `vpcProps` into which to deploy this pattern. Setting this to true will deploy the minimal, most private VPC to run the pattern:
\- One isolated subnet in each Availability Zone used by the CDK program
\- `enableDnsHostnames` and `enableDnsSupport` will both be set to true
If this property is `true` then `existingVpc` cannot be specified. Defaults to `false`. |
+
+## Pattern Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| lambdaFunction | [`lambda.Function`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html) | Returns an instance of `lambda.Function` managed by the construct |
+| kendraIndex | [`kendra.cfnIndex`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_kendra.CfnIndex.html) | Returns an instance of `kendra.cfnIndex` managed by the construct |
+| kendraDataSources | DataSourceProperties[] (this interface is defined by Solutions Constructs and described below) | A list of data sources created for this construct/index, each in an object that includes the role for that data source. |
+| lambdaRole | [`iam.Role`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.Role.html) | The role assumed by the Lambda function |
+| vpc? | [`ec2.IVpc`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.IVpc.html) | Returns an interface on the VPC used by the pattern (if any). This may be a VPC created by the pattern or the VPC supplied to the pattern constructor. |
+
+interface DataSourceProperties {
+ role?: [`iam.Role`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.Role.html),
+ source: | [`CfnDataSource`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_kendra.CfnDataSource.html)
+}
+## Lambda Function
+
+This pattern requires a lambda function that can access a Kendra index.
+
+## Default settings
+
+Out of the box implementation of the Construct without any overrides will set the following defaults:
+
+### AWS Lambda Function
+
+- Configure limited privilege access IAM role for Lambda function
+- Enable reusing connections with Keep-Alive for Node.js Lambda function
+- Enable X-Ray Tracing
+- Set Environment Variables
+ - (default) KENDRA_INDEX_ID
+ - AWS_NODEJS_CONNECTION_REUSE_ENABLED
+
+### Amazon Kendra Index
+
+- Creates Amazon Kendra endpoint in VPC if appropriate
+- Defaults to DEVELOPER_EDITION
+
+**Amazon Kendra DataSources**
+
+- Sets up correct IAM roles to access data for:
+ - S3 data sources
+ - Which others should we support in MLP? https://docs.aws.amazon.com/kendra/latest/dg/iam-roles.html
+- Adds each data source to Kendra index
+
+## Architecture
+
+![Architecture Diagram](architecture.png)
+
+* * *
+
+© Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/achitecture.png b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/achitecture.png
new file mode 100644
index 000000000..8d4393b51
Binary files /dev/null and b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/achitecture.png differ
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/lib/index.ts
new file mode 100644
index 000000000..e44f13a92
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/lib/index.ts
@@ -0,0 +1,204 @@
+/**
+ * Copyright 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 * as defaults from "@aws-solutions-constructs/core";
+import * as lambda from "aws-cdk-lib/aws-lambda";
+import * as iam from "aws-cdk-lib/aws-iam";
+import * as kendra from "aws-cdk-lib/aws-kendra";
+import * as ec2 from "aws-cdk-lib/aws-ec2";
+import { Construct } from "constructs";
+
+/**
+ * @summary The properties for the LambdaToKendra class.
+ */
+export interface LambdaToKendraProps {
+ /**
+ *
+ *
+ * @default - Optional user provided props to override the default props for the Kendra index. Is this required?
+ */
+ readonly kendraIndexProps?: kendra.CfnIndexProps;
+ /**
+ * A list of data sources that will provide data to the Kendra index. ?At least 1 must be specified. We will do majority of
+ * processing for some data sources (S3 crawler initially), but for others the props must be complete (e.g. proper roleArn, etc.)
+ *
+ * @default - empty list (no data sources)
+ */
+ readonly kendraDataSourcesProps: Array;
+ /**
+ * Optional - index permissions to grant to the Lambda function. One or more of the following
+ * may be specified: `Read`, `SubmitFeedback` and `Write`. Default is `["Read", "SubmitFeedback"]`. Read is
+ * all the operations IAM defines as Read and List. SubmitFeedback is only the SubmitFeedback action. Write is all the
+ * operations IAM defines as Write and Tag. This functionality may be overridden by providing a specific role arn in lambdaFunctionProps
+ *
+ * @default - ["Read", "SubmitFeedback"]
+ */
+ readonly indexPermissions?: string[];
+ /**
+ * Existing instance of a Kendra Index. Providing both this and kendraIndexProps will cause an error.
+ *
+ * @default - None
+ */
+ readonly existingKendraIndexObj?: kendra.CfnIndex;
+ /**
+ * 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 properties are used.
+ */
+ readonly lambdaFunctionProps?: lambda.FunctionProps;
+ /**
+ * An existing VPC for the construct to use (construct will NOT create a new VPC in this case)
+ */
+ readonly existingVpc?: ec2.IVpc;
+ /**
+ * Properties to override default properties if deployVpc is true
+ */
+ readonly vpcProps?: ec2.VpcProps;
+ /**
+ * Whether to deploy a new VPC
+ *
+ * @default - false
+ */
+ readonly deployVpc?: boolean;
+ /**
+ * Optional Name for the Lambda function environment variable set to the index id for the Kendra index.
+ *
+ * @default - KENDRA_INDEX_ID
+ */
+ readonly indexIdEnvironmentVariableName?: string;
+}
+
+/**
+ * @summary The LambdaToKendra class.
+ */
+export class LambdaToKendra extends Construct {
+ public readonly lambdaFunction: lambda.Function;
+ public readonly vpc?: ec2.IVpc;
+ public readonly kendraIndex: kendra.CfnIndex;
+ public readonly kendraDataSources: kendra.CfnDataSource[];
+
+ /**
+ * @summary Constructs a new instance of the LambdaToKendra class.
+ * @param {cdk.App} scope - represents the scope for all the resources.
+ * @param {string} id - this is a a scope-unique id.
+ * @param {LambdaToKendraProps} props - user provided props for the construct.
+ * @since 1.120.0
+ * @access public
+ */
+ constructor(scope: Construct, id: string, props: LambdaToKendraProps) {
+ super(scope, id);
+ defaults.CheckProps(props);
+
+ if (props.kendraIndexProps && props.existingKendraIndexObj) {
+ throw new Error('You may not provide both kendraIndexProps and existingKendraIndexObj');
+ }
+
+ if (props.kendraIndexProps && props.kendraDataSourcesProps) {
+ throw new Error('You may not provide both kendraDataSourcesProps and existingKendraIndexObj');
+ }
+
+ if (props.deployVpc || props.existingVpc) {
+ this.vpc = defaults.buildVpc(scope, {
+ defaultVpcProps: defaults.DefaultIsolatedVpcProps(),
+ existingVpc: props.existingVpc,
+ userVpcProps: props.vpcProps,
+ constructVpcProps: {
+ enableDnsHostnames: true,
+ enableDnsSupport: true,
+ },
+ });
+
+ defaults.AddAwsServiceEndpoint(scope, this.vpc, defaults.ServiceEndpointTypes.KENDRA);
+ }
+
+ // Setup the Lambda function
+ this.lambdaFunction = defaults.buildLambdaFunction(this, {
+ existingLambdaObj: props.existingLambdaObj,
+ lambdaFunctionProps: props.lambdaFunctionProps,
+ vpc: this.vpc,
+ });
+
+ this.kendraIndex = defaults.buildKendraIndex(this, id, {
+ kendraIndexProps: props.kendraIndexProps,
+ existingIndexObj: props.existingKendraIndexObj
+ });
+
+ this.kendraDataSources = defaults.AddMultipleKendraDataSources(this, id, this.kendraIndex, props.kendraDataSourcesProps);
+
+ // Update Lambda function IAM policy with correct privileges to Kendra index
+ const normalizedPermissions = props.indexPermissions ? defaults.normalizeKendraPermissions(props.indexPermissions) : undefined;
+ if (!normalizedPermissions || normalizedPermissions.includes("READ")) {
+ // Add policy with query permissions
+ this.lambdaFunction.addToRolePolicy(
+ new iam.PolicyStatement({
+ actions: [
+ "kendra:Query",
+ "kendra:Describe*",
+ "kendra:Get*",
+ "kendra:BatchGet*",
+ "kendra:List*",
+ "kendra:Retrieve"],
+ resources: [this.kendraIndex.attrArn]
+ })
+ );
+ }
+
+ if (!normalizedPermissions || normalizedPermissions.includes("SUBMITFEEDBACK")) {
+ // Add policy with query permissions
+ this.lambdaFunction.addToRolePolicy(
+ new iam.PolicyStatement({
+ actions: [
+ "kendra:SubmitFeedback"],
+ resources: [this.kendraIndex.attrArn]
+ })
+ );
+ }
+
+ if (normalizedPermissions?.includes("WRITE")) {
+ // Add policy with query permissions
+ this.lambdaFunction.addToRolePolicy(
+ new iam.PolicyStatement({
+ actions: [
+ "kendra:Associate*",
+ "kendra:BatchPut*",
+ "kendra:Clear",
+ "kendra:Create*",
+ "kendra:Delete*",
+ "kendra:Disassociate*",
+ "kendra:Put*",
+ "kendra:Update*",
+ "kendra:Start*",
+ "kendra:Submit*",
+ "kendra:Stop*",
+ "kendra:TagResource",
+ "kendra:UntagResource"
+ ],
+ resources: [this.kendraIndex.attrArn]
+ })
+ );
+ }
+
+ // Configure environment variables
+ const indexIdEnvironmentVariableName = props.indexIdEnvironmentVariableName || 'KENDRA_INDEX_ID';
+ this.lambdaFunction.addEnvironment(indexIdEnvironmentVariableName, this.kendraIndex.attrId);
+
+ }
+
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/package.json
new file mode 100644
index 000000000..ed75cea81
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/package.json
@@ -0,0 +1,96 @@
+{
+ "name": "@aws-solutions-constructs/aws-lambda-kendra",
+ "version": "0.0.0",
+ "description": "CDK constructs for defining an interaction between an AWS Lambda function and an existing Amazon Kinesis Firehose Delivery Stream.",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/awslabs/aws-solutions-constructs.git",
+ "directory": "source/patterns/@aws-solutions-constructs/aws-lambda-kendra"
+ },
+ "author": "Amazon Web Services (https://aws.amazon.com)",
+ "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-assert": "cdk-integ-assert-v2",
+ "jsii": "jsii",
+ "jsii-pacmak": "jsii-pacmak",
+ "build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
+ "integ-no-clean": "cdk-integ --no-clean",
+ "snapshot-update": "npm run jsii && npm test -- -u && npm run integ-assert"
+ },
+ "jsii": {
+ "outdir": "dist",
+ "targets": {
+ "java": {
+ "package": "software.amazon.awsconstructs.services.lambdakendra",
+ "maven": {
+ "groupId": "software.amazon.awsconstructs",
+ "artifactId": "lambdakendra"
+ }
+ },
+ "dotnet": {
+ "namespace": "Amazon.SolutionsConstructs.AWS.LambdaKendra",
+ "packageId": "Amazon.SolutionsConstructs.AWS.Lambdakendra",
+ "signAssembly": true,
+ "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png"
+ },
+ "python": {
+ "distName": "aws-solutions-constructs.aws-lambda-kendra",
+ "module": "aws_solutions_constructs.aws_lambda_kendra"
+ }
+ }
+ },
+ "dependencies": {
+ "@aws-solutions-constructs/core": "0.0.0"
+ },
+ "devDependencies": {
+ "@types/jest": "^27.4.0",
+ "@types/node": "^10.3.0",
+ "aws-cdk-lib": "2.82.0",
+ "@aws-solutions-constructs/core": "0.0.0",
+ "constructs": "^10.0.0"
+ },
+ "jest": {
+ "moduleFileExtensions": [
+ "js"
+ ],
+ "coverageReporters": [
+ "text",
+ [
+ "lcov",
+ {
+ "projectRoot": "../../../../"
+ }
+ ]
+ ]
+ },
+ "peerDependencies": {
+ "@aws-solutions-constructs/core": "0.0.0",
+ "aws-cdk-lib": "^2.82.0",
+ "constructs": "^10.0.0"
+ },
+ "keywords": [
+ "aws",
+ "cdk",
+ "awscdk",
+ "AWS Solutions Constructs",
+ "Amazon Kendra",
+ "AWS Lambda"
+ ],
+ "bugs": {
+ "url": "https://github.com/awslabs/aws-solutions-constructs/issues"
+ },
+ "homepage": "https://github.com/awslabs/aws-solutions-constructs#readme",
+ "directories": {
+ "lib": "lib",
+ "test": "test"
+ }
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.minimal-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.minimal-arguments.expected.json
new file mode 100644
index 000000000..d423282c9
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.minimal-arguments.expected.json
@@ -0,0 +1,779 @@
+{
+ "Description": "Integration Test for aws-lambda-kendra",
+ "Resources": {
+ "scrapBucketB11863B7": {
+ "Type": "AWS::S3::Bucket",
+ "Properties": {
+ "BucketEncryption": {
+ "ServerSideEncryptionConfiguration": [
+ {
+ "ServerSideEncryptionByDefault": {
+ "SSEAlgorithm": "AES256"
+ }
+ }
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "aws-cdk:auto-delete-objects",
+ "Value": "true"
+ }
+ ],
+ "VersioningConfiguration": {
+ "Status": "Enabled"
+ }
+ },
+ "UpdateReplacePolicy": "Delete",
+ "DeletionPolicy": "Delete",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W51",
+ "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation"
+ },
+ {
+ "id": "W35",
+ "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation"
+ },
+ {
+ "id": "W41",
+ "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct"
+ }
+ ]
+ }
+ }
+ },
+ "scrapBucketPolicy189B0607": {
+ "Type": "AWS::S3::BucketPolicy",
+ "Properties": {
+ "Bucket": {
+ "Ref": "scrapBucketB11863B7"
+ },
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "s3:GetBucket*",
+ "s3:List*",
+ "s3:DeleteObject*"
+ ],
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": {
+ "Fn::GetAtt": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092",
+ "Arn"
+ ]
+ }
+ },
+ "Resource": [
+ {
+ "Fn::GetAtt": [
+ "scrapBucketB11863B7",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "scrapBucketB11863B7",
+ "Arn"
+ ]
+ },
+ "/*"
+ ]
+ ]
+ }
+ ]
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": {
+ "Type": "Custom::S3AutoDeleteObjects",
+ "Properties": {
+ "ServiceToken": {
+ "Fn::GetAtt": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F",
+ "Arn"
+ ]
+ },
+ "BucketName": {
+ "Ref": "scrapBucketB11863B7"
+ }
+ },
+ "DependsOn": [
+ "scrapBucketPolicy189B0607"
+ ],
+ "UpdateReplacePolicy": "Delete",
+ "DeletionPolicy": "Delete"
+ },
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "lambda.amazonaws.com"
+ }
+ }
+ ]
+ },
+ "ManagedPolicyArns": [
+ {
+ "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+ }
+ ]
+ }
+ },
+ "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
+ },
+ "S3Key": "350185a1069fa20a23a583e20c77f6844218bd73097902362dc94f1a108f5d89.zip"
+ },
+ "Timeout": 900,
+ "MemorySize": 128,
+ "Handler": "__entrypoint__.handler",
+ "Role": {
+ "Fn::GetAtt": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092",
+ "Arn"
+ ]
+ },
+ "Runtime": "nodejs16.x",
+ "Description": {
+ "Fn::Join": [
+ "",
+ [
+ "Lambda function for auto-deleting objects in ",
+ {
+ "Ref": "scrapBucketB11863B7"
+ },
+ " S3 bucket."
+ ]
+ ]
+ }
+ },
+ "DependsOn": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W58",
+ "reason": "CDK generated custom resource"
+ },
+ {
+ "id": "W89",
+ "reason": "CDK generated custom resource"
+ },
+ {
+ "id": "W92",
+ "reason": "CDK generated custom resource"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentsLambdaFunctionServiceRole73B77FF7": {
+ "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"
+ }
+ ]
+ }
+ },
+ "minimalargumentsLambdaFunctionServiceRoleDefaultPolicy59EC60ED": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "kendra:Query",
+ "kendra:Describe*",
+ "kendra:Get*",
+ "kendra:BatchGet*",
+ "kendra:List*",
+ "kendra:Retrieve"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Arn"
+ ]
+ }
+ },
+ {
+ "Action": "kendra:SubmitFeedback",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "minimalargumentsLambdaFunctionServiceRoleDefaultPolicy59EC60ED",
+ "Roles": [
+ {
+ "Ref": "minimalargumentsLambdaFunctionServiceRole73B77FF7"
+ }
+ ]
+ },
+ "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."
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentsLambdaFunctionC19CFAAC": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
+ },
+ "S3Key": "e0128dd6e376c58a74b37bd6178ded8a5909d414a6891c26547b9778ac25679b.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "minimalargumentsLambdaFunctionServiceRole73B77FF7",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "KENDRA_INDEX_ID": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Id"
+ ]
+ }
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs18.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ }
+ },
+ "DependsOn": [
+ "minimalargumentsLambdaFunctionServiceRoleDefaultPolicy59EC60ED",
+ "minimalargumentsLambdaFunctionServiceRole73B77FF7"
+ ],
+ "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"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentskendraindexroleminimalargumentsB1DBDF7D": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "kendra.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Description": "Allow Kendra index to write CloudWatch Logs",
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "cloudwatch:PutMetricData",
+ "Condition": {
+ "StringEquals": {
+ "cloudwatch:namespace": "AWS/Kendra"
+ }
+ },
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": "logs:CreateLogGroup",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*"
+ ]
+ ]
+ }
+ },
+ {
+ "Action": "logs:DescribeLogGroups",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*"
+ ]
+ ]
+ }
+ },
+ {
+ "Action": [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ "logs:DescribeLogStream"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*:log-stream:*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "AllowLogging"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W11",
+ "reason": "PutMetricData does not allow resource specification, scope is narrowed by the namespace condition. https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazoncloudwatch.html"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentskendraindexminimalarguments5CBDD236": {
+ "Type": "AWS::Kendra::Index",
+ "Properties": {
+ "Edition": "DEVELOPER_EDITION",
+ "Name": {
+ "Fn::Join": [
+ "",
+ [
+ "KendraIndexminimal-arguments-",
+ {
+ "Fn::Select": [
+ 2,
+ {
+ "Fn::Split": [
+ "/",
+ {
+ "Ref": "AWS::StackId"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexroleminimalargumentsB1DBDF7D",
+ "Arn"
+ ]
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W80",
+ "reason": "We consulted the Kendra TFC and they confirmed the default encryption is sufficient for general use cases"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentsdatasourceroleminimalarguments00EAC5006": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "kendra.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Description": "Policy for Kendra S3 Data Source",
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "s3:GetObject",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:s3:::",
+ {
+ "Ref": "scrapBucketB11863B7"
+ },
+ "/*"
+ ]
+ ]
+ }
+ },
+ {
+ "Action": "s3:ListBucket",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:s3:::",
+ {
+ "Ref": "scrapBucketB11863B7"
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "Action": [
+ "kendra:BatchPutDocument",
+ "kendra:BatchDeleteDocument"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "s3CrawlPolicy"
+ }
+ ]
+ }
+ },
+ "minimalargumentsdatasourceminimalarguments05A02FF3B": {
+ "Type": "AWS::Kendra::DataSource",
+ "Properties": {
+ "IndexId": {
+ "Ref": "minimalargumentskendraindexminimalarguments5CBDD236"
+ },
+ "Name": {
+ "Fn::Join": [
+ "",
+ [
+ "s3-datasourceminimal-arguments0-",
+ {
+ "Fn::Select": [
+ 2,
+ {
+ "Fn::Split": [
+ "/",
+ {
+ "Ref": "AWS::StackId"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "Type": "S3",
+ "DataSourceConfiguration": {
+ "S3Configuration": {
+ "BucketName": {
+ "Ref": "scrapBucketB11863B7"
+ }
+ }
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "minimalargumentsdatasourceroleminimalarguments00EAC5006",
+ "Arn"
+ ]
+ }
+ }
+ }
+ },
+ "Mappings": {
+ "DefaultCrNodeVersionMap": {
+ "af-south-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-east-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-northeast-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-northeast-2": {
+ "value": "nodejs16.x"
+ },
+ "ap-northeast-3": {
+ "value": "nodejs16.x"
+ },
+ "ap-south-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-south-2": {
+ "value": "nodejs16.x"
+ },
+ "ap-southeast-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-southeast-2": {
+ "value": "nodejs16.x"
+ },
+ "ap-southeast-3": {
+ "value": "nodejs16.x"
+ },
+ "ca-central-1": {
+ "value": "nodejs16.x"
+ },
+ "cn-north-1": {
+ "value": "nodejs16.x"
+ },
+ "cn-northwest-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-central-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-central-2": {
+ "value": "nodejs16.x"
+ },
+ "eu-north-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-south-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-south-2": {
+ "value": "nodejs16.x"
+ },
+ "eu-west-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-west-2": {
+ "value": "nodejs16.x"
+ },
+ "eu-west-3": {
+ "value": "nodejs16.x"
+ },
+ "me-central-1": {
+ "value": "nodejs16.x"
+ },
+ "me-south-1": {
+ "value": "nodejs16.x"
+ },
+ "sa-east-1": {
+ "value": "nodejs16.x"
+ },
+ "us-east-1": {
+ "value": "nodejs16.x"
+ },
+ "us-east-2": {
+ "value": "nodejs16.x"
+ },
+ "us-gov-east-1": {
+ "value": "nodejs16.x"
+ },
+ "us-gov-west-1": {
+ "value": "nodejs16.x"
+ },
+ "us-iso-east-1": {
+ "value": "nodejs14.x"
+ },
+ "us-iso-west-1": {
+ "value": "nodejs14.x"
+ },
+ "us-isob-east-1": {
+ "value": "nodejs14.x"
+ },
+ "us-west-1": {
+ "value": "nodejs16.x"
+ },
+ "us-west-2": {
+ "value": "nodejs16.x"
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
+ }
+ },
+ "Rules": {
+ "CheckBootstrapVersion": {
+ "Assertions": [
+ {
+ "Assert": {
+ "Fn::Not": [
+ {
+ "Fn::Contains": [
+ [
+ "1",
+ "2",
+ "3",
+ "4",
+ "5"
+ ],
+ {
+ "Ref": "BootstrapVersion"
+ }
+ ]
+ }
+ ]
+ },
+ "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.minimal-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.minimal-arguments.ts
new file mode 100644
index 000000000..0cd2fcb64
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.minimal-arguments.ts
@@ -0,0 +1,44 @@
+/**
+ * Copyright 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 { App, Stack } from "aws-cdk-lib";
+import { LambdaToKendra } from "../lib";
+import * as lambda from 'aws-cdk-lib/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core';
+
+// Setup
+const app = new App();
+const stack = new Stack(app, generateIntegStackName(__filename));
+stack.templateOptions.description = 'Integration Test for aws-lambda-kendra';
+
+const testBucket = defaults.CreateScrapBucket(stack);
+
+new LambdaToKendra(stack, 'minimal-arguments', {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler',
+ },
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucket.bucketName,
+ }
+ }
+ }],
+});
+
+suppressAutoDeleteHandlerWarnings(stack);
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.multiple-sources.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.multiple-sources.expected.json
new file mode 100644
index 000000000..9e715550e
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.multiple-sources.expected.json
@@ -0,0 +1,827 @@
+{
+ "Description": "Integration Test for aws-lambda-kendra",
+ "Resources": {
+ "scrapBucketB11863B7": {
+ "Type": "AWS::S3::Bucket",
+ "Properties": {
+ "BucketEncryption": {
+ "ServerSideEncryptionConfiguration": [
+ {
+ "ServerSideEncryptionByDefault": {
+ "SSEAlgorithm": "AES256"
+ }
+ }
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "aws-cdk:auto-delete-objects",
+ "Value": "true"
+ }
+ ],
+ "VersioningConfiguration": {
+ "Status": "Enabled"
+ }
+ },
+ "UpdateReplacePolicy": "Delete",
+ "DeletionPolicy": "Delete",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W51",
+ "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation"
+ },
+ {
+ "id": "W35",
+ "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation"
+ },
+ {
+ "id": "W41",
+ "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct"
+ }
+ ]
+ }
+ }
+ },
+ "scrapBucketPolicy189B0607": {
+ "Type": "AWS::S3::BucketPolicy",
+ "Properties": {
+ "Bucket": {
+ "Ref": "scrapBucketB11863B7"
+ },
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "s3:GetBucket*",
+ "s3:List*",
+ "s3:DeleteObject*"
+ ],
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": {
+ "Fn::GetAtt": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092",
+ "Arn"
+ ]
+ }
+ },
+ "Resource": [
+ {
+ "Fn::GetAtt": [
+ "scrapBucketB11863B7",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "scrapBucketB11863B7",
+ "Arn"
+ ]
+ },
+ "/*"
+ ]
+ ]
+ }
+ ]
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": {
+ "Type": "Custom::S3AutoDeleteObjects",
+ "Properties": {
+ "ServiceToken": {
+ "Fn::GetAtt": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F",
+ "Arn"
+ ]
+ },
+ "BucketName": {
+ "Ref": "scrapBucketB11863B7"
+ }
+ },
+ "DependsOn": [
+ "scrapBucketPolicy189B0607"
+ ],
+ "UpdateReplacePolicy": "Delete",
+ "DeletionPolicy": "Delete"
+ },
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "lambda.amazonaws.com"
+ }
+ }
+ ]
+ },
+ "ManagedPolicyArns": [
+ {
+ "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+ }
+ ]
+ }
+ },
+ "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
+ },
+ "S3Key": "350185a1069fa20a23a583e20c77f6844218bd73097902362dc94f1a108f5d89.zip"
+ },
+ "Timeout": 900,
+ "MemorySize": 128,
+ "Handler": "__entrypoint__.handler",
+ "Role": {
+ "Fn::GetAtt": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092",
+ "Arn"
+ ]
+ },
+ "Runtime": "nodejs16.x",
+ "Description": {
+ "Fn::Join": [
+ "",
+ [
+ "Lambda function for auto-deleting objects in ",
+ {
+ "Ref": "scrapBucketB11863B7"
+ },
+ " S3 bucket."
+ ]
+ ]
+ }
+ },
+ "DependsOn": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W58",
+ "reason": "CDK generated custom resource"
+ },
+ {
+ "id": "W89",
+ "reason": "CDK generated custom resource"
+ },
+ {
+ "id": "W92",
+ "reason": "CDK generated custom resource"
+ }
+ ]
+ }
+ }
+ },
+ "existingRole3E995BBA": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "kendra.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "minimalargumentsLambdaFunctionServiceRole73B77FF7": {
+ "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"
+ }
+ ]
+ }
+ },
+ "minimalargumentsLambdaFunctionServiceRoleDefaultPolicy59EC60ED": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "kendra:Query",
+ "kendra:Describe*",
+ "kendra:Get*",
+ "kendra:BatchGet*",
+ "kendra:List*",
+ "kendra:Retrieve"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Arn"
+ ]
+ }
+ },
+ {
+ "Action": "kendra:SubmitFeedback",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "minimalargumentsLambdaFunctionServiceRoleDefaultPolicy59EC60ED",
+ "Roles": [
+ {
+ "Ref": "minimalargumentsLambdaFunctionServiceRole73B77FF7"
+ }
+ ]
+ },
+ "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."
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentsLambdaFunctionC19CFAAC": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
+ },
+ "S3Key": "e0128dd6e376c58a74b37bd6178ded8a5909d414a6891c26547b9778ac25679b.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "minimalargumentsLambdaFunctionServiceRole73B77FF7",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "KENDRA_INDEX_ID": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Id"
+ ]
+ }
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs18.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ }
+ },
+ "DependsOn": [
+ "minimalargumentsLambdaFunctionServiceRoleDefaultPolicy59EC60ED",
+ "minimalargumentsLambdaFunctionServiceRole73B77FF7"
+ ],
+ "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"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentskendraindexroleminimalargumentsB1DBDF7D": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "kendra.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Description": "Allow Kendra index to write CloudWatch Logs",
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "cloudwatch:PutMetricData",
+ "Condition": {
+ "StringEquals": {
+ "cloudwatch:namespace": "AWS/Kendra"
+ }
+ },
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": "logs:CreateLogGroup",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*"
+ ]
+ ]
+ }
+ },
+ {
+ "Action": "logs:DescribeLogGroups",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*"
+ ]
+ ]
+ }
+ },
+ {
+ "Action": [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ "logs:DescribeLogStream"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*:log-stream:*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "AllowLogging"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W11",
+ "reason": "PutMetricData does not allow resource specification, scope is narrowed by the namespace condition. https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazoncloudwatch.html"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentskendraindexminimalarguments5CBDD236": {
+ "Type": "AWS::Kendra::Index",
+ "Properties": {
+ "Edition": "DEVELOPER_EDITION",
+ "Name": {
+ "Fn::Join": [
+ "",
+ [
+ "KendraIndexminimal-arguments-",
+ {
+ "Fn::Select": [
+ 2,
+ {
+ "Fn::Split": [
+ "/",
+ {
+ "Ref": "AWS::StackId"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexroleminimalargumentsB1DBDF7D",
+ "Arn"
+ ]
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W80",
+ "reason": "We consulted the Kendra TFC and they confirmed the default encryption is sufficient for general use cases"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentsdatasourceroleminimalarguments00EAC5006": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "kendra.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Description": "Policy for Kendra S3 Data Source",
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "s3:GetObject",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:s3:::",
+ {
+ "Ref": "scrapBucketB11863B7"
+ },
+ "/*"
+ ]
+ ]
+ }
+ },
+ {
+ "Action": "s3:ListBucket",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:s3:::",
+ {
+ "Ref": "scrapBucketB11863B7"
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "Action": [
+ "kendra:BatchPutDocument",
+ "kendra:BatchDeleteDocument"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "s3CrawlPolicy"
+ }
+ ]
+ }
+ },
+ "minimalargumentsdatasourceminimalarguments05A02FF3B": {
+ "Type": "AWS::Kendra::DataSource",
+ "Properties": {
+ "IndexId": {
+ "Ref": "minimalargumentskendraindexminimalarguments5CBDD236"
+ },
+ "Name": {
+ "Fn::Join": [
+ "",
+ [
+ "s3-datasourceminimal-arguments0-",
+ {
+ "Fn::Select": [
+ 2,
+ {
+ "Fn::Split": [
+ "/",
+ {
+ "Ref": "AWS::StackId"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "Type": "S3",
+ "DataSourceConfiguration": {
+ "S3Configuration": {
+ "BucketName": {
+ "Ref": "scrapBucketB11863B7"
+ }
+ }
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "minimalargumentsdatasourceroleminimalarguments00EAC5006",
+ "Arn"
+ ]
+ }
+ }
+ },
+ "minimalargumentskendradatasourceminimalarguments1DA6FACEE": {
+ "Type": "AWS::Kendra::DataSource",
+ "Properties": {
+ "IndexId": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Id"
+ ]
+ },
+ "Name": "web-source",
+ "Type": "WEBCRAWLER",
+ "DataSourceConfiguration": {
+ "WebCrawlerConfiguration": {
+ "CrawlDepth": 1,
+ "Urls": {
+ "SeedUrlConfiguration": {
+ "SeedUrls": [
+ "https://aws.amazon.com"
+ ]
+ }
+ }
+ }
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "existingRole3E995BBA",
+ "Arn"
+ ]
+ }
+ }
+ }
+ },
+ "Mappings": {
+ "DefaultCrNodeVersionMap": {
+ "af-south-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-east-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-northeast-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-northeast-2": {
+ "value": "nodejs16.x"
+ },
+ "ap-northeast-3": {
+ "value": "nodejs16.x"
+ },
+ "ap-south-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-south-2": {
+ "value": "nodejs16.x"
+ },
+ "ap-southeast-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-southeast-2": {
+ "value": "nodejs16.x"
+ },
+ "ap-southeast-3": {
+ "value": "nodejs16.x"
+ },
+ "ca-central-1": {
+ "value": "nodejs16.x"
+ },
+ "cn-north-1": {
+ "value": "nodejs16.x"
+ },
+ "cn-northwest-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-central-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-central-2": {
+ "value": "nodejs16.x"
+ },
+ "eu-north-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-south-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-south-2": {
+ "value": "nodejs16.x"
+ },
+ "eu-west-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-west-2": {
+ "value": "nodejs16.x"
+ },
+ "eu-west-3": {
+ "value": "nodejs16.x"
+ },
+ "me-central-1": {
+ "value": "nodejs16.x"
+ },
+ "me-south-1": {
+ "value": "nodejs16.x"
+ },
+ "sa-east-1": {
+ "value": "nodejs16.x"
+ },
+ "us-east-1": {
+ "value": "nodejs16.x"
+ },
+ "us-east-2": {
+ "value": "nodejs16.x"
+ },
+ "us-gov-east-1": {
+ "value": "nodejs16.x"
+ },
+ "us-gov-west-1": {
+ "value": "nodejs16.x"
+ },
+ "us-iso-east-1": {
+ "value": "nodejs14.x"
+ },
+ "us-iso-west-1": {
+ "value": "nodejs14.x"
+ },
+ "us-isob-east-1": {
+ "value": "nodejs14.x"
+ },
+ "us-west-1": {
+ "value": "nodejs16.x"
+ },
+ "us-west-2": {
+ "value": "nodejs16.x"
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
+ }
+ },
+ "Rules": {
+ "CheckBootstrapVersion": {
+ "Assertions": [
+ {
+ "Assert": {
+ "Fn::Not": [
+ {
+ "Fn::Contains": [
+ [
+ "1",
+ "2",
+ "3",
+ "4",
+ "5"
+ ],
+ {
+ "Ref": "BootstrapVersion"
+ }
+ ]
+ }
+ ]
+ },
+ "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.multiple-sources.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.multiple-sources.ts
new file mode 100644
index 000000000..947f0f8c1
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.multiple-sources.ts
@@ -0,0 +1,68 @@
+/**
+ * Copyright 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 { App, Stack } from "aws-cdk-lib";
+import { LambdaToKendra } from "../lib";
+import * as lambda from 'aws-cdk-lib/aws-lambda';
+import * as iam from 'aws-cdk-lib/aws-iam';
+import * as kendra from 'aws-cdk-lib/aws-kendra';
+import * as defaults from '@aws-solutions-constructs/core';
+import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core';
+
+// Setup
+const app = new App();
+const stack = new Stack(app, generateIntegStackName(__filename));
+stack.templateOptions.description = 'Integration Test for aws-lambda-kendra';
+
+const testBucket = defaults.CreateScrapBucket(stack);
+const existingIamRole = new iam.Role(stack, 'existingRole', {
+ assumedBy: new iam.ServicePrincipal('kendra.amazonaws.com')
+});
+
+const sourceProps: kendra.CfnDataSource.WebCrawlerConfigurationProperty = {
+ urls: {
+ seedUrlConfiguration: {
+ seedUrls: ["https://aws.amazon.com"]
+ }
+ },
+ crawlDepth: 1,
+};
+
+const webCrawlerSource = {
+ name: "web-source",
+ roleArn: existingIamRole.roleArn,
+ type: "WEBCRAWLER",
+ dataSourceConfiguration: {
+ webCrawlerConfiguration: sourceProps
+ }
+};
+
+new LambdaToKendra(stack, 'minimal-arguments', {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler',
+ },
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucket.bucketName,
+ }
+ }
+ },
+ webCrawlerSource ],
+});
+
+suppressAutoDeleteHandlerWarnings(stack);
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.with-vpc.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.with-vpc.expected.json
new file mode 100644
index 000000000..394098d54
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.with-vpc.expected.json
@@ -0,0 +1,1221 @@
+{
+ "Description": "Integration Test for aws-lambda-kendra",
+ "Resources": {
+ "scrapBucketB11863B7": {
+ "Type": "AWS::S3::Bucket",
+ "Properties": {
+ "BucketEncryption": {
+ "ServerSideEncryptionConfiguration": [
+ {
+ "ServerSideEncryptionByDefault": {
+ "SSEAlgorithm": "AES256"
+ }
+ }
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "aws-cdk:auto-delete-objects",
+ "Value": "true"
+ }
+ ],
+ "VersioningConfiguration": {
+ "Status": "Enabled"
+ }
+ },
+ "UpdateReplacePolicy": "Delete",
+ "DeletionPolicy": "Delete",
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W51",
+ "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation"
+ },
+ {
+ "id": "W35",
+ "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation"
+ },
+ {
+ "id": "W41",
+ "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct"
+ }
+ ]
+ }
+ }
+ },
+ "scrapBucketPolicy189B0607": {
+ "Type": "AWS::S3::BucketPolicy",
+ "Properties": {
+ "Bucket": {
+ "Ref": "scrapBucketB11863B7"
+ },
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "s3:GetBucket*",
+ "s3:List*",
+ "s3:DeleteObject*"
+ ],
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": {
+ "Fn::GetAtt": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092",
+ "Arn"
+ ]
+ }
+ },
+ "Resource": [
+ {
+ "Fn::GetAtt": [
+ "scrapBucketB11863B7",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ {
+ "Fn::GetAtt": [
+ "scrapBucketB11863B7",
+ "Arn"
+ ]
+ },
+ "/*"
+ ]
+ ]
+ }
+ ]
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": {
+ "Type": "Custom::S3AutoDeleteObjects",
+ "Properties": {
+ "ServiceToken": {
+ "Fn::GetAtt": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F",
+ "Arn"
+ ]
+ },
+ "BucketName": {
+ "Ref": "scrapBucketB11863B7"
+ }
+ },
+ "DependsOn": [
+ "scrapBucketPolicy189B0607"
+ ],
+ "UpdateReplacePolicy": "Delete",
+ "DeletionPolicy": "Delete"
+ },
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "lambda.amazonaws.com"
+ }
+ }
+ ]
+ },
+ "ManagedPolicyArns": [
+ {
+ "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+ }
+ ]
+ }
+ },
+ "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
+ },
+ "S3Key": "350185a1069fa20a23a583e20c77f6844218bd73097902362dc94f1a108f5d89.zip"
+ },
+ "Timeout": 900,
+ "MemorySize": 128,
+ "Handler": "__entrypoint__.handler",
+ "Role": {
+ "Fn::GetAtt": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092",
+ "Arn"
+ ]
+ },
+ "Runtime": "nodejs16.x",
+ "Description": {
+ "Fn::Join": [
+ "",
+ [
+ "Lambda function for auto-deleting objects in ",
+ {
+ "Ref": "scrapBucketB11863B7"
+ },
+ " S3 bucket."
+ ]
+ ]
+ }
+ },
+ "DependsOn": [
+ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092"
+ ],
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W58",
+ "reason": "CDK generated custom resource"
+ },
+ {
+ "id": "W89",
+ "reason": "CDK generated custom resource"
+ },
+ {
+ "id": "W92",
+ "reason": "CDK generated custom resource"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentsLambdaFunctionServiceRole73B77FF7": {
+ "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"
+ }
+ ]
+ }
+ },
+ "minimalargumentsLambdaFunctionServiceRoleDefaultPolicy59EC60ED": {
+ "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": "*"
+ },
+ {
+ "Action": [
+ "kendra:Query",
+ "kendra:Describe*",
+ "kendra:Get*",
+ "kendra:BatchGet*",
+ "kendra:List*",
+ "kendra:Retrieve"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Arn"
+ ]
+ }
+ },
+ {
+ "Action": "kendra:SubmitFeedback",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "minimalargumentsLambdaFunctionServiceRoleDefaultPolicy59EC60ED",
+ "Roles": [
+ {
+ "Ref": "minimalargumentsLambdaFunctionServiceRole73B77FF7"
+ }
+ ]
+ },
+ "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."
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentsReplaceDefaultSecurityGroupsecuritygroupFDD71705": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "with-vpc/minimal-arguments/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"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentsLambdaFunctionC19CFAAC": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
+ },
+ "S3Key": "e0128dd6e376c58a74b37bd6178ded8a5909d414a6891c26547b9778ac25679b.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "minimalargumentsLambdaFunctionServiceRole73B77FF7",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "KENDRA_INDEX_ID": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Id"
+ ]
+ }
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs18.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "minimalargumentsReplaceDefaultSecurityGroupsecuritygroupFDD71705",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ "Ref": "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "minimalargumentsLambdaFunctionServiceRoleDefaultPolicy59EC60ED",
+ "minimalargumentsLambdaFunctionServiceRole73B77FF7",
+ "VpcisolatedSubnet1RouteTableAssociationD259E31A",
+ "VpcisolatedSubnet2RouteTableAssociation25A4716F",
+ "VpcisolatedSubnet3RouteTableAssociationDC010BEB"
+ ],
+ "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"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentskendraindexroleminimalargumentsB1DBDF7D": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "kendra.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Description": "Allow Kendra index to write CloudWatch Logs",
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "cloudwatch:PutMetricData",
+ "Condition": {
+ "StringEquals": {
+ "cloudwatch:namespace": "AWS/Kendra"
+ }
+ },
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": "logs:CreateLogGroup",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*"
+ ]
+ ]
+ }
+ },
+ {
+ "Action": "logs:DescribeLogGroups",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*"
+ ]
+ ]
+ }
+ },
+ {
+ "Action": [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ "logs:DescribeLogStream"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":logs:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*:log-stream:*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "AllowLogging"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W11",
+ "reason": "PutMetricData does not allow resource specification, scope is narrowed by the namespace condition. https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazoncloudwatch.html"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentskendraindexminimalarguments5CBDD236": {
+ "Type": "AWS::Kendra::Index",
+ "Properties": {
+ "Edition": "DEVELOPER_EDITION",
+ "Name": {
+ "Fn::Join": [
+ "",
+ [
+ "KendraIndexminimal-arguments-",
+ {
+ "Fn::Select": [
+ 2,
+ {
+ "Fn::Split": [
+ "/",
+ {
+ "Ref": "AWS::StackId"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexroleminimalargumentsB1DBDF7D",
+ "Arn"
+ ]
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W80",
+ "reason": "We consulted the Kendra TFC and they confirmed the default encryption is sufficient for general use cases"
+ }
+ ]
+ }
+ }
+ },
+ "minimalargumentsdatasourceroleminimalarguments00EAC5006": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "kendra.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Description": "Policy for Kendra S3 Data Source",
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "s3:GetObject",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:s3:::",
+ {
+ "Ref": "scrapBucketB11863B7"
+ },
+ "/*"
+ ]
+ ]
+ }
+ },
+ {
+ "Action": "s3:ListBucket",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:s3:::",
+ {
+ "Ref": "scrapBucketB11863B7"
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "Action": [
+ "kendra:BatchPutDocument",
+ "kendra:BatchDeleteDocument"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "minimalargumentskendraindexminimalarguments5CBDD236",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "s3CrawlPolicy"
+ }
+ ]
+ }
+ },
+ "minimalargumentsdatasourceminimalarguments05A02FF3B": {
+ "Type": "AWS::Kendra::DataSource",
+ "Properties": {
+ "IndexId": {
+ "Ref": "minimalargumentskendraindexminimalarguments5CBDD236"
+ },
+ "Name": {
+ "Fn::Join": [
+ "",
+ [
+ "s3-datasourceminimal-arguments0-",
+ {
+ "Fn::Select": [
+ 2,
+ {
+ "Fn::Split": [
+ "/",
+ {
+ "Ref": "AWS::StackId"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "Type": "S3",
+ "DataSourceConfiguration": {
+ "S3Configuration": {
+ "BucketName": {
+ "Ref": "scrapBucketB11863B7"
+ }
+ }
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "minimalargumentsdatasourceroleminimalarguments00EAC5006",
+ "Arn"
+ ]
+ }
+ }
+ },
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "10.0.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "with-vpc/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1SubnetE62B1B9B": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1a",
+ "CidrBlock": "10.0.0.0/18",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "isolated"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Isolated"
+ },
+ {
+ "Key": "Name",
+ "Value": "with-vpc/Vpc/isolatedSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1RouteTableE442650B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "with-vpc/Vpc/isolatedSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1RouteTableAssociationD259E31A": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcisolatedSubnet1RouteTableE442650B"
+ },
+ "SubnetId": {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ }
+ }
+ },
+ "VpcisolatedSubnet2Subnet39217055": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1b",
+ "CidrBlock": "10.0.64.0/18",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "isolated"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Isolated"
+ },
+ {
+ "Key": "Name",
+ "Value": "with-vpc/Vpc/isolatedSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet2RouteTable334F9764": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "with-vpc/Vpc/isolatedSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet2RouteTableAssociation25A4716F": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcisolatedSubnet2RouteTable334F9764"
+ },
+ "SubnetId": {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ }
+ }
+ },
+ "VpcisolatedSubnet3Subnet44F2537D": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1c",
+ "CidrBlock": "10.0.128.0/18",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "isolated"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Isolated"
+ },
+ {
+ "Key": "Name",
+ "Value": "with-vpc/Vpc/isolatedSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet3RouteTableA2F6BBC0": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "with-vpc/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": "with-vpc/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,
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "with-vpc/Vpc"
+ }
+ ]
+ },
+ "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",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "with-vpc/Vpc"
+ }
+ ],
+ "TrafficType": "ALL"
+ }
+ },
+ "VpcKENDRAD98378E1": {
+ "Type": "AWS::EC2::VPCEndpoint",
+ "Properties": {
+ "ServiceName": {
+ "Fn::Join": [
+ "",
+ [
+ "com.amazonaws.",
+ {
+ "Ref": "AWS::Region"
+ },
+ ".kendra"
+ ]
+ ]
+ },
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "PrivateDnsEnabled": true,
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "withvpcKENDRAsecuritygroup6E04E691",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ "Ref": "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ],
+ "VpcEndpointType": "Interface"
+ }
+ },
+ "withvpcKENDRAsecuritygroup6E04E691": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "with-vpc/with-vpc-KENDRA-security-group",
+ "SecurityGroupEgress": [
+ {
+ "CidrIp": "0.0.0.0/0",
+ "Description": "Allow all outbound traffic by default",
+ "IpProtocol": "-1"
+ }
+ ],
+ "SecurityGroupIngress": [
+ {
+ "CidrIp": {
+ "Fn::GetAtt": [
+ "Vpc8378EB38",
+ "CidrBlock"
+ ]
+ },
+ "Description": {
+ "Fn::Join": [
+ "",
+ [
+ "from ",
+ {
+ "Fn::GetAtt": [
+ "Vpc8378EB38",
+ "CidrBlock"
+ ]
+ },
+ ":443"
+ ]
+ ]
+ },
+ "FromPort": 443,
+ "IpProtocol": "tcp",
+ "ToPort": 443
+ }
+ ],
+ "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"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "Mappings": {
+ "DefaultCrNodeVersionMap": {
+ "af-south-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-east-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-northeast-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-northeast-2": {
+ "value": "nodejs16.x"
+ },
+ "ap-northeast-3": {
+ "value": "nodejs16.x"
+ },
+ "ap-south-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-south-2": {
+ "value": "nodejs16.x"
+ },
+ "ap-southeast-1": {
+ "value": "nodejs16.x"
+ },
+ "ap-southeast-2": {
+ "value": "nodejs16.x"
+ },
+ "ap-southeast-3": {
+ "value": "nodejs16.x"
+ },
+ "ca-central-1": {
+ "value": "nodejs16.x"
+ },
+ "cn-north-1": {
+ "value": "nodejs16.x"
+ },
+ "cn-northwest-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-central-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-central-2": {
+ "value": "nodejs16.x"
+ },
+ "eu-north-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-south-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-south-2": {
+ "value": "nodejs16.x"
+ },
+ "eu-west-1": {
+ "value": "nodejs16.x"
+ },
+ "eu-west-2": {
+ "value": "nodejs16.x"
+ },
+ "eu-west-3": {
+ "value": "nodejs16.x"
+ },
+ "me-central-1": {
+ "value": "nodejs16.x"
+ },
+ "me-south-1": {
+ "value": "nodejs16.x"
+ },
+ "sa-east-1": {
+ "value": "nodejs16.x"
+ },
+ "us-east-1": {
+ "value": "nodejs16.x"
+ },
+ "us-east-2": {
+ "value": "nodejs16.x"
+ },
+ "us-gov-east-1": {
+ "value": "nodejs16.x"
+ },
+ "us-gov-west-1": {
+ "value": "nodejs16.x"
+ },
+ "us-iso-east-1": {
+ "value": "nodejs14.x"
+ },
+ "us-iso-west-1": {
+ "value": "nodejs14.x"
+ },
+ "us-isob-east-1": {
+ "value": "nodejs14.x"
+ },
+ "us-west-1": {
+ "value": "nodejs16.x"
+ },
+ "us-west-2": {
+ "value": "nodejs16.x"
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
+ }
+ },
+ "Rules": {
+ "CheckBootstrapVersion": {
+ "Assertions": [
+ {
+ "Assert": {
+ "Fn::Not": [
+ {
+ "Fn::Contains": [
+ [
+ "1",
+ "2",
+ "3",
+ "4",
+ "5"
+ ],
+ {
+ "Ref": "BootstrapVersion"
+ }
+ ]
+ }
+ ]
+ },
+ "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.with-vpc.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.with-vpc.ts
new file mode 100644
index 000000000..776d83f26
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.with-vpc.ts
@@ -0,0 +1,45 @@
+/**
+ * Copyright 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 { App, Stack } from "aws-cdk-lib";
+import { LambdaToKendra } from "../lib";
+import * as lambda from 'aws-cdk-lib/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core';
+
+// Setup
+const app = new App();
+const stack = new Stack(app, generateIntegStackName(__filename));
+stack.templateOptions.description = 'Integration Test for aws-lambda-kendra';
+
+const testBucket = defaults.CreateScrapBucket(stack);
+
+new LambdaToKendra(stack, 'minimal-arguments', {
+ lambdaFunctionProps: {
+ code: lambda.Code.fromAsset(`lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler',
+ },
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucket.bucketName,
+ }
+ }
+ }],
+ deployVpc: true
+});
+
+suppressAutoDeleteHandlerWarnings(stack);
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/lambda-kendra.test.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/lambda-kendra.test.ts
new file mode 100644
index 000000000..df13e471c
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/lambda-kendra.test.ts
@@ -0,0 +1,1040 @@
+/**
+ * Copyright 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 { LambdaToKendra } from "../lib";
+import * as lambda from 'aws-cdk-lib/aws-lambda';
+import * as ec2 from 'aws-cdk-lib/aws-ec2';
+import * as kendra from 'aws-cdk-lib/aws-kendra';
+import * as iam from 'aws-cdk-lib/aws-iam';
+import * as cdk from "aws-cdk-lib";
+import { Template } from 'aws-cdk-lib/assertions';
+import * as defaults from '@aws-solutions-constructs/core';
+
+test.only('Launch with minimal code and check structure', () => {
+ const stack = new cdk.Stack();
+ const testFunctionName = 'test-function-name24334';
+ const testBucketName = 'test-bucket-name12344';
+
+ const lambdaProps: lambda.FunctionProps = {
+ functionName: testFunctionName,
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ }
+ ]
+ });
+
+ const template = Template.fromStack(stack);
+ template.hasResourceProperties("AWS::Lambda::Function", {
+ FunctionName: testFunctionName,
+ Environment: {
+ Variables: {
+ KENDRA_INDEX_ID: {
+ "Fn::GetAtt": ["samplekendraindexsample8A81A6C2", "Id"]
+ }
+ }
+ },
+ });
+ template.hasResourceProperties("AWS::Kendra::Index", {
+ RoleArn: {
+ "Fn::GetAtt": [
+ "samplekendraindexrolesample4F9E7B66",
+ "Arn",
+ ],
+ },
+ });
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ Type: 'S3',
+ DataSourceConfiguration: {
+ S3Configuration: {
+ BucketName: testBucketName
+ },
+ },
+ RoleArn: {
+ "Fn::GetAtt": ["sampledatasourcerolesample05A05F8BD", "Arn"]
+ },
+ });
+ // Confirm policy for Kendra index
+ template.hasResourceProperties("AWS::IAM::Role", {
+ Description: "Allow Kendra index to write CloudWatch Logs",
+ Policies: [
+ {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: "cloudwatch:PutMetricData",
+ Condition: {
+ StringEquals: {
+ "cloudwatch:namespace": "AWS/Kendra"
+ }
+ },
+ Effect: "Allow",
+ Resource: "*"
+ },
+ {
+ Action: "logs:CreateLogGroup",
+ Effect: "Allow",
+ Resource: {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:logs:",
+ {
+ Ref: "AWS::Region"
+ },
+ ":",
+ {
+ Ref: "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*"
+ ]
+ ]
+ }
+ },
+ {
+ Action: "logs:DescribeLogGroups",
+ Effect: "Allow",
+ Resource: {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ Ref: "AWS::Partition"
+ },
+ ":logs:",
+ {
+ Ref: "AWS::Region"
+ },
+ ":",
+ {
+ Ref: "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*"
+ ]
+ ]
+ }
+ },
+ {
+ Action: [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ "logs:DescribeLogStream"
+ ],
+ Effect: "Allow",
+ Resource: {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ Ref: "AWS::Partition"
+ },
+ ":logs:",
+ {
+ Ref: "AWS::Region"
+ },
+ ":",
+ {
+ Ref: "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*:log-stream:*"
+ ]
+ ]
+ }
+ }
+ ],
+ Version: "2012-10-17"
+ },
+ PolicyName: "AllowLogging"
+ }
+ ],
+ });
+ // Confirm policy for Kendra index
+ template.hasResourceProperties("AWS::IAM::Role", {
+ Description: "Policy for Kendra S3 Data Source",
+ AssumeRolePolicyDocument: {
+ Statement: [
+ {
+ Action: "sts:AssumeRole",
+ Effect: "Allow",
+ Principal: {
+ Service: "kendra.amazonaws.com"
+ }
+ }
+ ],
+ Version: "2012-10-17"
+ },
+ Policies: [
+ {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: "s3:GetObject",
+ Effect: "Allow",
+ Resource: `arn:aws:s3:::test-bucket-name12344/*`
+ },
+ {
+ Action: "s3:ListBucket",
+ Effect: "Allow",
+ Resource: `arn:aws:s3:::test-bucket-name12344`
+ },
+ {
+ Action: [
+ "kendra:BatchPutDocument",
+ "kendra:BatchDeleteDocument"
+ ],
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "samplekendraindexsample8A81A6C2",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ Version: "2012-10-17"
+ },
+ PolicyName: "s3CrawlPolicy"
+ }
+ ]
+ });
+ // Confirm that Lambda function has QUERY access
+ template.hasResourceProperties("AWS::IAM::Policy", {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ Effect: "Allow",
+ Resource: "*"
+ },
+ {
+ Action: [
+ "kendra:Query",
+ "kendra:Describe*",
+ "kendra:Get*",
+ "kendra:BatchGet*",
+ "kendra:List*",
+ "kendra:Retrieve",
+ ],
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "samplekendraindexsample8A81A6C2",
+ "Arn"
+ ]
+ }
+ },
+ {
+ Action: "kendra:SubmitFeedback",
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "samplekendraindexsample8A81A6C2",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ },
+ Roles: [
+ {
+ Ref: "sampletestfunctionname24334ServiceRole99395A01"
+ }
+ ]
+ });
+});
+
+test('Check pattern properties on minimal launch', () => {
+ const stack = new cdk.Stack();
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ const newConstruct = new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: 'your-bucket-name',
+ }
+ }
+ }
+ ],
+ });
+
+ expect(newConstruct.lambdaFunction).toBeDefined();
+ expect(newConstruct.kendraDataSources).toBeDefined();
+ expect(newConstruct.kendraDataSources.length).toEqual(1);
+ expect(newConstruct.kendraIndex).toBeDefined();
+
+});
+
+test('Launch with VPC', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name12539';
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ new LambdaToKendra(stack, 'sample', {
+ deployVpc: true,
+ lambdaFunctionProps: lambdaProps,
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ }
+ ],
+ });
+
+ const template = Template.fromStack(stack);
+
+ // Check the VPC
+ template.hasResourceProperties("AWS::EC2::VPC", {
+ CidrBlock: "10.0.0.0/16",
+ EnableDnsHostnames: true,
+ EnableDnsSupport: true,
+ InstanceTenancy: "default",
+ });
+
+ // Is the Lambda function associated with the VPC
+ template.hasResourceProperties("AWS::Lambda::Function", {
+ VpcConfig: {
+ SecurityGroupIds: [
+ {
+ "Fn::GetAtt": [
+ "sampleReplaceDefaultSecurityGroupsecuritygroupE5725669",
+ "GroupId"
+ ]
+ }
+ ],
+ SubnetIds: [
+ {
+ Ref: "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ Ref: "VpcisolatedSubnet2Subnet39217055"
+ }
+ ]
+ }
+ });
+ // Check that the Lambda function Policy has proper network access
+ template.hasResourceProperties("AWS::IAM::Policy", {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ Effect: "Allow",
+ Resource: "*"
+ },
+ {},
+ {},
+ {}
+ ],
+ },
+ Roles: [
+ {
+ Ref: "sampleLambdaFunctionServiceRole7A3C4AF5"
+ }
+ ]
+ });
+
+ // Check for the Kendra endpoint in the VPC
+ template.hasResourceProperties("AWS::EC2::VPCEndpoint", {
+ ServiceName: {
+ "Fn::Join": [
+ "",
+ [
+ "com.amazonaws.",
+ {
+ Ref: "AWS::Region"
+ },
+ ".kendra"
+ ]
+ ]
+ },
+ VpcId: {
+ Ref: "Vpc8378EB38"
+ },
+ PrivateDnsEnabled: true,
+ SecurityGroupIds: [
+ {
+ "Fn::GetAtt": [
+ "DefaultKENDRAsecuritygroup34536A79",
+ "GroupId"
+ ]
+ }
+ ],
+ SubnetIds: [
+ {
+ Ref: "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ Ref: "VpcisolatedSubnet2Subnet39217055"
+ }
+ ],
+ VpcEndpointType: "Interface"
+ });
+});
+
+test('Launch with existing lambda', () => {
+ const stack = new cdk.Stack();
+ const testTimeout = 17;
+
+ const testFunctionName = 'test-name';
+
+ const existingFunction = new lambda.Function(stack, 'existing-function', {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler',
+ timeout: cdk.Duration.seconds(testTimeout),
+ functionName: testFunctionName
+ });
+
+ new LambdaToKendra(stack, 'sample', {
+ existingLambdaObj: existingFunction,
+ kendraDataSourcesProps: [],
+ });
+
+ const template = Template.fromStack(stack);
+ template.resourceCountIs("AWS::Lambda::Function", 1);
+ template.hasResourceProperties("AWS::Lambda::Function", {
+ Timeout: testTimeout,
+ FunctionName: testFunctionName,
+ });
+});
+
+test('Confirm error with existing vpc and vpc props', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name22';
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ const app = () => {
+ new LambdaToKendra(stack, 'sample', {
+ existingVpc: defaults.getTestVpc(stack),
+ deployVpc: true,
+ vpcProps: {
+ ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16')
+ },
+ lambdaFunctionProps: lambdaProps,
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ }
+ ],
+ });
+ };
+
+ expect(app).toThrowError(/Error - Either provide an existingVpc or some combination of deployVpc and vpcProps, but not both.\n/);
+});
+
+test('Confirm error with data source with no bucket name', () => {
+ const stack = new cdk.Stack();
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ const app = () => {
+ new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ }
+ }
+ }
+ ],
+ });
+ };
+
+ expect(app).toThrowError(/Error - an S3 Kendra DataSource requires the DataSourceCofiguration.S3Configuration.bucketName prop/);
+});
+
+test('Launch with existing vpc', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name22';
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ new LambdaToKendra(stack, 'sample', {
+ existingVpc: defaults.getTestVpc(stack),
+ lambdaFunctionProps: lambdaProps,
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ }
+ ],
+ });
+
+ const template = Template.fromStack(stack);
+ template.resourceCountIs("AWS::EC2::VPC", 1);
+ template.resourceCountIs("AWS::EC2::VPCEndpoint", 1);
+ template.hasResourceProperties("AWS::EC2::VPC", {
+ EnableDnsHostnames: true,
+ EnableDnsSupport: true,
+ });
+
+});
+
+test('Launch with Read/Write permissions on kendra index', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name22';
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ indexPermissions: ["ReaD", "SubmitFeedBack", "wrITE"], // this also checks case sensitivity
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ }
+ ],
+ });
+
+ const template = Template.fromStack(stack);
+ template.hasResourceProperties("AWS::IAM::Policy", {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ Effect: "Allow",
+ Resource: "*"
+ },
+ {
+ Action: [
+ "kendra:Query",
+ "kendra:Describe*",
+ "kendra:Get*",
+ "kendra:BatchGet*",
+ "kendra:List*",
+ "kendra:Retrieve",
+ ],
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "samplekendraindexsample8A81A6C2",
+ "Arn"
+ ]
+ }
+ },
+ {
+ Action: "kendra:SubmitFeedback",
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "samplekendraindexsample8A81A6C2",
+ "Arn"
+ ]
+ }
+ },
+ {
+ Action: [
+ "kendra:Associate*",
+ "kendra:BatchPut*",
+ "kendra:Clear",
+ "kendra:Create*",
+ "kendra:Delete*",
+ "kendra:Disassociate*",
+ "kendra:Put*",
+ "kendra:Update*",
+ "kendra:Start*",
+ "kendra:Submit*",
+ "kendra:Stop*",
+ "kendra:TagResource",
+ "kendra:UntagResource"
+ ],
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "samplekendraindexsample8A81A6C2",
+ "Arn"
+ ]
+ }
+ }
+ ]
+ }
+ });
+});
+
+test('Launch with Write permissions on kendra index', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name22';
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ indexPermissions: ["WRITE"],
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ }
+ ],
+ });
+
+ const template = Template.fromStack(stack);
+ template.hasResourceProperties("AWS::IAM::Policy", {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ Effect: "Allow",
+ Resource: "*"
+ },
+ {
+ Action: [
+ "kendra:Associate*",
+ "kendra:BatchPut*",
+ "kendra:Clear",
+ "kendra:Create*",
+ "kendra:Delete*",
+ "kendra:Disassociate*",
+ "kendra:Put*",
+ "kendra:Update*",
+ "kendra:Start*",
+ "kendra:Submit*",
+ "kendra:Stop*",
+ "kendra:TagResource",
+ "kendra:UntagResource"
+ ],
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "samplekendraindexsample8A81A6C2",
+ "Arn"
+ ]
+ }
+ }
+ ]
+ }
+ });
+});
+
+test('Launch with Read permissions on kendra index', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name22';
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ indexPermissions: ["READ"],
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ }
+ ],
+ });
+
+ const template = Template.fromStack(stack);
+ template.hasResourceProperties("AWS::IAM::Policy", {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ Effect: "Allow",
+ Resource: "*"
+ },
+ {
+ Action: [
+ "kendra:Query",
+ "kendra:Describe*",
+ "kendra:Get*",
+ "kendra:BatchGet*",
+ "kendra:List*",
+ "kendra:Retrieve",
+ ],
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "samplekendraindexsample8A81A6C2",
+ "Arn"
+ ]
+ }
+ }
+ ]
+ }
+ });
+});
+
+test('Launch with SubmitFeedback permissions on kendra index', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name22';
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ indexPermissions: ["SUBMITFEEDBACK"],
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ }
+ ],
+ });
+
+ const template = Template.fromStack(stack);
+ template.hasResourceProperties("AWS::IAM::Policy", {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ Effect: "Allow",
+ Resource: "*"
+ },
+ {
+ Action: "kendra:SubmitFeedback",
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "samplekendraindexsample8A81A6C2",
+ "Arn"
+ ]
+ }
+ }
+ ]
+ }
+ });
+});
+
+test('Launch with existing kendra index', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name22';
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ const existingRole = new iam.Role(stack, 'existing-role', {
+ assumedBy: new iam.ServicePrincipal('kendra.amazonaws.com'),
+ });
+
+ const existingIndex = new kendra.CfnIndex(stack, 'existing-index', {
+ edition: 'ENTERPRISE',
+ name: 'existingIndex',
+ roleArn: existingRole.roleArn
+ });
+
+ new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ indexPermissions: ["WRITE"],
+ existingKendraIndexObj: existingIndex,
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ }
+ ],
+ });
+
+ const template = Template.fromStack(stack);
+
+ // Make sure we didn't create an index anyway
+ template.resourceCountIs("AWS::Kendra::Index", 1);
+ template.hasResourceProperties("AWS::Kendra::Index", {
+ Edition: 'ENTERPRISE'
+ });
+});
+
+test('Launch with S3 data source with overridden defaults', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name223423';
+ const testInclusionPattern = 'this-folder';
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ indexPermissions: ["WRITE"],
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ inclusionPatterns: [testInclusionPattern],
+ bucketName: testBucketName,
+ }
+ }
+ }
+ ],
+ });
+
+ const template = Template.fromStack(stack);
+ template.resourceCountIs("AWS::Kendra::DataSource", 1);
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ Type: 'S3',
+ DataSourceConfiguration: {
+ S3Configuration: {
+ InclusionPatterns: [testInclusionPattern],
+ BucketName: testBucketName
+ },
+ }
+ });
+});
+
+test('Launch with S3 data source and unsupported data source', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name22';
+ const nonImplementedSourceType = "WEBCRAWLER";
+ const nonImplementedSourceName = "test-other-source";
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ // Create a role
+ const fakeRole = new iam.Role(stack, 'fakeRole', {
+ assumedBy: new iam.ServicePrincipal('kendra.amazonaws.com'),
+ roleName: 'externalFakeRole'
+ });
+
+ new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ indexPermissions: ["WRITE"],
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ },
+ {
+ name: nonImplementedSourceName,
+ roleArn: fakeRole.roleArn,
+ type: nonImplementedSourceType,
+ }],
+ });
+
+ const template = Template.fromStack(stack);
+ template.resourceCountIs("AWS::Kendra::DataSource", 2);
+
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ Type: 'S3',
+ DataSourceConfiguration: {
+ S3Configuration: {
+ BucketName: testBucketName
+ },
+ },
+ RoleArn: {
+ "Fn::GetAtt": ["sampledatasourcerolesample05A05F8BD", "Arn"]
+ },
+ });
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ Name: nonImplementedSourceName,
+ Type: nonImplementedSourceType,
+ });
+
+});
+
+test('Launch with multiple S3 data sources', () => {
+ const stack = new cdk.Stack();
+ const testBucketName = 'test-bucket-name22342';
+ const secondBucketName = 'second-bucket-name22342342';
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ new LambdaToKendra(stack, 'two-sources', {
+ lambdaFunctionProps: lambdaProps,
+ indexPermissions: ["WRITE"],
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ }
+ },
+ {
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: secondBucketName,
+ }
+ }
+ }],
+ });
+
+ const template = Template.fromStack(stack);
+ template.resourceCountIs("AWS::Kendra::DataSource", 2);
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ Type: 'S3',
+ DataSourceConfiguration: {
+ S3Configuration: {
+ BucketName: testBucketName
+ },
+ },
+ RoleArn: {
+ "Fn::GetAtt": ["twosourcesdatasourceroletwosources0B8E24996", "Arn"]
+ },
+ });
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ Type: 'S3',
+ DataSourceConfiguration: {
+ S3Configuration: {
+ BucketName: secondBucketName
+ },
+ },
+ RoleArn: {
+ "Fn::GetAtt": ["twosourcesdatasourceroletwosources164176C5E", "Arn"]
+ },
+ });
+});
+
+test('Test with custom environment variable name', () => {
+ const stack = new cdk.Stack();
+
+ const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_18_X,
+ handler: 'index.handler'
+ };
+
+ new LambdaToKendra(stack, 'sample', {
+ lambdaFunctionProps: lambdaProps,
+ kendraDataSourcesProps: [{
+ type: 'S3',
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: 'your-bucket-name',
+ }
+ }
+ }
+ ],
+ indexIdEnvironmentVariableName: "MY_VAR_NAME",
+ });
+
+ const template = Template.fromStack(stack);
+
+ template.hasResourceProperties("AWS::Lambda::Function", {
+ Environment: {
+ Variables: {
+ MY_VAR_NAME: {
+ "Fn::GetAtt": ["samplekendraindexsample8A81A6C2", "Id"]
+ }
+ }
+ }
+ });
+
+});
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/lambda/index.js b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/lambda/index.js
new file mode 100644
index 000000000..3c81710ce
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/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: `Stub Lambda function for testing - ${event.path}\n`
+ };
+ };
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kinesisfirehose/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-kinesisfirehose/package.json
index 410688503..52c3932da 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-kinesisfirehose/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kinesisfirehose/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/package.json
index ef068cafd..244610868 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/package.json
index 9ae4539af..740aeea46 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-sagemakerendpoint/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-sagemakerendpoint/package.json
index e692caf20..d77b5c82c 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-sagemakerendpoint/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-sagemakerendpoint/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-secretsmanager/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-secretsmanager/package.json
index 132469759..d7bd7905b 100755
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-secretsmanager/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-secretsmanager/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-sns/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-sns/package.json
index f0a06d0a3..e9723e9eb 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-sns/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-sns/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-sqs-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-sqs-lambda/package.json
index 281d0f87c..393f1fa79 100755
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-sqs-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-sqs-lambda/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-sqs/package.json
index e267670bb..a6c6f68fa 100755
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-sqs/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-sqs/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-ssmstringparameter/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-ssmstringparameter/package.json
index 17e47393b..3832d4536 100755
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-ssmstringparameter/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-ssmstringparameter/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-stepfunctions/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-stepfunctions/package.json
index f02357455..33209346c 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-stepfunctions/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-stepfunctions/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-route53-alb/package.json b/source/patterns/@aws-solutions-constructs/aws-route53-alb/package.json
index 9ca4e2c33..f39c56640 100644
--- a/source/patterns/@aws-solutions-constructs/aws-route53-alb/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-route53-alb/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-route53-apigateway/package.json b/source/patterns/@aws-solutions-constructs/aws-route53-apigateway/package.json
index 8946e168a..603244834 100755
--- a/source/patterns/@aws-solutions-constructs/aws-route53-apigateway/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-route53-apigateway/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-s3-lambda/package.json
index d1f30a615..06f505371 100644
--- a/source/patterns/@aws-solutions-constructs/aws-s3-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-s3-lambda/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/package.json
index 571c95e30..53683665d 100644
--- a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/package.json b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/package.json
index b32c965e3..b49393755 100644
--- a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-sns-lambda/package.json
index 6fd0ea602..5f064d71f 100644
--- a/source/patterns/@aws-solutions-constructs/aws-sns-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-sns-lambda/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json
index 35164cac5..3ba34b00e 100644
--- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-sqs-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-sqs-lambda/package.json
index 56e25ae7a..2fe74d18a 100644
--- a/source/patterns/@aws-solutions-constructs/aws-sqs-lambda/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-sqs-lambda/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-alb/package.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-alb/package.json
index bc2f80938..a09984c62 100644
--- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-alb/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-alb/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/package.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/package.json
index 87a219833..0c057d852 100644
--- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/package.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/package.json
index fce80f599..8b43b8dbb 100644
--- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/package.json
+++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"integ-no-clean": "cdk-integ --no-clean",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
diff --git a/source/patterns/@aws-solutions-constructs/core/index.ts b/source/patterns/@aws-solutions-constructs/core/index.ts
index 7ad6ee16d..c35ed2b18 100644
--- a/source/patterns/@aws-solutions-constructs/core/index.ts
+++ b/source/patterns/@aws-solutions-constructs/core/index.ts
@@ -71,4 +71,5 @@ export * from './lib/eventbridge-helper';
export * from './lib/waf-defaults';
export * from './lib/waf-helper';
export * from './lib/opensearch-defaults';
-export * from './lib/opensearch-helper';
\ No newline at end of file
+export * from './lib/opensearch-helper';
+export * from './lib/kendra-helper';
diff --git a/source/patterns/@aws-solutions-constructs/core/lib/kendra-defaults.ts b/source/patterns/@aws-solutions-constructs/core/lib/kendra-defaults.ts
new file mode 100644
index 000000000..9901947bc
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/core/lib/kendra-defaults.ts
@@ -0,0 +1,26 @@
+/**
+ * Copyright 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 kendra from 'aws-cdk-lib/aws-kendra';
+import { generatePhysicalName } from "./utils";
+
+/**
+ * @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
+ */
+export function DefaultKendraIndexProps(id: string, roleArn?: string): kendra.CfnIndexProps {
+ return {
+ name: generatePhysicalName("", ["KendraIndex", id], 1000),
+ roleArn,
+ edition: 'DEVELOPER_EDITION',
+ } as kendra.CfnIndexProps;
+}
diff --git a/source/patterns/@aws-solutions-constructs/core/lib/kendra-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/kendra-helper.ts
new file mode 100644
index 000000000..0b88b9edc
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/core/lib/kendra-helper.ts
@@ -0,0 +1,254 @@
+/**
+ * Copyright 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.
+ */
+
+/*
+ * The functions found here in the core library are for internal use and can be changed
+ * or removed outside of a major release. We recommend against calling them directly from client code.
+ */
+
+import * as kendra from 'aws-cdk-lib/aws-kendra';
+import * as iam from 'aws-cdk-lib/aws-iam';
+import { addCfnSuppressRules, consolidateProps } from "./utils";
+import { Aws } from 'aws-cdk-lib';
+
+// Note: To ensure CDKv2 compatibility, keep the import statement for Construct separate
+import { Construct } from 'constructs';
+import { DefaultKendraIndexProps } from './kendra-defaults';
+import { generatePhysicalName, overrideProps } from "./utils";
+
+export interface BuildKendraIndexProps {
+ readonly kendraIndexProps?: kendra.CfnIndexProps | any;
+ /**
+ * Existing instance of Kendra Index object, Providing both this and kendraIndexProps will cause an error.
+ *
+ * @default - None
+ */
+ readonly existingIndexObj?: kendra.CfnIndex;
+}
+
+/**
+ * @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
+ */
+export function buildKendraIndex(scope: Construct, id: string, props: BuildKendraIndexProps): kendra.CfnIndex {
+ // Conditional lambda function creation
+ if (props.existingIndexObj) {
+ // The client provided an Index, so we'll do nothing and return it to them
+ return props.existingIndexObj;
+ } else {
+
+ let indexRoleArn: string = "";
+
+ // If the client provided a role, then don't bother creating a new one that we don't need
+ if (!props.kendraIndexProps?.roleArn) {
+ indexRoleArn = CreateKendraIndexLoggingRole(scope, id);
+ }
+ const defaultIndexProperties = DefaultKendraIndexProps(id, indexRoleArn);
+
+ const consolidatedIndexProperties = consolidateProps(defaultIndexProperties, props.kendraIndexProps);
+ const newIndex = new kendra.CfnIndex(scope, `kendra-index-${id}`, consolidatedIndexProperties);
+ addCfnSuppressRules(newIndex, [{
+ id: "W80",
+ reason: "We consulted the Kendra TFC and they confirmed the default encryption is sufficient for general use cases"
+ }]);
+
+ return newIndex;
+ }
+}
+
+/**
+ * @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
+ */
+export function AddMultipleKendraDataSources(scope: Construct,
+ id: string,
+ kendraIndex: kendra.CfnIndex,
+ clientDataSourceProps: Array>): kendra.CfnDataSource[] {
+
+ const returnDataSources: kendra.CfnDataSource[] = [];
+ clientDataSourceProps.forEach((props, index) => {
+ returnDataSources.push(AddKendraDataSource(scope, `${id}${index}`, kendraIndex, props));
+ });
+ return returnDataSources;
+}
+
+/**
+ * @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
+ */
+export function AddKendraDataSource(scope: Construct,
+ id: string, index: kendra.CfnIndex,
+ clientDataSourceProps: kendra.CfnDataSourceProps | any): kendra.CfnDataSource {
+
+ if (clientDataSourceProps.type === 'S3') {
+ return CreateS3DataSource(scope, index, id, clientDataSourceProps);
+ } else {
+ if (clientDataSourceProps.indexId) {
+ throw new Error('Invalid DataSource prop specified - Construct must set the indexId prop');
+ }
+ return new kendra.CfnDataSource(scope, `kendra-data-source-${id}`, {
+ ...clientDataSourceProps,
+ indexId: index.attrId
+ });
+ }
+}
+
+function CreateS3DataSource(scope: Construct,
+ targetIndex: kendra.CfnIndex,
+ id: string,
+ clientProps: Partial): kendra.CfnDataSource {
+
+ // We go through some hoops here to extract the various inputs, because we need to narrow
+ // the type to remove the union with IResolvable
+ const dataSourceConfig = clientProps.dataSourceConfiguration as kendra.CfnDataSource.DataSourceConfigurationProperty;
+ if (!dataSourceConfig) {
+ throw new Error('Error - an S3 Kendra DataSource requires an DataSourceCofiguration prop');
+ }
+
+ const s3DataSourceConfig = dataSourceConfig.s3Configuration as kendra.CfnDataSource.S3DataSourceConfigurationProperty;
+
+ if (!s3DataSourceConfig) {
+ throw new Error('Error - an S3 Kendra DataSource requires an DataSourceCofiguration.S3Configuration prop');
+ }
+
+ // No Bucket name is an error
+ if (!s3DataSourceConfig.bucketName) {
+ throw new Error('Error - an S3 Kendra DataSource requires the DataSourceCofiguration.S3Configuration.bucketName prop');
+ }
+
+ // If there's no role, make a role and put it into defaultProps
+ // Put bucket name in default props
+ let defaultProps: kendra.CfnDataSourceProps = {
+ indexId: targetIndex.ref,
+ name: generatePhysicalName('', ['s3-datasource', id], 1000),
+ type: 'S3'
+ };
+
+ // Return consolidated default and user props
+ if (!clientProps.roleArn) {
+ const s3CrawlPolicy = new iam.PolicyDocument({
+ statements: [
+ new iam.PolicyStatement({
+ actions: [
+ "s3:GetObject"
+ ],
+ resources: [
+ `arn:aws:s3:::${s3DataSourceConfig.bucketName}/*`
+ ],
+ effect: iam.Effect.ALLOW
+ }),
+ new iam.PolicyStatement({
+ actions: [
+ "s3:ListBucket"
+ ],
+ resources: [
+ `arn:aws:s3:::${s3DataSourceConfig.bucketName}`
+ ],
+ effect: iam.Effect.ALLOW
+ }),
+ new iam.PolicyStatement({
+ effect: iam.Effect.ALLOW,
+ actions: [
+ "kendra:BatchPutDocument",
+ "kendra:BatchDeleteDocument"
+ ],
+ resources: [
+ targetIndex.attrArn
+ ]
+ }),
+ ]
+ });
+
+ const dataSourceRole: iam.Role = new iam.Role(scope, `data-source-role-${id}`, {
+ assumedBy: new iam.ServicePrincipal('kendra.amazonaws.com'),
+ description: 'Policy for Kendra S3 Data Source',
+ inlinePolicies: {
+ s3CrawlPolicy,
+ },
+ });
+ defaultProps = overrideProps(defaultProps, { roleArn: dataSourceRole.roleArn });
+ }
+
+ const consolidatedProps: kendra.CfnDataSourceProps = consolidateProps(defaultProps, clientProps);
+
+ return new kendra.CfnDataSource(scope, `data-source-${id}`, consolidatedProps);
+
+}
+
+function CreateKendraIndexLoggingRole(scope: Construct, id: string): string {
+ const allowKendraToLogPolicy = new iam.PolicyDocument({
+ statements: [
+ new iam.PolicyStatement({
+ resources: ['*'],
+ actions: [
+ "cloudwatch:PutMetricData"
+ ],
+ effect: iam.Effect.ALLOW,
+ conditions: {
+ StringEquals: {
+ "cloudwatch:namespace": "AWS/Kendra"
+ }
+ }
+ }),
+ new iam.PolicyStatement({
+ resources: [`arn:aws:logs:${Aws.REGION}:${Aws.ACCOUNT_ID}:log-group:/aws/kendra/*`],
+ actions: [
+ "logs:CreateLogGroup"
+ ],
+ effect: iam.Effect.ALLOW,
+ }),
+ new iam.PolicyStatement({
+ resources: [`arn:${Aws.PARTITION}:logs:${Aws.REGION}:${Aws.ACCOUNT_ID}:log-group:/aws/kendra/*`],
+ actions: [
+ "logs:DescribeLogGroups"
+ ],
+ effect: iam.Effect.ALLOW,
+ }),
+ new iam.PolicyStatement({
+ resources: [`arn:${Aws.PARTITION}:logs:${Aws.REGION}:${Aws.ACCOUNT_ID}:log-group:/aws/kendra/*:log-stream:*`],
+ actions: [
+ 'logs:CreateLogStream',
+ 'logs:PutLogEvents',
+ 'logs:DescribeLogStream',
+ ],
+ effect: iam.Effect.ALLOW,
+ }),
+ ],
+ });
+
+ const indexRole: iam.Role = new iam.Role(scope, `kendra-index-role-${id}`, {
+ assumedBy: new iam.ServicePrincipal('kendra.amazonaws.com'),
+ description: 'Allow Kendra index to write CloudWatch Logs',
+ inlinePolicies: {
+ AllowLogging: allowKendraToLogPolicy,
+ },
+ });
+ addCfnSuppressRules(indexRole, [{
+ id: "W11",
+ reason: "PutMetricData does not allow resource specification, " +
+ "scope is narrowed by the namespace condition. " +
+ "https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazoncloudwatch.html"
+ }]);
+ return indexRole.roleArn;
+}
+
+// @summary Confirm each entry is a correct value, uppercase each entry
+export function normalizeKendraPermissions(rawPermissions: string[]): string[] {
+ const validPermissions = ["READ", "SUBMITFEEDBACK", "WRITE"];
+
+ const result = rawPermissions.map((s) => {
+ const upperCaseValue = s.toUpperCase();
+ if (!validPermissions.includes(upperCaseValue)) {
+ throw new Error(`Invalid indexPermission value - valid values are "READ", "SUBMITFEEDBACK" and "WRITE"`);
+ }
+ return upperCaseValue;
+ });
+ return result;
+}
diff --git a/source/patterns/@aws-solutions-constructs/core/lib/vpc-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/vpc-helper.ts
index 542dc9f1d..6ca739ca4 100644
--- a/source/patterns/@aws-solutions-constructs/core/lib/vpc-helper.ts
+++ b/source/patterns/@aws-solutions-constructs/core/lib/vpc-helper.ts
@@ -78,7 +78,8 @@ export enum ServiceEndpointTypes {
ECR_DKR = "ECR_DKR",
EVENTS = "CLOUDWATCH_EVENTS",
KINESIS_FIREHOSE = "KINESIS_FIREHOSE",
- KINESIS_STREAMS = "KINESIS_STREAMS"
+ KINESIS_STREAMS = "KINESIS_STREAMS",
+ KENDRA = "KENDRA"
}
enum EndpointTypes {
@@ -158,6 +159,11 @@ const endpointSettings: EndpointDefinition[] = [
endpointName: ServiceEndpointTypes.KINESIS_STREAMS,
endpointType: EndpointTypes.INTERFACE,
endpointInterfaceService: ec2.InterfaceVpcEndpointAwsService.KINESIS_STREAMS
+ },
+ {
+ endpointName: ServiceEndpointTypes.KENDRA,
+ endpointType: EndpointTypes.INTERFACE,
+ endpointInterfaceService: ec2.InterfaceVpcEndpointAwsService.KENDRA
}
];
diff --git a/source/patterns/@aws-solutions-constructs/core/package.json b/source/patterns/@aws-solutions-constructs/core/package.json
index af8148840..98f6fdf47 100644
--- a/source/patterns/@aws-solutions-constructs/core/package.json
+++ b/source/patterns/@aws-solutions-constructs/core/package.json
@@ -23,7 +23,7 @@
"clean": "tsc -b --clean",
"watch": "tsc -b -w",
"integ": "cdk-integ",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"jsii": "jsii",
"jsii-pacmak": "jsii-pacmak",
"build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert",
diff --git a/source/patterns/@aws-solutions-constructs/core/test/kendra-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/kendra-helper.test.ts
new file mode 100644
index 000000000..8e934d19a
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/core/test/kendra-helper.test.ts
@@ -0,0 +1,555 @@
+/**
+ * Copyright 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 { Stack } from 'aws-cdk-lib';
+// import * as defaults from '../index';
+import { Match, Template } from 'aws-cdk-lib/assertions';
+import { buildKendraIndex, AddKendraDataSource, AddMultipleKendraDataSources, normalizeKendraPermissions } from '../lib/kendra-helper';
+import * as kendra from 'aws-cdk-lib/aws-kendra';
+import * as iam from 'aws-cdk-lib/aws-iam';
+
+// import { Construct } from 'constructs';
+
+test('Launch Kendra index with defaults', () => {
+ const stack = new Stack(undefined, undefined);
+
+ buildKendraIndex(stack, 'test', {});
+
+ const template = Template.fromStack(stack);
+ template.hasResourceProperties('AWS::Kendra::Index', {});
+ template.resourceCountIs("AWS::IAM::Role", 1);
+});
+
+test('Confirm kendra has log wrting privileges', () => {
+ const stack = new Stack(undefined, undefined);
+
+ buildKendraIndex(stack, 'test', {});
+
+ const template = Template.fromStack(stack);
+ template.hasResourceProperties("AWS::IAM::Role", {
+ Description: "Allow Kendra index to write CloudWatch Logs",
+ Policies: [
+ {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: "cloudwatch:PutMetricData",
+ Condition: {
+ StringEquals: {
+ "cloudwatch:namespace": "AWS/Kendra"
+ }
+ },
+ Effect: "Allow",
+ Resource: "*"
+ },
+ {
+ Action: "logs:CreateLogGroup",
+ Effect: "Allow",
+ Resource: {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:logs:",
+ {
+ Ref: "AWS::Region"
+ },
+ ":",
+ {
+ Ref: "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*"
+ ]
+ ]
+ }
+ },
+ {
+ Action: "logs:DescribeLogGroups",
+ Effect: "Allow",
+ Resource: {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ Ref: "AWS::Partition"
+ },
+ ":logs:",
+ {
+ Ref: "AWS::Region"
+ },
+ ":",
+ {
+ Ref: "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*"
+ ]
+ ]
+ }
+ },
+ {
+ Action: [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ "logs:DescribeLogStream"
+ ],
+ Effect: "Allow",
+ Resource: {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ Ref: "AWS::Partition"
+ },
+ ":logs:",
+ {
+ Ref: "AWS::Region"
+ },
+ ":",
+ {
+ Ref: "AWS::AccountId"
+ },
+ ":log-group:/aws/kendra/*:log-stream:*"
+ ]
+ ]
+ }
+ }
+ ],
+ Version: "2012-10-17"
+ },
+ PolicyName: "AllowLogging"
+ }
+ ],
+ });
+});
+
+test('Launch Kendra index with custom properties', () => {
+ const testName = 'test-index-name';
+ const stack = new Stack(undefined, undefined);
+
+ buildKendraIndex(stack, 'test', {
+ kendraIndexProps: {
+ name: testName
+ }
+ });
+
+ const template = Template.fromStack(stack);
+ template.hasResourceProperties('AWS::Kendra::Index', {
+ Name: testName
+ });
+ template.resourceCountIs("AWS::IAM::Role", 1);
+});
+
+test('Launch Kendra index with existing role', () => {
+ const fakeRoleArn = 'fake-arn';
+ const stack = new Stack(undefined, undefined);
+
+ buildKendraIndex(stack, 'test', {
+ kendraIndexProps: {
+ roleArn: fakeRoleArn
+ }
+ });
+
+ const template = Template.fromStack(stack);
+ template.hasResourceProperties('AWS::Kendra::Index', {
+ RoleArn: fakeRoleArn
+ });
+ template.resourceCountIs("AWS::IAM::Role", 0);
+});
+
+test('use existing Kendra index', () => {
+ const stack = new Stack(undefined, undefined);
+
+ const indexProps: kendra.CfnIndexProps = {
+ edition: 'DEVELOPER_EDITION',
+ name: 'kendra-test',
+ roleArn: 'fake-arn'
+ };
+
+ const testIndex = new kendra.CfnIndex(stack, `test`, indexProps);
+
+ buildKendraIndex(stack, 'test', {
+ existingIndexObj: testIndex
+ });
+
+ const template = Template.fromStack(stack);
+ template.resourceCountIs('AWS::Kendra::Index', 1);
+});
+
+test('Launch Kendra index with an S3 data source', () => {
+ const testName = 'test-name';
+ const testBucketName = 'test-bucket-name';
+ const stack = new Stack(undefined, undefined);
+
+ const newIndex = buildKendraIndex(stack, 'test', {});
+
+ AddKendraDataSource(stack, 'testSource', newIndex, {
+ type: 'S3',
+ name: testName,
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ },
+ });
+
+ const template = Template.fromStack(stack);
+ template.resourceCountIs('AWS::Kendra::Index', 1);
+ template.resourceCountIs("AWS::Kendra::DataSource", 1);
+ template.resourceCountIs("AWS::IAM::Role", 2);
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ Name: testName,
+ RoleArn: Match.anyValue(),
+ Type: 'S3',
+ DataSourceConfiguration: {
+ S3Configuration: {
+ BucketName: testBucketName
+ }
+ }
+ });
+ template.hasResourceProperties("AWS::IAM::Role", {
+ AssumeRolePolicyDocument: {
+ Statement: [
+ {
+ Action: "sts:AssumeRole",
+ Effect: "Allow",
+ Principal: {
+ Service: "kendra.amazonaws.com"
+ }
+ }
+ ],
+ Version: "2012-10-17"
+ },
+ Policies: [
+ {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: "s3:GetObject",
+ Effect: "Allow",
+ Resource: `arn:aws:s3:::${testBucketName}/*`
+ },
+ {
+ Action: "s3:ListBucket",
+ Effect: "Allow",
+ Resource: `arn:aws:s3:::${testBucketName}`
+ },
+ {
+ Action: [
+ "kendra:BatchPutDocument",
+ "kendra:BatchDeleteDocument"
+ ],
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "kendraindextest",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ Version: "2012-10-17"
+ },
+ PolicyName: "s3CrawlPolicy"
+ }
+ ]
+ });
+});
+
+test('Launch Kendra index with a customized S3 data source', () => {
+ const testName = 'test-name';
+ const testBucketName = 'test-bucket-name';
+ const stack = new Stack(undefined, undefined);
+
+ const newIndex = buildKendraIndex(stack, 'test', {});
+
+ AddKendraDataSource(stack, 'testSource', newIndex, {
+ type: 'S3',
+ name: testName,
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ inclusionPatterns: ['IncludeThis'],
+ }
+ },
+ });
+
+ const template = Template.fromStack(stack);
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ DataSourceConfiguration: {
+ S3Configuration: {
+ BucketName: "test-bucket-name",
+ InclusionPatterns: ['IncludeThis'],
+ }
+ },
+ });
+ template.resourceCountIs('AWS::Kendra::Index', 1);
+ template.resourceCountIs("AWS::Kendra::DataSource", 1);
+ template.resourceCountIs("AWS::IAM::Role", 2);
+});
+
+test('Launch Kendra index with non-implemented data source', () => {
+ const nonImplementedSourceType = "WEBCRAWLER";
+ const nonImplementedSourceName = "test-other-source";
+ const stack = new Stack(undefined, undefined);
+ const testIndex = buildKendraIndex(stack, 'test', {});
+
+ // Create a role
+ const fakeRole = new iam.Role(stack, 'fakeRole', {
+ assumedBy: new iam.ServicePrincipal('kendra.amazonaws.com'),
+ roleName: 'externalFakeRole'
+ });
+
+ // Create data source props
+ const dataSourceProps: Partial = {
+ name: nonImplementedSourceName,
+ roleArn: fakeRole.roleArn,
+ type: nonImplementedSourceType,
+ };
+
+ // Add a data source
+ AddKendraDataSource(stack, 'test-other-source', testIndex, dataSourceProps);
+
+ const template = Template.fromStack(stack);
+ template.resourceCountIs('AWS::Kendra::Index', 1);
+ template.resourceCountIs('AWS::Kendra::DataSource', 1);
+ template.resourceCountIs('AWS::IAM::Role', 2);
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ Name: nonImplementedSourceName,
+ Type: nonImplementedSourceType
+ });
+});
+
+test('Confirm error if client supplies an index in the DataSource props', () => {
+ const nonImplementedSourceType = "WEBCRAWLER";
+ const nonImplementedSourceName = "test-other-source";
+ const stack = new Stack(undefined, undefined);
+ const testIndex = buildKendraIndex(stack, 'test', {});
+
+ // Create a role
+ const fakeRole = new iam.Role(stack, 'fakeRole', {
+ assumedBy: new iam.ServicePrincipal('kendra.amazonaws.com'),
+ roleName: 'externalFakeRole'
+ });
+
+ // Create data source props
+ const dataSourceProps: Partial = {
+ name: nonImplementedSourceName,
+ roleArn: fakeRole.roleArn,
+ type: nonImplementedSourceType,
+ indexId: testIndex.attrId,
+ };
+
+ // Add a data source
+ const app = () => {
+ AddKendraDataSource(stack, 'test-other-source', testIndex, dataSourceProps);
+ };
+
+ expect(app).toThrowError(/Invalid DataSource prop specified - Construct must set the indexId prop/);
+});
+
+test('Confirm error if does not provide DataSource configuration', () => {
+ const stack = new Stack(undefined, undefined);
+ const testIndex = buildKendraIndex(stack, 'test', {});
+
+ // Create data source props
+ const dataSourceProps: Partial = {
+ type: "S3"
+ };
+
+ // Add a data source
+ const app = () => {
+ AddKendraDataSource(stack, 'test-bad-source', testIndex, dataSourceProps);
+ };
+
+ expect(app).toThrowError(/Error - an S3 Kendra DataSource requires an DataSourceCofiguration prop/);
+});
+
+test('Confirm error if does not provide S3 DataSource configuration', () => {
+ const stack = new Stack(undefined, undefined);
+ const testIndex = buildKendraIndex(stack, 'test', {});
+
+ // Create data source props
+ const dataSourceProps: Partial = {
+ type: "S3",
+ dataSourceConfiguration: {
+
+ }
+ };
+
+ // Add a data source
+ const app = () => {
+ AddKendraDataSource(stack, 'test-bad-source', testIndex, dataSourceProps);
+ };
+
+ expect(app).toThrowError(/Error - an S3 Kendra DataSource requires an DataSourceCofiguration.S3Configuration prop/);
+});
+
+test('Confirm error if does not provide S3 Bucketname', () => {
+ const stack = new Stack(undefined, undefined);
+ const testIndex = buildKendraIndex(stack, 'test', {});
+
+ // Create data source props
+ const dataSourceProps: Partial = {
+ type: "S3",
+ dataSourceConfiguration: {
+ s3Configuration: {
+ // Going through some hoops here to force this conditiion, but since we
+ // allow | any for props, a client could do this
+ } as kendra.CfnDataSource.S3DataSourceConfigurationProperty
+ }
+ };
+
+ // Add a data source
+ const app = () => {
+ AddKendraDataSource(stack, 'test-bad-source', testIndex, dataSourceProps);
+ };
+
+ expect(app).toThrowError(/Error - an S3 Kendra DataSource requires the DataSourceCofiguration.S3Configuration.bucketName prop/);
+});
+
+test('Launch Kendra index with multiple data sources', () => {
+ const testName = 'test-name';
+ const testBucketName = 'test-bucket-name';
+ const nonImplementedSourceType = "WEBCRAWLER";
+ const nonImplementedSourceName = "test-other-source";
+ const stack = new Stack(undefined, undefined);
+
+ const newIndex = buildKendraIndex(stack, 'test', {});
+
+ const s3DataSourceProps = {
+ type: 'S3',
+ name: testName,
+ dataSourceConfiguration: {
+ s3Configuration: {
+ bucketName: testBucketName,
+ }
+ },
+ };
+
+ // Create a role
+ const fakeRole = new iam.Role(stack, 'fakeRole', {
+ assumedBy: new iam.ServicePrincipal('kendra.amazonaws.com'),
+ roleName: 'externalFakeRole'
+ });
+
+ // Create data source props
+ const otherDataSourceProps: Partial = {
+ name: nonImplementedSourceName,
+ roleArn: fakeRole.roleArn,
+ type: nonImplementedSourceType
+ };
+
+ // Add a data source
+ const sourceObjects = AddMultipleKendraDataSources(stack, 'test-other-source', newIndex, [
+ s3DataSourceProps,
+ otherDataSourceProps
+ ]
+ );
+
+ expect(sourceObjects.length).toEqual(2);
+ expect(sourceObjects[0].type).toEqual('S3');
+ expect(sourceObjects[1].name).toEqual(nonImplementedSourceName);
+
+ const template = Template.fromStack(stack);
+ template.resourceCountIs('AWS::Kendra::Index', 1);
+ template.resourceCountIs('AWS::Kendra::DataSource', 2);
+ template.resourceCountIs('AWS::IAM::Role', 3);
+
+ // Look for S3 data source
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ Name: testName,
+ RoleArn: Match.anyValue(),
+ Type: 'S3',
+ DataSourceConfiguration: {
+ S3Configuration: {
+ BucketName: testBucketName
+ }
+ }
+ });
+ template.hasResourceProperties("AWS::IAM::Role", {
+ AssumeRolePolicyDocument: {
+ Statement: [
+ {
+ Action: "sts:AssumeRole",
+ Effect: "Allow",
+ Principal: {
+ Service: "kendra.amazonaws.com"
+ }
+ }
+ ],
+ Version: "2012-10-17"
+ },
+ Policies: [
+ {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: "s3:GetObject",
+ Effect: "Allow",
+ Resource: `arn:aws:s3:::${testBucketName}/*`
+ },
+ {
+ Action: "s3:ListBucket",
+ Effect: "Allow",
+ Resource: `arn:aws:s3:::${testBucketName}`
+ },
+ {
+ Action: [
+ "kendra:BatchPutDocument",
+ "kendra:BatchDeleteDocument"
+ ],
+ Effect: "Allow",
+ Resource: {
+ "Fn::GetAtt": [
+ "kendraindextest",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ Version: "2012-10-17"
+ },
+ PolicyName: "s3CrawlPolicy"
+ }
+ ]
+ });
+
+ // Look for non-implemented data source
+ template.hasResourceProperties("AWS::Kendra::DataSource", {
+ Name: nonImplementedSourceName,
+ Type: nonImplementedSourceType,
+ IndexId: Match.anyValue()
+ });
+
+});
+
+test('Confirm Errof for bad kendra Permission in normalizeKendraPermissions()', () => {
+ const inputs = ["read", "submitfeedback", "write"];
+
+ const outputs = normalizeKendraPermissions(inputs);
+
+ expect(outputs.includes("READ")).toBeTruthy();
+ expect(outputs.includes("SUBMITFEEDBACK")).toBeTruthy();
+ expect(outputs.includes("WRITE")).toBeTruthy();
+});
+
+test('Confirm successful operation of normalizeKendraPermissions()', () => {
+ const inputs = ["badvalue", "write"];
+
+ const app = () => {
+ normalizeKendraPermissions(inputs);
+ };
+
+ expect(app).toThrowError(/Invalid indexPermission value - valid values are "READ", "SUBMITFEEDBACK" and "WRITE"/);
+});
\ No newline at end of file
diff --git a/source/tools/cdk-integ-tools/package.json b/source/tools/cdk-integ-tools/package.json
index b621fb506..25e932120 100644
--- a/source/tools/cdk-integ-tools/package.json
+++ b/source/tools/cdk-integ-tools/package.json
@@ -11,7 +11,7 @@
},
"bin": {
"cdk-integ": "bin/cdk-integ",
- "cdk-integ-assert": "bin/cdk-integ-assert",
+ "cdk-integ-assert-v2": "bin/cdk-integ-assert",
"cdk-integ-assert-v2": "bin/cdk-integ-assert-v2"
},
"scripts": {
diff --git a/source/use_cases/aws-custom-glue-etl/package.json b/source/use_cases/aws-custom-glue-etl/package.json
index 5c5109f21..bf7d3c0df 100644
--- a/source/use_cases/aws-custom-glue-etl/package.json
+++ b/source/use_cases/aws-custom-glue-etl/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"build+lint+test": "npm run build && npm run lint && npm test && npm run integ-assert"
},
"dependencies": {
diff --git a/source/use_cases/aws-restaurant-management-demo/package.json b/source/use_cases/aws-restaurant-management-demo/package.json
index 97dd93028..fbfc26b9c 100644
--- a/source/use_cases/aws-restaurant-management-demo/package.json
+++ b/source/use_cases/aws-restaurant-management-demo/package.json
@@ -25,7 +25,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"build+lint+test": "npm run build && npm run lint && npm test && npm run integ-assert"
},
"devDependencies": {
diff --git a/source/use_cases/aws-s3-static-website/package.json b/source/use_cases/aws-s3-static-website/package.json
index 8f039306f..b529176ee 100644
--- a/source/use_cases/aws-s3-static-website/package.json
+++ b/source/use_cases/aws-s3-static-website/package.json
@@ -24,7 +24,7 @@
"watch": "tsc -b -w",
"integ": "cdk-integ",
"integ-no-clean": "cdk-integ --no-clean",
- "integ-assert": "cdk-integ-assert",
+ "integ-assert": "cdk-integ-assert-v2",
"build+lint+test": "npm run build && npm run lint && npm test && npm run integ-assert"
},
"dependencies": {