diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2cc1374f9..fa5d44040 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+## [1.158.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.11.0...v1.158.0) (2022-07-26)
+
+* Upgraded all patterns to CDK v1.158.0
+
## [1.157.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.8.0...v1.157.0) (2022-06-13)
* Upgraded all patterns to CDK v1.157.0
diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md
index 3809be8a1..87eae3d84 100644
--- a/CHANGELOG.v2.md
+++ b/CHANGELOG.v2.md
@@ -2,6 +2,24 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+## [2.11.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.10.0...v2.11.0) (2022-07-18)
+
+* Built upon underlying CDK version V2.24.0
+
+### Features
+
+* **aws-lambda-elasticsearch-kibana:** added VPC support ([#718](https://github.com/awslabs/aws-solutions-constructs/issues/718)) ([33e8f17](https://github.com/awslabs/aws-solutions-constructs/commit/33e8f17a1d1df5be78882a8a59b54d689fea1e82))
+
+## [2.10.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.9.0...v2.10.0) (2022-07-01)
+
+* Includes all functionality of V1.157.0
+* Built upon underlying CDK version V2.24.0
+
+## [2.9.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.8.0...v2.9.0) (2022-06-13)
+
+* Includes all functionality of V1.157.0
+* Built upon underlying CDK version V2.23.0
+
## [2.8.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.7.0...v2.8.0) (2022-05-20)
* Includes all functionality of V1.156.1
diff --git a/deployment/v2/align-version.js b/deployment/v2/align-version.js
index 430b2420b..d798207ef 100755
--- a/deployment/v2/align-version.js
+++ b/deployment/v2/align-version.js
@@ -10,7 +10,7 @@ const findVersion = process.argv[2];
const replaceVersion = process.argv[3];
// these versions need to be sourced from a config file
-const awsCdkLibVersion = '2.23.0';
+const awsCdkLibVersion = '2.24.0';
const constructsVersion = '10.0.0';
const MODULE_EXEMPTIONS = new Set([
'@aws-cdk/cloudformation-diff',
diff --git a/source/lerna.json b/source/lerna.json
index 700afd1b4..886f3571d 100644
--- a/source/lerna.json
+++ b/source/lerna.json
@@ -6,5 +6,5 @@
"./patterns/@aws-solutions-constructs/*"
],
"rejectCycles": "true",
- "version": "1.157.0"
+ "version": "1.158.0"
}
diff --git a/source/lerna.v2.json b/source/lerna.v2.json
index ebc92f06a..0a3022dea 100644
--- a/source/lerna.v2.json
+++ b/source/lerna.v2.json
@@ -6,5 +6,5 @@
"./patterns/@aws-solutions-constructs/*"
],
"rejectCycles": "true",
- "version": "2.8.0"
+ "version": "2.11.0"
}
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/README.md b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/README.md
index e8cedf598..8f1286c70 100755
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/README.md
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/README.md
@@ -25,6 +25,13 @@
## Overview
This AWS Solutions Construct implements the AWS Lambda function and Amazon Elasticsearch Service with the least privileged permissions.
+**Some cluster configurations (e.g VPC access) require the existence of the `AWSServiceRoleForAmazonElasticsearchService` Service-Linked Role in your account.**
+
+**You will need to create the service-linked role using the AWS CLI once in any account using this construct (it may have already been run to support other stacks):**
+```
+aws iam create-service-linked-role --aws-service-name es.amazonaws.com
+```
+
Here is a minimal deployable pattern definition:
Typescript
@@ -105,7 +112,10 @@ new LambdaToElasticSearchAndKibana(this, "sample",
|domainName|`string`|Domain name for the Cognito and the Elasticsearch Service|
|cognitoDomainName?|`string`|Optional Cognito Domain Name, if provided it will be used for Cognito Domain, and domainName will be used for the Elasticsearch Domain|
|createCloudWatchAlarms|`boolean`|Whether to create recommended CloudWatch alarms|
-|domainEndpointEnvironmentVariableName?|`string`|Optional Name for the Lambda function environment variable set to the domain endpoint. Default: DOMAIN_ENDPOINT |
+|domainEndpointEnvironmentVariableName?|`string`|Optional Name for the ElasticSearch domain endpoint environment variable set for the Lambda function.|
+|existingVpc?|[`ec2.IVpc`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_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/latest/docs/@aws-cdk_aws-ec2.Vpc.html#static-fromwbrlookupscope-id-options) method.|
+|vpcProps?|[`ec2.VpcProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_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 overrriden. 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
@@ -118,6 +128,7 @@ new LambdaToElasticSearchAndKibana(this, "sample",
|elasticsearchDomain|[`elasticsearch.CfnDomain`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-elasticsearch.CfnDomain.html)|Returns an instance of elasticsearch.CfnDomain created by the construct|
|elasticsearchDomain|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|Returns an instance of iam.Role created by the construct for elasticsearch.CfnDomain|
|cloudwatchAlarms?|[`cloudwatch.Alarm[]`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-cloudwatch.Alarm.html)|Returns a list of cloudwatch.Alarm created by the construct|
+|vpc?|[`ec2.IVpc`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_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.|
## Lambda Function
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/lib/index.ts
index 60ee7c346..5380ed086 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/lib/index.ts
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/lib/index.ts
@@ -20,6 +20,7 @@ import * as defaults from '@aws-solutions-constructs/core';
import { Construct } from '@aws-cdk/core';
import { Role } from '@aws-cdk/aws-iam';
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
+import * as ec2 from '@aws-cdk/aws-ec2';
/**
* @summary The properties for the CognitoToApiGatewayToLambda Construct
@@ -67,6 +68,24 @@ export interface LambdaToElasticSearchAndKibanaProps {
* @default - DOMAIN_ENDPOINT
*/
readonly domainEndpointEnvironmentVariableName?: string;
+ /**
+ * An existing VPC for the construct to use (construct will NOT create a new VPC in this case)
+ *
+ * @default - None
+ */
+ readonly existingVpc?: ec2.IVpc;
+ /**
+ * Properties to override default properties if deployVpc is true
+ *
+ * @default - DefaultIsolatedVpcProps() in vpc-defaults.ts
+ */
+ readonly vpcProps?: ec2.VpcProps;
+ /**
+ * Whether to deploy a new VPC
+ *
+ * @default - false
+ */
+ readonly deployVpc?: boolean;
}
export class LambdaToElasticSearchAndKibana extends Construct {
@@ -77,6 +96,7 @@ export class LambdaToElasticSearchAndKibana extends Construct {
public readonly elasticsearchRole: iam.Role;
public readonly lambdaFunction: lambda.Function;
public readonly cloudwatchAlarms?: cloudwatch.Alarm[];
+ public readonly vpc?: ec2.IVpc;
/**
* @summary Constructs a new instance of the CognitoToApiGatewayToLambda class.
@@ -90,37 +110,62 @@ export class LambdaToElasticSearchAndKibana extends Construct {
super(scope, id);
defaults.CheckProps(props);
+ if (props.vpcProps && !props.deployVpc) {
+ throw new Error("Error - deployVpc must be true when defining vpcProps");
+ }
+
+ if (props.lambdaFunctionProps?.vpc || props.lambdaFunctionProps?.vpcSubnets) {
+ throw new Error("Error - Define VPC using construct parameters not Lambda function props");
+ }
+
+ if (props.esDomainProps?.vpcOptions) {
+ throw new Error("Error - Define VPC using construct parameters not Elasticsearch props");
+ }
+
+ if (props.deployVpc || props.existingVpc) {
+ this.vpc = defaults.buildVpc(scope, {
+ defaultVpcProps: defaults.DefaultIsolatedVpcProps(),
+ existingVpc: props.existingVpc,
+ userVpcProps: props.vpcProps,
+ constructVpcProps: {
+ enableDnsHostnames: true,
+ enableDnsSupport: true,
+ },
+ });
+ }
+
this.lambdaFunction = defaults.buildLambdaFunction(this, {
existingLambdaObj: props.existingLambdaObj,
- lambdaFunctionProps: props.lambdaFunctionProps
+ lambdaFunctionProps: props.lambdaFunctionProps,
+ vpc: this.vpc
});
// Find the lambda service Role ARN
const lambdaFunctionRoleARN = this.lambdaFunction.role?.roleArn;
- this.userPool = defaults.buildUserPool(this);
- this.userPoolClient = defaults.buildUserPoolClient(this, this.userPool);
- this.identityPool = defaults.buildIdentityPool(this, this.userPool, this.userPoolClient);
-
- let cognitoDomainName = props.domainName;
-
- if (props.cognitoDomainName) {
- cognitoDomainName = props.cognitoDomainName;
- }
+ let cognitoAuthorizedRole: iam.Role;
- const cognitoAuthorizedRole: Role = defaults.setupCognitoForElasticSearch(this, cognitoDomainName, {
- userpool: this.userPool,
- identitypool: this.identityPool,
- userpoolclient: this.userPoolClient
- });
+ [this.userPool, this.userPoolClient, this.identityPool, cognitoAuthorizedRole] =
+ this.setupCognito(this, props.cognitoDomainName ?? props.domainName);
- [this.elasticsearchDomain, this.elasticsearchRole] = defaults.buildElasticSearch(this, props.domainName, {
+ const buildElasticSearchProps: any = {
userpool: this.userPool,
identitypool: this.identityPool,
cognitoAuthorizedRoleARN: cognitoAuthorizedRole.roleArn,
- serviceRoleARN: lambdaFunctionRoleARN}, props.esDomainProps);
+ serviceRoleARN: lambdaFunctionRoleARN,
+ vpc: this.vpc,
+ domainName: props.domainName,
+ clientDomainProps: props.esDomainProps
+ };
+
+ if (this.vpc) {
+ const securityGroupIds = defaults.getLambdaVpcSecurityGroupIds(this.lambdaFunction);
+ buildElasticSearchProps.securityGroupIds = securityGroupIds;
+ }
+
+ [this.elasticsearchDomain, this.elasticsearchRole] = defaults.buildElasticSearch(this, buildElasticSearchProps);
- // Add ES Domain to lambda envrionment variable
+ // Add ES Domain to lambda environment variable
const domainEndpointEnvironmentVariableName = props.domainEndpointEnvironmentVariableName || 'DOMAIN_ENDPOINT';
this.lambdaFunction.addEnvironment(domainEndpointEnvironmentVariableName, this.elasticsearchDomain.attrDomainEndpoint);
@@ -129,4 +174,18 @@ export class LambdaToElasticSearchAndKibana extends Construct {
this.cloudwatchAlarms = defaults.buildElasticSearchCWAlarms(this);
}
}
-}
+
+ setupCognito(scope: Construct, domainName: string): [cognito.UserPool, cognito.UserPoolClient, cognito.CfnIdentityPool, iam.Role] {
+ const userPool = defaults.buildUserPool(scope);
+ const userPoolClient = defaults.buildUserPoolClient(scope, userPool);
+ const identityPool = defaults.buildIdentityPool(scope, userPool, userPoolClient);
+
+ const cognitoAuthorizedRole: Role = defaults.setupCognitoForElasticSearch(scope, domainName, {
+ userpool: userPool,
+ identitypool: identityPool,
+ userpoolclient: userPoolClient
+ });
+
+ return [userPool, userPoolClient, identityPool, cognitoAuthorizedRole];
+ }
+}
\ No newline at end of file
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 e77215b4b..7c9104a72 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
@@ -56,6 +56,7 @@
"@aws-cdk/aws-lambda": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/aws-cognito": "0.0.0",
+ "@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-elasticsearch": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-cloudwatch": "0.0.0",
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithClusterConfig.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithClusterConfig.expected.json
new file mode 100644
index 000000000..be239d9b0
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithClusterConfig.expected.json
@@ -0,0 +1,979 @@
+{
+ "Resources": {
+ "testlambdaelasticsearchkibana5LambdaFunctionServiceRole26E43B12": {
+ "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"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana5LambdaFunctionServiceRoleDefaultPolicy8A628F4B": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testlambdaelasticsearchkibana5LambdaFunctionServiceRoleDefaultPolicy8A628F4B",
+ "Roles": [
+ {
+ "Ref": "testlambdaelasticsearchkibana5LambdaFunctionServiceRole26E43B12"
+ }
+ ]
+ },
+ "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."
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana5ReplaceDefaultSecurityGroupsecuritygroup375DBE67": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "deployFunctionWithClusterConfig/test-lambda-elasticsearch-kibana5/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"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana5LambdaFunction5382AC86": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
+ },
+ "S3Key": "67a9971e29baab2bde3043bb70ce5b53318b95429a1ce9b189cf65223e8682db.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5LambdaFunctionServiceRole26E43B12",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "DOMAIN_ENDPOINT": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5ElasticsearchDomain58F77409",
+ "DomainEndpoint"
+ ]
+ }
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5ReplaceDefaultSecurityGroupsecuritygroup375DBE67",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "testlambdaelasticsearchkibana5LambdaFunctionServiceRoleDefaultPolicy8A628F4B",
+ "testlambdaelasticsearchkibana5LambdaFunctionServiceRole26E43B12"
+ ],
+ "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"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana5CognitoUserPool4E321CD0": {
+ "Type": "AWS::Cognito::UserPool",
+ "Properties": {
+ "AccountRecoverySetting": {
+ "RecoveryMechanisms": [
+ {
+ "Name": "verified_phone_number",
+ "Priority": 1
+ },
+ {
+ "Name": "verified_email",
+ "Priority": 2
+ }
+ ]
+ },
+ "AdminCreateUserConfig": {
+ "AllowAdminCreateUserOnly": true
+ },
+ "EmailVerificationMessage": "The verification code to your new account is {####}",
+ "EmailVerificationSubject": "Verify your new account",
+ "SmsVerificationMessage": "The verification code to your new account is {####}",
+ "UserPoolAddOns": {
+ "AdvancedSecurityMode": "ENFORCED"
+ },
+ "VerificationMessageTemplate": {
+ "DefaultEmailOption": "CONFIRM_WITH_CODE",
+ "EmailMessage": "The verification code to your new account is {####}",
+ "EmailSubject": "Verify your new account",
+ "SmsMessage": "The verification code to your new account is {####}"
+ }
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain"
+ },
+ "testlambdaelasticsearchkibana5CognitoUserPoolClientB41FB91B": {
+ "Type": "AWS::Cognito::UserPoolClient",
+ "Properties": {
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibana5CognitoUserPool4E321CD0"
+ },
+ "AllowedOAuthFlows": [
+ "implicit",
+ "code"
+ ],
+ "AllowedOAuthFlowsUserPoolClient": true,
+ "AllowedOAuthScopes": [
+ "profile",
+ "phone",
+ "email",
+ "openid",
+ "aws.cognito.signin.user.admin"
+ ],
+ "CallbackURLs": [
+ "https://example.com"
+ ],
+ "SupportedIdentityProviders": [
+ "COGNITO"
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana5CognitoIdentityPool1B0A6046": {
+ "Type": "AWS::Cognito::IdentityPool",
+ "Properties": {
+ "AllowUnauthenticatedIdentities": false,
+ "CognitoIdentityProviders": [
+ {
+ "ClientId": {
+ "Ref": "testlambdaelasticsearchkibana5CognitoUserPoolClientB41FB91B"
+ },
+ "ProviderName": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5CognitoUserPool4E321CD0",
+ "ProviderName"
+ ]
+ },
+ "ServerSideTokenCheck": true
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana5UserPoolDomainE2693371": {
+ "Type": "AWS::Cognito::UserPoolDomain",
+ "Properties": {
+ "Domain": "deploytestwithclusterconfig",
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibana5CognitoUserPool4E321CD0"
+ }
+ },
+ "DependsOn": [
+ "testlambdaelasticsearchkibana5CognitoUserPool4E321CD0"
+ ]
+ },
+ "testlambdaelasticsearchkibana5CognitoAuthorizedRole784B2C89": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRoleWithWebIdentity",
+ "Condition": {
+ "StringEquals": {
+ "cognito-identity.amazonaws.com:aud": {
+ "Ref": "testlambdaelasticsearchkibana5CognitoIdentityPool1B0A6046"
+ }
+ },
+ "ForAnyValue:StringLike": {
+ "cognito-identity.amazonaws.com:amr": "authenticated"
+ }
+ },
+ "Effect": "Allow",
+ "Principal": {
+ "Federated": "cognito-identity.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "es:ESHttp*",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestwithclusterconfig/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "CognitoAccessPolicy"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana5IdentityPoolRoleMappingE355C8E9": {
+ "Type": "AWS::Cognito::IdentityPoolRoleAttachment",
+ "Properties": {
+ "IdentityPoolId": {
+ "Ref": "testlambdaelasticsearchkibana5CognitoIdentityPool1B0A6046"
+ },
+ "Roles": {
+ "authenticated": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5CognitoAuthorizedRole784B2C89",
+ "Arn"
+ ]
+ }
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana5CognitoKibanaConfigureRoleD2E8341A": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "es.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana5CognitoKibanaConfigureRolePolicy11560205": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "cognito-idp:DescribeUserPool",
+ "cognito-idp:CreateUserPoolClient",
+ "cognito-idp:DeleteUserPoolClient",
+ "cognito-idp:DescribeUserPoolClient",
+ "cognito-idp:AdminInitiateAuth",
+ "cognito-idp:AdminUserGlobalSignOut",
+ "cognito-idp:ListUserPoolClients",
+ "cognito-identity:DescribeIdentityPool",
+ "cognito-identity:UpdateIdentityPool",
+ "cognito-identity:SetIdentityPoolRoles",
+ "cognito-identity:GetIdentityPoolRoles",
+ "es:UpdateElasticsearchDomainConfig"
+ ],
+ "Effect": "Allow",
+ "Resource": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5CognitoUserPool4E321CD0",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:cognito-identity:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":identitypool/",
+ {
+ "Ref": "testlambdaelasticsearchkibana5CognitoIdentityPool1B0A6046"
+ }
+ ]
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestwithclusterconfig"
+ ]
+ ]
+ }
+ ]
+ },
+ {
+ "Action": "iam:PassRole",
+ "Condition": {
+ "StringLike": {
+ "iam:PassedToService": "cognito-identity.amazonaws.com"
+ }
+ },
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5CognitoKibanaConfigureRoleD2E8341A",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testlambdaelasticsearchkibana5CognitoKibanaConfigureRolePolicy11560205",
+ "Roles": [
+ {
+ "Ref": "testlambdaelasticsearchkibana5CognitoKibanaConfigureRoleD2E8341A"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana5ElasticsearchDomain58F77409": {
+ "Type": "AWS::Elasticsearch::Domain",
+ "Properties": {
+ "AccessPolicies": {
+ "Statement": [
+ {
+ "Action": "es:ESHttp*",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5CognitoAuthorizedRole784B2C89",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5LambdaFunctionServiceRole26E43B12",
+ "Arn"
+ ]
+ }
+ ]
+ },
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestwithclusterconfig/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "CognitoOptions": {
+ "Enabled": true,
+ "IdentityPoolId": {
+ "Ref": "testlambdaelasticsearchkibana5CognitoIdentityPool1B0A6046"
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5CognitoKibanaConfigureRoleD2E8341A",
+ "Arn"
+ ]
+ },
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibana5CognitoUserPool4E321CD0"
+ }
+ },
+ "DomainName": "deploytestwithclusterconfig",
+ "EBSOptions": {
+ "EBSEnabled": true,
+ "VolumeSize": 10
+ },
+ "ElasticsearchClusterConfig": {
+ "DedicatedMasterCount": 3,
+ "DedicatedMasterEnabled": true,
+ "InstanceCount": 2,
+ "ZoneAwarenessConfig": {
+ "AvailabilityZoneCount": 2
+ },
+ "ZoneAwarenessEnabled": true
+ },
+ "ElasticsearchVersion": "6.3",
+ "EncryptionAtRestOptions": {
+ "Enabled": true
+ },
+ "NodeToNodeEncryptionOptions": {
+ "Enabled": true
+ },
+ "SnapshotOptions": {
+ "AutomatedSnapshotStartHour": 1
+ },
+ "VPCOptions": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana5ReplaceDefaultSecurityGroupsecuritygroup375DBE67",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ }
+ ]
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W28",
+ "reason": "The ES Domain is passed dynamically as as parameter and explicitly specified to ensure that IAM policies are configured to lockdown access to this specific ES instance only"
+ },
+ {
+ "id": "W90",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana5StatusRedAlarm916EC672": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "At least one primary shard and its replicas are not allocated to a node. ",
+ "MetricName": "ClusterStatus.red",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana5StatusYellowAlarm7DCAF60A": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "At least one replica shard is not allocated to a node.",
+ "MetricName": "ClusterStatus.yellow",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana5FreeStorageSpaceTooLowAlarmEC2C0D7B": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "LessThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "A node in your cluster is down to 20 GiB of free storage space.",
+ "MetricName": "FreeStorageSpace",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Minimum",
+ "Threshold": 20000
+ }
+ },
+ "testlambdaelasticsearchkibana5IndexWritesBlockedTooHighAlarmD496CE3E": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Your cluster is blocking write requests.",
+ "MetricName": "ClusterIndexWritesBlocked",
+ "Namespace": "AWS/ES",
+ "Period": 300,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana5AutomatedSnapshotFailureTooHighAlarm97129BC4": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "An automated snapshot failed. This failure is often the result of a red cluster health status.",
+ "MetricName": "AutomatedSnapshotFailure",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana5CPUUtilizationTooHighAlarm3BAAA397": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 3,
+ "AlarmDescription": "100% CPU utilization is not uncommon, but sustained high usage is problematic. Consider using larger instance types or adding instances.",
+ "MetricName": "CPUUtilization",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 80
+ }
+ },
+ "testlambdaelasticsearchkibana5JVMMemoryPressureTooHighAlarm251AD583": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Average JVM memory pressure over last 15 minutes too high. Consider scaling vertically.",
+ "MetricName": "JVMMemoryPressure",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 80
+ }
+ },
+ "testlambdaelasticsearchkibana5MasterCPUUtilizationTooHighAlarm97A330CC": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 3,
+ "AlarmDescription": "Average CPU utilization over last 45 minutes too high. Consider using larger instance types for your dedicated master nodes.",
+ "MetricName": "MasterCPUUtilization",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 50
+ }
+ },
+ "testlambdaelasticsearchkibana5MasterJVMMemoryPressureTooHighAlarm7DABB351": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Average JVM memory pressure over last 15 minutes too high. Consider scaling vertically.",
+ "MetricName": "MasterJVMMemoryPressure",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 50
+ }
+ },
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "10.0.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithClusterConfig/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": "deployFunctionWithClusterConfig/Vpc/isolatedSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1RouteTableE442650B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithClusterConfig/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": "deployFunctionWithClusterConfig/Vpc/isolatedSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet2RouteTable334F9764": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithClusterConfig/Vpc/isolatedSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet2RouteTableAssociation25A4716F": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcisolatedSubnet2RouteTable334F9764"
+ },
+ "SubnetId": {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ }
+ }
+ },
+ "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": "deployFunctionWithClusterConfig/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": "deployFunctionWithClusterConfig/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",
+ "TrafficType": "ALL",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithClusterConfig/Vpc"
+ }
+ ]
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [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-elasticsearch-kibana/test/integ.deployFunctionWithClusterConfig.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithClusterConfig.ts
new file mode 100644
index 000000000..5c6e98009
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithClusterConfig.ts
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2022 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.
+ */
+
+/// !cdk-integ *
+import { App, Stack } from "@aws-cdk/core";
+import { LambdaToElasticSearchAndKibana } from "../lib";
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+
+// Setup
+const app = new App();
+const stack = new Stack(app, defaults.generateIntegStackName(__filename), {});
+
+const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler',
+};
+
+const esDomainProps = {
+ elasticsearchClusterConfig: {
+ dedicatedMasterEnabled: true,
+ dedicatedMasterCount: 3,
+ instanceCount: 2,
+ zoneAwarenessEnabled: true,
+ zoneAwarenessConfig: {
+ availabilityZoneCount: 2
+ }
+ }
+};
+
+new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana5', {
+ lambdaFunctionProps: lambdaProps,
+ domainName: "deploytestwithclusterconfig",
+ esDomainProps,
+ deployVpc: true,
+ vpcProps: {
+ maxAzs: 2
+ }
+});
+
+// Synth
+app.synth();
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithExistingVpc.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithExistingVpc.expected.json
new file mode 100644
index 000000000..18714b10a
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithExistingVpc.expected.json
@@ -0,0 +1,1412 @@
+{
+ "Resources": {
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "172.168.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet1Subnet5C2D37C4": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1a",
+ "CidrBlock": "172.168.0.0/19",
+ "MapPublicIpOnLaunch": true,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "Public"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Public"
+ },
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet1"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet1RouteTable6C95E38E": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet1RouteTableAssociation97140677": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPublicSubnet1RouteTable6C95E38E"
+ },
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet1Subnet5C2D37C4"
+ }
+ }
+ },
+ "VpcPublicSubnet1DefaultRoute3DA9E72A": {
+ "Type": "AWS::EC2::Route",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPublicSubnet1RouteTable6C95E38E"
+ },
+ "DestinationCidrBlock": "0.0.0.0/0",
+ "GatewayId": {
+ "Ref": "VpcIGWD7BA715C"
+ }
+ },
+ "DependsOn": [
+ "VpcVPCGWBF912B6E"
+ ]
+ },
+ "VpcPublicSubnet1EIPD7E02669": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet1NATGateway4D7517AA": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet1Subnet5C2D37C4"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet1EIPD7E02669",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet2Subnet691E08A3": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1b",
+ "CidrBlock": "172.168.32.0/19",
+ "MapPublicIpOnLaunch": true,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "Public"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Public"
+ },
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet2"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet2RouteTable94F7E489": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet2RouteTableAssociationDD5762D8": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPublicSubnet2RouteTable94F7E489"
+ },
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet2Subnet691E08A3"
+ }
+ }
+ },
+ "VpcPublicSubnet2DefaultRoute97F91067": {
+ "Type": "AWS::EC2::Route",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPublicSubnet2RouteTable94F7E489"
+ },
+ "DestinationCidrBlock": "0.0.0.0/0",
+ "GatewayId": {
+ "Ref": "VpcIGWD7BA715C"
+ }
+ },
+ "DependsOn": [
+ "VpcVPCGWBF912B6E"
+ ]
+ },
+ "VpcPublicSubnet2EIP3C605A87": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet2NATGateway9182C01D": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet2Subnet691E08A3"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet2EIP3C605A87",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet3SubnetBE12F0B6": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1c",
+ "CidrBlock": "172.168.64.0/19",
+ "MapPublicIpOnLaunch": true,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "Public"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Public"
+ },
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet3"
+ }
+ ]
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W33",
+ "reason": "Allow Public Subnets to have MapPublicIpOnLaunch set to true"
+ }
+ ]
+ }
+ }
+ },
+ "VpcPublicSubnet3RouteTable93458DBB": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet3RouteTableAssociation1F1EDF02": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPublicSubnet3RouteTable93458DBB"
+ },
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet3SubnetBE12F0B6"
+ }
+ }
+ },
+ "VpcPublicSubnet3DefaultRoute4697774F": {
+ "Type": "AWS::EC2::Route",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPublicSubnet3RouteTable93458DBB"
+ },
+ "DestinationCidrBlock": "0.0.0.0/0",
+ "GatewayId": {
+ "Ref": "VpcIGWD7BA715C"
+ }
+ },
+ "DependsOn": [
+ "VpcVPCGWBF912B6E"
+ ]
+ },
+ "VpcPublicSubnet3EIP3A666A23": {
+ "Type": "AWS::EC2::EIP",
+ "Properties": {
+ "Domain": "vpc",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPublicSubnet3NATGateway7640CD1D": {
+ "Type": "AWS::EC2::NatGateway",
+ "Properties": {
+ "SubnetId": {
+ "Ref": "VpcPublicSubnet3SubnetBE12F0B6"
+ },
+ "AllocationId": {
+ "Fn::GetAtt": [
+ "VpcPublicSubnet3EIP3A666A23",
+ "AllocationId"
+ ]
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PublicSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet1Subnet536B997A": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1a",
+ "CidrBlock": "172.168.96.0/19",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "Private"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Private"
+ },
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PrivateSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet1RouteTableB2C5B500": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PrivateSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet1RouteTableAssociation70C59FA6": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPrivateSubnet1RouteTableB2C5B500"
+ },
+ "SubnetId": {
+ "Ref": "VpcPrivateSubnet1Subnet536B997A"
+ }
+ }
+ },
+ "VpcPrivateSubnet1DefaultRouteBE02A9ED": {
+ "Type": "AWS::EC2::Route",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPrivateSubnet1RouteTableB2C5B500"
+ },
+ "DestinationCidrBlock": "0.0.0.0/0",
+ "NatGatewayId": {
+ "Ref": "VpcPublicSubnet1NATGateway4D7517AA"
+ }
+ }
+ },
+ "VpcPrivateSubnet2Subnet3788AAA1": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1b",
+ "CidrBlock": "172.168.128.0/19",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "Private"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Private"
+ },
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PrivateSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet2RouteTableA678073B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PrivateSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet2RouteTableAssociationA89CAD56": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPrivateSubnet2RouteTableA678073B"
+ },
+ "SubnetId": {
+ "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+ }
+ }
+ },
+ "VpcPrivateSubnet2DefaultRoute060D2087": {
+ "Type": "AWS::EC2::Route",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPrivateSubnet2RouteTableA678073B"
+ },
+ "DestinationCidrBlock": "0.0.0.0/0",
+ "NatGatewayId": {
+ "Ref": "VpcPublicSubnet2NATGateway9182C01D"
+ }
+ }
+ },
+ "VpcPrivateSubnet3SubnetF258B56E": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1c",
+ "CidrBlock": "172.168.160.0/19",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "Private"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Private"
+ },
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PrivateSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet3RouteTableD98824C7": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc/PrivateSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcPrivateSubnet3RouteTableAssociation16BDDC43": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPrivateSubnet3RouteTableD98824C7"
+ },
+ "SubnetId": {
+ "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+ }
+ }
+ },
+ "VpcPrivateSubnet3DefaultRoute94B74F0D": {
+ "Type": "AWS::EC2::Route",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcPrivateSubnet3RouteTableD98824C7"
+ },
+ "DestinationCidrBlock": "0.0.0.0/0",
+ "NatGatewayId": {
+ "Ref": "VpcPublicSubnet3NATGateway7640CD1D"
+ }
+ }
+ },
+ "VpcIGWD7BA715C": {
+ "Type": "AWS::EC2::InternetGateway",
+ "Properties": {
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcVPCGWBF912B6E": {
+ "Type": "AWS::EC2::VPCGatewayAttachment",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "InternetGatewayId": {
+ "Ref": "VpcIGWD7BA715C"
+ }
+ }
+ },
+ "VpcFlowLogIAMRole6A475D41": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "vpc-flow-logs.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/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": "deployFunctionWithExistingVpc/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",
+ "TrafficType": "ALL",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithExistingVpc/Vpc"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana4LambdaFunctionServiceRoleA52BB7F9": {
+ "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"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana4LambdaFunctionServiceRoleDefaultPolicyA5AD88E5": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testlambdaelasticsearchkibana4LambdaFunctionServiceRoleDefaultPolicyA5AD88E5",
+ "Roles": [
+ {
+ "Ref": "testlambdaelasticsearchkibana4LambdaFunctionServiceRoleA52BB7F9"
+ }
+ ]
+ },
+ "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."
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana4ReplaceDefaultSecurityGroupsecuritygroupA79E2B92": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "deployFunctionWithExistingVpc/test-lambda-elasticsearch-kibana4/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"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana4LambdaFunction2C5856DF": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": "cdk-hnb659fds-assets-12345678-test-region",
+ "S3Key": "67a9971e29baab2bde3043bb70ce5b53318b95429a1ce9b189cf65223e8682db.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4LambdaFunctionServiceRoleA52BB7F9",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "DOMAIN_ENDPOINT": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4ElasticsearchDomainC32C1BE6",
+ "DomainEndpoint"
+ ]
+ }
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4ReplaceDefaultSecurityGroupsecuritygroupA79E2B92",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcPrivateSubnet1Subnet536B997A"
+ },
+ {
+ "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+ },
+ {
+ "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "testlambdaelasticsearchkibana4LambdaFunctionServiceRoleDefaultPolicyA5AD88E5",
+ "testlambdaelasticsearchkibana4LambdaFunctionServiceRoleA52BB7F9"
+ ],
+ "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"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana4CognitoUserPool37A5CDE1": {
+ "Type": "AWS::Cognito::UserPool",
+ "Properties": {
+ "AccountRecoverySetting": {
+ "RecoveryMechanisms": [
+ {
+ "Name": "verified_phone_number",
+ "Priority": 1
+ },
+ {
+ "Name": "verified_email",
+ "Priority": 2
+ }
+ ]
+ },
+ "AdminCreateUserConfig": {
+ "AllowAdminCreateUserOnly": true
+ },
+ "EmailVerificationMessage": "The verification code to your new account is {####}",
+ "EmailVerificationSubject": "Verify your new account",
+ "SmsVerificationMessage": "The verification code to your new account is {####}",
+ "UserPoolAddOns": {
+ "AdvancedSecurityMode": "ENFORCED"
+ },
+ "VerificationMessageTemplate": {
+ "DefaultEmailOption": "CONFIRM_WITH_CODE",
+ "EmailMessage": "The verification code to your new account is {####}",
+ "EmailSubject": "Verify your new account",
+ "SmsMessage": "The verification code to your new account is {####}"
+ }
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain"
+ },
+ "testlambdaelasticsearchkibana4CognitoUserPoolClientABBF34C4": {
+ "Type": "AWS::Cognito::UserPoolClient",
+ "Properties": {
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibana4CognitoUserPool37A5CDE1"
+ },
+ "AllowedOAuthFlows": [
+ "implicit",
+ "code"
+ ],
+ "AllowedOAuthFlowsUserPoolClient": true,
+ "AllowedOAuthScopes": [
+ "profile",
+ "phone",
+ "email",
+ "openid",
+ "aws.cognito.signin.user.admin"
+ ],
+ "CallbackURLs": [
+ "https://example.com"
+ ],
+ "SupportedIdentityProviders": [
+ "COGNITO"
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana4CognitoIdentityPool76EE9793": {
+ "Type": "AWS::Cognito::IdentityPool",
+ "Properties": {
+ "AllowUnauthenticatedIdentities": false,
+ "CognitoIdentityProviders": [
+ {
+ "ClientId": {
+ "Ref": "testlambdaelasticsearchkibana4CognitoUserPoolClientABBF34C4"
+ },
+ "ProviderName": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4CognitoUserPool37A5CDE1",
+ "ProviderName"
+ ]
+ },
+ "ServerSideTokenCheck": true
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana4UserPoolDomain4CAAF2F6": {
+ "Type": "AWS::Cognito::UserPoolDomain",
+ "Properties": {
+ "Domain": "deploytestwithexistingvpc",
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibana4CognitoUserPool37A5CDE1"
+ }
+ },
+ "DependsOn": [
+ "testlambdaelasticsearchkibana4CognitoUserPool37A5CDE1"
+ ]
+ },
+ "testlambdaelasticsearchkibana4CognitoAuthorizedRoleA7D6B392": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRoleWithWebIdentity",
+ "Condition": {
+ "StringEquals": {
+ "cognito-identity.amazonaws.com:aud": {
+ "Ref": "testlambdaelasticsearchkibana4CognitoIdentityPool76EE9793"
+ }
+ },
+ "ForAnyValue:StringLike": {
+ "cognito-identity.amazonaws.com:amr": "authenticated"
+ }
+ },
+ "Effect": "Allow",
+ "Principal": {
+ "Federated": "cognito-identity.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "es:ESHttp*",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestwithexistingvpc/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "CognitoAccessPolicy"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana4IdentityPoolRoleMapping9378D177": {
+ "Type": "AWS::Cognito::IdentityPoolRoleAttachment",
+ "Properties": {
+ "IdentityPoolId": {
+ "Ref": "testlambdaelasticsearchkibana4CognitoIdentityPool76EE9793"
+ },
+ "Roles": {
+ "authenticated": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4CognitoAuthorizedRoleA7D6B392",
+ "Arn"
+ ]
+ }
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana4CognitoKibanaConfigureRole6A058B80": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "es.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana4CognitoKibanaConfigureRolePolicy1615E937": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "cognito-idp:DescribeUserPool",
+ "cognito-idp:CreateUserPoolClient",
+ "cognito-idp:DeleteUserPoolClient",
+ "cognito-idp:DescribeUserPoolClient",
+ "cognito-idp:AdminInitiateAuth",
+ "cognito-idp:AdminUserGlobalSignOut",
+ "cognito-idp:ListUserPoolClients",
+ "cognito-identity:DescribeIdentityPool",
+ "cognito-identity:UpdateIdentityPool",
+ "cognito-identity:SetIdentityPoolRoles",
+ "cognito-identity:GetIdentityPoolRoles",
+ "es:UpdateElasticsearchDomainConfig"
+ ],
+ "Effect": "Allow",
+ "Resource": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4CognitoUserPool37A5CDE1",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:cognito-identity:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":identitypool/",
+ {
+ "Ref": "testlambdaelasticsearchkibana4CognitoIdentityPool76EE9793"
+ }
+ ]
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestwithexistingvpc"
+ ]
+ ]
+ }
+ ]
+ },
+ {
+ "Action": "iam:PassRole",
+ "Condition": {
+ "StringLike": {
+ "iam:PassedToService": "cognito-identity.amazonaws.com"
+ }
+ },
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4CognitoKibanaConfigureRole6A058B80",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testlambdaelasticsearchkibana4CognitoKibanaConfigureRolePolicy1615E937",
+ "Roles": [
+ {
+ "Ref": "testlambdaelasticsearchkibana4CognitoKibanaConfigureRole6A058B80"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana4ElasticsearchDomainC32C1BE6": {
+ "Type": "AWS::Elasticsearch::Domain",
+ "Properties": {
+ "AccessPolicies": {
+ "Statement": [
+ {
+ "Action": "es:ESHttp*",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4CognitoAuthorizedRoleA7D6B392",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4LambdaFunctionServiceRoleA52BB7F9",
+ "Arn"
+ ]
+ }
+ ]
+ },
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestwithexistingvpc/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "CognitoOptions": {
+ "Enabled": true,
+ "IdentityPoolId": {
+ "Ref": "testlambdaelasticsearchkibana4CognitoIdentityPool76EE9793"
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4CognitoKibanaConfigureRole6A058B80",
+ "Arn"
+ ]
+ },
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibana4CognitoUserPool37A5CDE1"
+ }
+ },
+ "DomainName": "deploytestwithexistingvpc",
+ "EBSOptions": {
+ "EBSEnabled": true,
+ "VolumeSize": 10
+ },
+ "ElasticsearchClusterConfig": {
+ "DedicatedMasterCount": 3,
+ "DedicatedMasterEnabled": true,
+ "InstanceCount": 3,
+ "ZoneAwarenessConfig": {
+ "AvailabilityZoneCount": 3
+ },
+ "ZoneAwarenessEnabled": true
+ },
+ "ElasticsearchVersion": "6.3",
+ "EncryptionAtRestOptions": {
+ "Enabled": true
+ },
+ "NodeToNodeEncryptionOptions": {
+ "Enabled": true
+ },
+ "SnapshotOptions": {
+ "AutomatedSnapshotStartHour": 1
+ },
+ "VPCOptions": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana4ReplaceDefaultSecurityGroupsecuritygroupA79E2B92",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcPrivateSubnet1Subnet536B997A"
+ },
+ {
+ "Ref": "VpcPrivateSubnet2Subnet3788AAA1"
+ },
+ {
+ "Ref": "VpcPrivateSubnet3SubnetF258B56E"
+ }
+ ]
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W28",
+ "reason": "The ES Domain is passed dynamically as as parameter and explicitly specified to ensure that IAM policies are configured to lockdown access to this specific ES instance only"
+ },
+ {
+ "id": "W90",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana4StatusRedAlarm56DEE5C7": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "At least one primary shard and its replicas are not allocated to a node. ",
+ "MetricName": "ClusterStatus.red",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana4StatusYellowAlarm810B4F9E": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "At least one replica shard is not allocated to a node.",
+ "MetricName": "ClusterStatus.yellow",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana4FreeStorageSpaceTooLowAlarmF3FB31EA": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "LessThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "A node in your cluster is down to 20 GiB of free storage space.",
+ "MetricName": "FreeStorageSpace",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Minimum",
+ "Threshold": 20000
+ }
+ },
+ "testlambdaelasticsearchkibana4IndexWritesBlockedTooHighAlarmF2968C92": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Your cluster is blocking write requests.",
+ "MetricName": "ClusterIndexWritesBlocked",
+ "Namespace": "AWS/ES",
+ "Period": 300,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana4AutomatedSnapshotFailureTooHighAlarm53EB1ABB": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "An automated snapshot failed. This failure is often the result of a red cluster health status.",
+ "MetricName": "AutomatedSnapshotFailure",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana4CPUUtilizationTooHighAlarm29B67D10": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 3,
+ "AlarmDescription": "100% CPU utilization is not uncommon, but sustained high usage is problematic. Consider using larger instance types or adding instances.",
+ "MetricName": "CPUUtilization",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 80
+ }
+ },
+ "testlambdaelasticsearchkibana4JVMMemoryPressureTooHighAlarm9DDED711": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Average JVM memory pressure over last 15 minutes too high. Consider scaling vertically.",
+ "MetricName": "JVMMemoryPressure",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 80
+ }
+ },
+ "testlambdaelasticsearchkibana4MasterCPUUtilizationTooHighAlarmE66867F2": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 3,
+ "AlarmDescription": "Average CPU utilization over last 45 minutes too high. Consider using larger instance types for your dedicated master nodes.",
+ "MetricName": "MasterCPUUtilization",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 50
+ }
+ },
+ "testlambdaelasticsearchkibana4MasterJVMMemoryPressureTooHighAlarm83E1822E": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Average JVM memory pressure over last 15 minutes too high. Consider scaling vertically.",
+ "MetricName": "MasterJVMMemoryPressure",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 50
+ }
+ }
+ },
+ "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-elasticsearch-kibana/test/integ.deployFunctionWithExistingVpc.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithExistingVpc.ts
new file mode 100644
index 000000000..c4e481177
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithExistingVpc.ts
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2022 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.
+ */
+
+/// !cdk-integ *
+import { App, Stack } from "@aws-cdk/core";
+import { LambdaToElasticSearchAndKibana } from "../lib";
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+
+const app = new App();
+const stack = new Stack(app, defaults.generateIntegStackName(__filename), {
+ env: {
+ region: process.env.CDK_DEFAULT_REGION,
+ account: process.env.CDK_DEFAULT_ACCOUNT,
+ }
+});
+
+// Create VPC
+const vpc = defaults.getTestVpc(stack);
+
+const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler',
+};
+
+new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana4', {
+ lambdaFunctionProps: lambdaProps,
+ domainName: "deploytestwithexistingvpc",
+ existingVpc: vpc
+});
+
+// Synth
+app.synth();
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithVpcProps.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithVpcProps.expected.json
new file mode 100644
index 000000000..aceea9831
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithVpcProps.expected.json
@@ -0,0 +1,1033 @@
+{
+ "Resources": {
+ "testlambdaelasticsearchkibana3LambdaFunctionServiceRoleA3C1E07E": {
+ "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"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana3LambdaFunctionServiceRoleDefaultPolicyA148ED7D": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testlambdaelasticsearchkibana3LambdaFunctionServiceRoleDefaultPolicyA148ED7D",
+ "Roles": [
+ {
+ "Ref": "testlambdaelasticsearchkibana3LambdaFunctionServiceRoleA3C1E07E"
+ }
+ ]
+ },
+ "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."
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana3ReplaceDefaultSecurityGroupsecuritygroupEB497E1A": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "deployFunctionWithVpcProps/test-lambda-elasticsearch-kibana3/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"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana3LambdaFunctionE377CF86": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": "cdk-hnb659fds-assets-12345678-test-region",
+ "S3Key": "67a9971e29baab2bde3043bb70ce5b53318b95429a1ce9b189cf65223e8682db.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3LambdaFunctionServiceRoleA3C1E07E",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "DOMAIN_ENDPOINT": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3ElasticsearchDomain268DE741",
+ "DomainEndpoint"
+ ]
+ }
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3ReplaceDefaultSecurityGroupsecuritygroupEB497E1A",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ "Ref": "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "testlambdaelasticsearchkibana3LambdaFunctionServiceRoleDefaultPolicyA148ED7D",
+ "testlambdaelasticsearchkibana3LambdaFunctionServiceRoleA3C1E07E"
+ ],
+ "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"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana3CognitoUserPoolEF0D5793": {
+ "Type": "AWS::Cognito::UserPool",
+ "Properties": {
+ "AccountRecoverySetting": {
+ "RecoveryMechanisms": [
+ {
+ "Name": "verified_phone_number",
+ "Priority": 1
+ },
+ {
+ "Name": "verified_email",
+ "Priority": 2
+ }
+ ]
+ },
+ "AdminCreateUserConfig": {
+ "AllowAdminCreateUserOnly": true
+ },
+ "EmailVerificationMessage": "The verification code to your new account is {####}",
+ "EmailVerificationSubject": "Verify your new account",
+ "SmsVerificationMessage": "The verification code to your new account is {####}",
+ "UserPoolAddOns": {
+ "AdvancedSecurityMode": "ENFORCED"
+ },
+ "VerificationMessageTemplate": {
+ "DefaultEmailOption": "CONFIRM_WITH_CODE",
+ "EmailMessage": "The verification code to your new account is {####}",
+ "EmailSubject": "Verify your new account",
+ "SmsMessage": "The verification code to your new account is {####}"
+ }
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain"
+ },
+ "testlambdaelasticsearchkibana3CognitoUserPoolClient89D3C6A0": {
+ "Type": "AWS::Cognito::UserPoolClient",
+ "Properties": {
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibana3CognitoUserPoolEF0D5793"
+ },
+ "AllowedOAuthFlows": [
+ "implicit",
+ "code"
+ ],
+ "AllowedOAuthFlowsUserPoolClient": true,
+ "AllowedOAuthScopes": [
+ "profile",
+ "phone",
+ "email",
+ "openid",
+ "aws.cognito.signin.user.admin"
+ ],
+ "CallbackURLs": [
+ "https://example.com"
+ ],
+ "SupportedIdentityProviders": [
+ "COGNITO"
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana3CognitoIdentityPool48956B3D": {
+ "Type": "AWS::Cognito::IdentityPool",
+ "Properties": {
+ "AllowUnauthenticatedIdentities": false,
+ "CognitoIdentityProviders": [
+ {
+ "ClientId": {
+ "Ref": "testlambdaelasticsearchkibana3CognitoUserPoolClient89D3C6A0"
+ },
+ "ProviderName": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3CognitoUserPoolEF0D5793",
+ "ProviderName"
+ ]
+ },
+ "ServerSideTokenCheck": true
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana3UserPoolDomain11719EB9": {
+ "Type": "AWS::Cognito::UserPoolDomain",
+ "Properties": {
+ "Domain": "deploytestwithvpcprops",
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibana3CognitoUserPoolEF0D5793"
+ }
+ },
+ "DependsOn": [
+ "testlambdaelasticsearchkibana3CognitoUserPoolEF0D5793"
+ ]
+ },
+ "testlambdaelasticsearchkibana3CognitoAuthorizedRole2E055088": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRoleWithWebIdentity",
+ "Condition": {
+ "StringEquals": {
+ "cognito-identity.amazonaws.com:aud": {
+ "Ref": "testlambdaelasticsearchkibana3CognitoIdentityPool48956B3D"
+ }
+ },
+ "ForAnyValue:StringLike": {
+ "cognito-identity.amazonaws.com:amr": "authenticated"
+ }
+ },
+ "Effect": "Allow",
+ "Principal": {
+ "Federated": "cognito-identity.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "es:ESHttp*",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestwithvpcprops/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "CognitoAccessPolicy"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana3IdentityPoolRoleMappingDF65C594": {
+ "Type": "AWS::Cognito::IdentityPoolRoleAttachment",
+ "Properties": {
+ "IdentityPoolId": {
+ "Ref": "testlambdaelasticsearchkibana3CognitoIdentityPool48956B3D"
+ },
+ "Roles": {
+ "authenticated": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3CognitoAuthorizedRole2E055088",
+ "Arn"
+ ]
+ }
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana3CognitoKibanaConfigureRoleE8D8BE00": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "es.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana3CognitoKibanaConfigureRolePolicyF1017A7A": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "cognito-idp:DescribeUserPool",
+ "cognito-idp:CreateUserPoolClient",
+ "cognito-idp:DeleteUserPoolClient",
+ "cognito-idp:DescribeUserPoolClient",
+ "cognito-idp:AdminInitiateAuth",
+ "cognito-idp:AdminUserGlobalSignOut",
+ "cognito-idp:ListUserPoolClients",
+ "cognito-identity:DescribeIdentityPool",
+ "cognito-identity:UpdateIdentityPool",
+ "cognito-identity:SetIdentityPoolRoles",
+ "cognito-identity:GetIdentityPoolRoles",
+ "es:UpdateElasticsearchDomainConfig"
+ ],
+ "Effect": "Allow",
+ "Resource": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3CognitoUserPoolEF0D5793",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:cognito-identity:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":identitypool/",
+ {
+ "Ref": "testlambdaelasticsearchkibana3CognitoIdentityPool48956B3D"
+ }
+ ]
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestwithvpcprops"
+ ]
+ ]
+ }
+ ]
+ },
+ {
+ "Action": "iam:PassRole",
+ "Condition": {
+ "StringLike": {
+ "iam:PassedToService": "cognito-identity.amazonaws.com"
+ }
+ },
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3CognitoKibanaConfigureRoleE8D8BE00",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testlambdaelasticsearchkibana3CognitoKibanaConfigureRolePolicyF1017A7A",
+ "Roles": [
+ {
+ "Ref": "testlambdaelasticsearchkibana3CognitoKibanaConfigureRoleE8D8BE00"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibana3ElasticsearchDomain268DE741": {
+ "Type": "AWS::Elasticsearch::Domain",
+ "Properties": {
+ "AccessPolicies": {
+ "Statement": [
+ {
+ "Action": "es:ESHttp*",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3CognitoAuthorizedRole2E055088",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3LambdaFunctionServiceRoleA3C1E07E",
+ "Arn"
+ ]
+ }
+ ]
+ },
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestwithvpcprops/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "CognitoOptions": {
+ "Enabled": true,
+ "IdentityPoolId": {
+ "Ref": "testlambdaelasticsearchkibana3CognitoIdentityPool48956B3D"
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3CognitoKibanaConfigureRoleE8D8BE00",
+ "Arn"
+ ]
+ },
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibana3CognitoUserPoolEF0D5793"
+ }
+ },
+ "DomainName": "deploytestwithvpcprops",
+ "EBSOptions": {
+ "EBSEnabled": true,
+ "VolumeSize": 10
+ },
+ "ElasticsearchClusterConfig": {
+ "DedicatedMasterCount": 3,
+ "DedicatedMasterEnabled": true,
+ "InstanceCount": 3,
+ "ZoneAwarenessConfig": {
+ "AvailabilityZoneCount": 3
+ },
+ "ZoneAwarenessEnabled": true
+ },
+ "ElasticsearchVersion": "6.3",
+ "EncryptionAtRestOptions": {
+ "Enabled": true
+ },
+ "NodeToNodeEncryptionOptions": {
+ "Enabled": true
+ },
+ "SnapshotOptions": {
+ "AutomatedSnapshotStartHour": 1
+ },
+ "VPCOptions": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibana3ReplaceDefaultSecurityGroupsecuritygroupEB497E1A",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ "Ref": "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ]
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W28",
+ "reason": "The ES Domain is passed dynamically as as parameter and explicitly specified to ensure that IAM policies are configured to lockdown access to this specific ES instance only"
+ },
+ {
+ "id": "W90",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibana3StatusRedAlarmFECC950E": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "At least one primary shard and its replicas are not allocated to a node. ",
+ "MetricName": "ClusterStatus.red",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana3StatusYellowAlarmC7FC4662": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "At least one replica shard is not allocated to a node.",
+ "MetricName": "ClusterStatus.yellow",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana3FreeStorageSpaceTooLowAlarmB9AD5427": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "LessThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "A node in your cluster is down to 20 GiB of free storage space.",
+ "MetricName": "FreeStorageSpace",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Minimum",
+ "Threshold": 20000
+ }
+ },
+ "testlambdaelasticsearchkibana3IndexWritesBlockedTooHighAlarm36E1D418": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Your cluster is blocking write requests.",
+ "MetricName": "ClusterIndexWritesBlocked",
+ "Namespace": "AWS/ES",
+ "Period": 300,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana3AutomatedSnapshotFailureTooHighAlarmA83D4971": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "An automated snapshot failed. This failure is often the result of a red cluster health status.",
+ "MetricName": "AutomatedSnapshotFailure",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibana3CPUUtilizationTooHighAlarmE77470C0": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 3,
+ "AlarmDescription": "100% CPU utilization is not uncommon, but sustained high usage is problematic. Consider using larger instance types or adding instances.",
+ "MetricName": "CPUUtilization",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 80
+ }
+ },
+ "testlambdaelasticsearchkibana3JVMMemoryPressureTooHighAlarm084D2991": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Average JVM memory pressure over last 15 minutes too high. Consider scaling vertically.",
+ "MetricName": "JVMMemoryPressure",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 80
+ }
+ },
+ "testlambdaelasticsearchkibana3MasterCPUUtilizationTooHighAlarm3331F781": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 3,
+ "AlarmDescription": "Average CPU utilization over last 45 minutes too high. Consider using larger instance types for your dedicated master nodes.",
+ "MetricName": "MasterCPUUtilization",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 50
+ }
+ },
+ "testlambdaelasticsearchkibana3MasterJVMMemoryPressureTooHighAlarm149E8D14": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Average JVM memory pressure over last 15 minutes too high. Consider scaling vertically.",
+ "MetricName": "MasterJVMMemoryPressure",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 50
+ }
+ },
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "172.168.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithVpcProps/Vpc"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1SubnetE62B1B9B": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "AvailabilityZone": "test-region-1a",
+ "CidrBlock": "172.168.0.0/18",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "isolated"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Isolated"
+ },
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithVpcProps/Vpc/isolatedSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1RouteTableE442650B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithVpcProps/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": "172.168.64.0/18",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "isolated"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Isolated"
+ },
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithVpcProps/Vpc/isolatedSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet2RouteTable334F9764": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithVpcProps/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": "172.168.128.0/18",
+ "MapPublicIpOnLaunch": false,
+ "Tags": [
+ {
+ "Key": "aws-cdk:subnet-name",
+ "Value": "isolated"
+ },
+ {
+ "Key": "aws-cdk:subnet-type",
+ "Value": "Isolated"
+ },
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithVpcProps/Vpc/isolatedSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet3RouteTableA2F6BBC0": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithVpcProps/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": "deployFunctionWithVpcProps/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": "deployFunctionWithVpcProps/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",
+ "TrafficType": "ALL",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployFunctionWithVpcProps/Vpc"
+ }
+ ]
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [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-elasticsearch-kibana/test/integ.deployFunctionWithVpcProps.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithVpcProps.ts
new file mode 100644
index 000000000..8242ff52f
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployFunctionWithVpcProps.ts
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2022 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 { App, Stack } from "@aws-cdk/core";
+import { LambdaToElasticSearchAndKibana } from "../lib";
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+
+// Setup
+const app = new App();
+const stack = new Stack(app, defaults.generateIntegStackName(__filename), {
+ env: {
+ region: process.env.CDK_DEFAULT_REGION,
+ account: process.env.CDK_DEFAULT_ACCOUNT,
+ }
+});
+
+const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler',
+};
+
+new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana3', {
+ lambdaFunctionProps: lambdaProps,
+ domainName: "deploytestwithvpcprops",
+ deployVpc: true,
+ vpcProps: {
+ cidr: '172.168.0.0/16',
+ }
+});
+
+// Synth
+app.synth();
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployToFiveZones.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployToFiveZones.expected.json
new file mode 100644
index 000000000..107822941
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployToFiveZones.expected.json
@@ -0,0 +1,1035 @@
+{
+ "Resources": {
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRole3AFFEAA2": {
+ "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"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRoleDefaultPolicy199413EB": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testlambdaelasticsearchkibanaLambdaFunctionServiceRoleDefaultPolicy199413EB",
+ "Roles": [
+ {
+ "Ref": "testlambdaelasticsearchkibanaLambdaFunctionServiceRole3AFFEAA2"
+ }
+ ]
+ },
+ "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."
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaReplaceDefaultSecurityGroupsecuritygroup9C517245": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "deployToFiveZones/test-lambda-elasticsearch-kibana/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"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaLambdaFunction601D26D3": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
+ },
+ "S3Key": "67a9971e29baab2bde3043bb70ce5b53318b95429a1ce9b189cf65223e8682db.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRole3AFFEAA2",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "DOMAIN_ENDPOINT": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaElasticsearchDomain50D5F86E",
+ "DomainEndpoint"
+ ]
+ }
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaReplaceDefaultSecurityGroupsecuritygroup9C517245",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ "Ref": "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRoleDefaultPolicy199413EB",
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRole3AFFEAA2"
+ ],
+ "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"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaCognitoUserPool9537802B": {
+ "Type": "AWS::Cognito::UserPool",
+ "Properties": {
+ "AccountRecoverySetting": {
+ "RecoveryMechanisms": [
+ {
+ "Name": "verified_phone_number",
+ "Priority": 1
+ },
+ {
+ "Name": "verified_email",
+ "Priority": 2
+ }
+ ]
+ },
+ "AdminCreateUserConfig": {
+ "AllowAdminCreateUserOnly": true
+ },
+ "EmailVerificationMessage": "The verification code to your new account is {####}",
+ "EmailVerificationSubject": "Verify your new account",
+ "SmsVerificationMessage": "The verification code to your new account is {####}",
+ "UserPoolAddOns": {
+ "AdvancedSecurityMode": "ENFORCED"
+ },
+ "VerificationMessageTemplate": {
+ "DefaultEmailOption": "CONFIRM_WITH_CODE",
+ "EmailMessage": "The verification code to your new account is {####}",
+ "EmailSubject": "Verify your new account",
+ "SmsMessage": "The verification code to your new account is {####}"
+ }
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain"
+ },
+ "testlambdaelasticsearchkibanaCognitoUserPoolClient8F70A2AA": {
+ "Type": "AWS::Cognito::UserPoolClient",
+ "Properties": {
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoUserPool9537802B"
+ },
+ "AllowedOAuthFlows": [
+ "implicit",
+ "code"
+ ],
+ "AllowedOAuthFlowsUserPoolClient": true,
+ "AllowedOAuthScopes": [
+ "profile",
+ "phone",
+ "email",
+ "openid",
+ "aws.cognito.signin.user.admin"
+ ],
+ "CallbackURLs": [
+ "https://example.com"
+ ],
+ "SupportedIdentityProviders": [
+ "COGNITO"
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibanaCognitoIdentityPoolC48068F0": {
+ "Type": "AWS::Cognito::IdentityPool",
+ "Properties": {
+ "AllowUnauthenticatedIdentities": false,
+ "CognitoIdentityProviders": [
+ {
+ "ClientId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoUserPoolClient8F70A2AA"
+ },
+ "ProviderName": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoUserPool9537802B",
+ "ProviderName"
+ ]
+ },
+ "ServerSideTokenCheck": true
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibanaUserPoolDomainB9BDF063": {
+ "Type": "AWS::Cognito::UserPoolDomain",
+ "Properties": {
+ "Domain": "deploytestfivezones",
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoUserPool9537802B"
+ }
+ },
+ "DependsOn": [
+ "testlambdaelasticsearchkibanaCognitoUserPool9537802B"
+ ]
+ },
+ "testlambdaelasticsearchkibanaCognitoAuthorizedRole88FAFCFA": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRoleWithWebIdentity",
+ "Condition": {
+ "StringEquals": {
+ "cognito-identity.amazonaws.com:aud": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoIdentityPoolC48068F0"
+ }
+ },
+ "ForAnyValue:StringLike": {
+ "cognito-identity.amazonaws.com:amr": "authenticated"
+ }
+ },
+ "Effect": "Allow",
+ "Principal": {
+ "Federated": "cognito-identity.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "es:ESHttp*",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestfivezones/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "CognitoAccessPolicy"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibanaIdentityPoolRoleMappingBD0A239B": {
+ "Type": "AWS::Cognito::IdentityPoolRoleAttachment",
+ "Properties": {
+ "IdentityPoolId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoIdentityPoolC48068F0"
+ },
+ "Roles": {
+ "authenticated": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoAuthorizedRole88FAFCFA",
+ "Arn"
+ ]
+ }
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaCognitoKibanaConfigureRole8F40C1A1": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "es.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaCognitoKibanaConfigureRolePolicyB7090E91": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "cognito-idp:DescribeUserPool",
+ "cognito-idp:CreateUserPoolClient",
+ "cognito-idp:DeleteUserPoolClient",
+ "cognito-idp:DescribeUserPoolClient",
+ "cognito-idp:AdminInitiateAuth",
+ "cognito-idp:AdminUserGlobalSignOut",
+ "cognito-idp:ListUserPoolClients",
+ "cognito-identity:DescribeIdentityPool",
+ "cognito-identity:UpdateIdentityPool",
+ "cognito-identity:SetIdentityPoolRoles",
+ "cognito-identity:GetIdentityPoolRoles",
+ "es:UpdateElasticsearchDomainConfig"
+ ],
+ "Effect": "Allow",
+ "Resource": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoUserPool9537802B",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:cognito-identity:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":identitypool/",
+ {
+ "Ref": "testlambdaelasticsearchkibanaCognitoIdentityPoolC48068F0"
+ }
+ ]
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestfivezones"
+ ]
+ ]
+ }
+ ]
+ },
+ {
+ "Action": "iam:PassRole",
+ "Condition": {
+ "StringLike": {
+ "iam:PassedToService": "cognito-identity.amazonaws.com"
+ }
+ },
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoKibanaConfigureRole8F40C1A1",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testlambdaelasticsearchkibanaCognitoKibanaConfigureRolePolicyB7090E91",
+ "Roles": [
+ {
+ "Ref": "testlambdaelasticsearchkibanaCognitoKibanaConfigureRole8F40C1A1"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibanaElasticsearchDomain50D5F86E": {
+ "Type": "AWS::Elasticsearch::Domain",
+ "Properties": {
+ "AccessPolicies": {
+ "Statement": [
+ {
+ "Action": "es:ESHttp*",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoAuthorizedRole88FAFCFA",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRole3AFFEAA2",
+ "Arn"
+ ]
+ }
+ ]
+ },
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/deploytestfivezones/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "CognitoOptions": {
+ "Enabled": true,
+ "IdentityPoolId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoIdentityPoolC48068F0"
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoKibanaConfigureRole8F40C1A1",
+ "Arn"
+ ]
+ },
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoUserPool9537802B"
+ }
+ },
+ "DomainName": "deploytestfivezones",
+ "EBSOptions": {
+ "EBSEnabled": true,
+ "VolumeSize": 10
+ },
+ "ElasticsearchClusterConfig": {
+ "DedicatedMasterCount": 3,
+ "DedicatedMasterEnabled": true,
+ "InstanceCount": 3,
+ "ZoneAwarenessConfig": {
+ "AvailabilityZoneCount": 3
+ },
+ "ZoneAwarenessEnabled": true
+ },
+ "ElasticsearchVersion": "6.3",
+ "EncryptionAtRestOptions": {
+ "Enabled": true
+ },
+ "NodeToNodeEncryptionOptions": {
+ "Enabled": true
+ },
+ "SnapshotOptions": {
+ "AutomatedSnapshotStartHour": 1
+ },
+ "VPCOptions": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaReplaceDefaultSecurityGroupsecuritygroup9C517245",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ "Ref": "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ "Ref": "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ]
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W28",
+ "reason": "The ES Domain is passed dynamically as as parameter and explicitly specified to ensure that IAM policies are configured to lockdown access to this specific ES instance only"
+ },
+ {
+ "id": "W90",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaStatusRedAlarmCFCDB629": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "At least one primary shard and its replicas are not allocated to a node. ",
+ "MetricName": "ClusterStatus.red",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibanaStatusYellowAlarm24B9D1CB": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "At least one replica shard is not allocated to a node.",
+ "MetricName": "ClusterStatus.yellow",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibanaFreeStorageSpaceTooLowAlarm0B4D4E35": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "LessThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "A node in your cluster is down to 20 GiB of free storage space.",
+ "MetricName": "FreeStorageSpace",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Minimum",
+ "Threshold": 20000
+ }
+ },
+ "testlambdaelasticsearchkibanaIndexWritesBlockedTooHighAlarmB8C0E99C": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Your cluster is blocking write requests.",
+ "MetricName": "ClusterIndexWritesBlocked",
+ "Namespace": "AWS/ES",
+ "Period": 300,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibanaAutomatedSnapshotFailureTooHighAlarm75F2676B": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "An automated snapshot failed. This failure is often the result of a red cluster health status.",
+ "MetricName": "AutomatedSnapshotFailure",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibanaCPUUtilizationTooHighAlarmF16BA5D9": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 3,
+ "AlarmDescription": "100% CPU utilization is not uncommon, but sustained high usage is problematic. Consider using larger instance types or adding instances.",
+ "MetricName": "CPUUtilization",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 80
+ }
+ },
+ "testlambdaelasticsearchkibanaJVMMemoryPressureTooHighAlarm18224533": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Average JVM memory pressure over last 15 minutes too high. Consider scaling vertically.",
+ "MetricName": "JVMMemoryPressure",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 80
+ }
+ },
+ "testlambdaelasticsearchkibanaMasterCPUUtilizationTooHighAlarmE5E5999F": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 3,
+ "AlarmDescription": "Average CPU utilization over last 45 minutes too high. Consider using larger instance types for your dedicated master nodes.",
+ "MetricName": "MasterCPUUtilization",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 50
+ }
+ },
+ "testlambdaelasticsearchkibanaMasterJVMMemoryPressureTooHighAlarm297FF1BE": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Average JVM memory pressure over last 15 minutes too high. Consider scaling vertically.",
+ "MetricName": "MasterJVMMemoryPressure",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 50
+ }
+ },
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "10.0.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployToFiveZones/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": "deployToFiveZones/Vpc/isolatedSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1RouteTableE442650B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployToFiveZones/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": "deployToFiveZones/Vpc/isolatedSubnet2"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet2RouteTable334F9764": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployToFiveZones/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": "deployToFiveZones/Vpc/isolatedSubnet3"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet3RouteTableA2F6BBC0": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployToFiveZones/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": "deployToFiveZones/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": "deployToFiveZones/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",
+ "TrafficType": "ALL",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "deployToFiveZones/Vpc"
+ }
+ ]
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [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-elasticsearch-kibana/test/integ.deployToFiveZones.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployToFiveZones.ts
new file mode 100644
index 000000000..8ee0e27f2
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.deployToFiveZones.ts
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2022 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.
+ */
+
+/// !cdk-integ *
+import { App, Stack } from "@aws-cdk/core";
+import { LambdaToElasticSearchAndKibana } from "../lib";
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+
+// Setup
+const app = new App();
+const stack = new Stack(app, defaults.generateIntegStackName(__filename), {});
+
+const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler',
+};
+
+new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', {
+ lambdaFunctionProps: lambdaProps,
+ domainName: "deploytestfivezones",
+ deployVpc: true,
+ vpcProps: {
+ maxAzs: 5
+ }
+});
+
+// Synth
+app.synth();
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.disabledZoneAwareness.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.disabledZoneAwareness.expected.json
new file mode 100644
index 000000000..daf83505e
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.disabledZoneAwareness.expected.json
@@ -0,0 +1,920 @@
+{
+ "Resources": {
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRole3AFFEAA2": {
+ "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"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRoleDefaultPolicy199413EB": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ "ec2:AssignPrivateIpAddresses",
+ "ec2:UnassignPrivateIpAddresses"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ },
+ {
+ "Action": [
+ "xray:PutTraceSegments",
+ "xray:PutTelemetryRecords"
+ ],
+ "Effect": "Allow",
+ "Resource": "*"
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testlambdaelasticsearchkibanaLambdaFunctionServiceRoleDefaultPolicy199413EB",
+ "Roles": [
+ {
+ "Ref": "testlambdaelasticsearchkibanaLambdaFunctionServiceRole3AFFEAA2"
+ }
+ ]
+ },
+ "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."
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaReplaceDefaultSecurityGroupsecuritygroup9C517245": {
+ "Type": "AWS::EC2::SecurityGroup",
+ "Properties": {
+ "GroupDescription": "disabledZoneAwareness/test-lambda-elasticsearch-kibana/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"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaLambdaFunction601D26D3": {
+ "Type": "AWS::Lambda::Function",
+ "Properties": {
+ "Code": {
+ "S3Bucket": {
+ "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
+ },
+ "S3Key": "67a9971e29baab2bde3043bb70ce5b53318b95429a1ce9b189cf65223e8682db.zip"
+ },
+ "Role": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRole3AFFEAA2",
+ "Arn"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "DOMAIN_ENDPOINT": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaElasticsearchDomain50D5F86E",
+ "DomainEndpoint"
+ ]
+ }
+ }
+ },
+ "Handler": "index.handler",
+ "Runtime": "nodejs12.x",
+ "TracingConfig": {
+ "Mode": "Active"
+ },
+ "VpcConfig": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaReplaceDefaultSecurityGroupsecuritygroup9C517245",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ }
+ ]
+ }
+ },
+ "DependsOn": [
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRoleDefaultPolicy199413EB",
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRole3AFFEAA2"
+ ],
+ "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"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaCognitoUserPool9537802B": {
+ "Type": "AWS::Cognito::UserPool",
+ "Properties": {
+ "AccountRecoverySetting": {
+ "RecoveryMechanisms": [
+ {
+ "Name": "verified_phone_number",
+ "Priority": 1
+ },
+ {
+ "Name": "verified_email",
+ "Priority": 2
+ }
+ ]
+ },
+ "AdminCreateUserConfig": {
+ "AllowAdminCreateUserOnly": true
+ },
+ "EmailVerificationMessage": "The verification code to your new account is {####}",
+ "EmailVerificationSubject": "Verify your new account",
+ "SmsVerificationMessage": "The verification code to your new account is {####}",
+ "UserPoolAddOns": {
+ "AdvancedSecurityMode": "ENFORCED"
+ },
+ "VerificationMessageTemplate": {
+ "DefaultEmailOption": "CONFIRM_WITH_CODE",
+ "EmailMessage": "The verification code to your new account is {####}",
+ "EmailSubject": "Verify your new account",
+ "SmsMessage": "The verification code to your new account is {####}"
+ }
+ },
+ "UpdateReplacePolicy": "Retain",
+ "DeletionPolicy": "Retain"
+ },
+ "testlambdaelasticsearchkibanaCognitoUserPoolClient8F70A2AA": {
+ "Type": "AWS::Cognito::UserPoolClient",
+ "Properties": {
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoUserPool9537802B"
+ },
+ "AllowedOAuthFlows": [
+ "implicit",
+ "code"
+ ],
+ "AllowedOAuthFlowsUserPoolClient": true,
+ "AllowedOAuthScopes": [
+ "profile",
+ "phone",
+ "email",
+ "openid",
+ "aws.cognito.signin.user.admin"
+ ],
+ "CallbackURLs": [
+ "https://example.com"
+ ],
+ "SupportedIdentityProviders": [
+ "COGNITO"
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibanaCognitoIdentityPoolC48068F0": {
+ "Type": "AWS::Cognito::IdentityPool",
+ "Properties": {
+ "AllowUnauthenticatedIdentities": false,
+ "CognitoIdentityProviders": [
+ {
+ "ClientId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoUserPoolClient8F70A2AA"
+ },
+ "ProviderName": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoUserPool9537802B",
+ "ProviderName"
+ ]
+ },
+ "ServerSideTokenCheck": true
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibanaUserPoolDomainB9BDF063": {
+ "Type": "AWS::Cognito::UserPoolDomain",
+ "Properties": {
+ "Domain": "disabledzoneawareness",
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoUserPool9537802B"
+ }
+ },
+ "DependsOn": [
+ "testlambdaelasticsearchkibanaCognitoUserPool9537802B"
+ ]
+ },
+ "testlambdaelasticsearchkibanaCognitoAuthorizedRole88FAFCFA": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRoleWithWebIdentity",
+ "Condition": {
+ "StringEquals": {
+ "cognito-identity.amazonaws.com:aud": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoIdentityPoolC48068F0"
+ }
+ },
+ "ForAnyValue:StringLike": {
+ "cognito-identity.amazonaws.com:amr": "authenticated"
+ }
+ },
+ "Effect": "Allow",
+ "Principal": {
+ "Federated": "cognito-identity.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "Policies": [
+ {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": "es:ESHttp*",
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:",
+ {
+ "Ref": "AWS::Partition"
+ },
+ ":es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/disabledzoneawareness/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "CognitoAccessPolicy"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibanaIdentityPoolRoleMappingBD0A239B": {
+ "Type": "AWS::Cognito::IdentityPoolRoleAttachment",
+ "Properties": {
+ "IdentityPoolId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoIdentityPoolC48068F0"
+ },
+ "Roles": {
+ "authenticated": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoAuthorizedRole88FAFCFA",
+ "Arn"
+ ]
+ }
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaCognitoKibanaConfigureRole8F40C1A1": {
+ "Type": "AWS::IAM::Role",
+ "Properties": {
+ "AssumeRolePolicyDocument": {
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "es.amazonaws.com"
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaCognitoKibanaConfigureRolePolicyB7090E91": {
+ "Type": "AWS::IAM::Policy",
+ "Properties": {
+ "PolicyDocument": {
+ "Statement": [
+ {
+ "Action": [
+ "cognito-idp:DescribeUserPool",
+ "cognito-idp:CreateUserPoolClient",
+ "cognito-idp:DeleteUserPoolClient",
+ "cognito-idp:DescribeUserPoolClient",
+ "cognito-idp:AdminInitiateAuth",
+ "cognito-idp:AdminUserGlobalSignOut",
+ "cognito-idp:ListUserPoolClients",
+ "cognito-identity:DescribeIdentityPool",
+ "cognito-identity:UpdateIdentityPool",
+ "cognito-identity:SetIdentityPoolRoles",
+ "cognito-identity:GetIdentityPoolRoles",
+ "es:UpdateElasticsearchDomainConfig"
+ ],
+ "Effect": "Allow",
+ "Resource": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoUserPool9537802B",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:cognito-identity:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":identitypool/",
+ {
+ "Ref": "testlambdaelasticsearchkibanaCognitoIdentityPoolC48068F0"
+ }
+ ]
+ ]
+ },
+ {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/disabledzoneawareness"
+ ]
+ ]
+ }
+ ]
+ },
+ {
+ "Action": "iam:PassRole",
+ "Condition": {
+ "StringLike": {
+ "iam:PassedToService": "cognito-identity.amazonaws.com"
+ }
+ },
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoKibanaConfigureRole8F40C1A1",
+ "Arn"
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "PolicyName": "testlambdaelasticsearchkibanaCognitoKibanaConfigureRolePolicyB7090E91",
+ "Roles": [
+ {
+ "Ref": "testlambdaelasticsearchkibanaCognitoKibanaConfigureRole8F40C1A1"
+ }
+ ]
+ }
+ },
+ "testlambdaelasticsearchkibanaElasticsearchDomain50D5F86E": {
+ "Type": "AWS::Elasticsearch::Domain",
+ "Properties": {
+ "AccessPolicies": {
+ "Statement": [
+ {
+ "Action": "es:ESHttp*",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoAuthorizedRole88FAFCFA",
+ "Arn"
+ ]
+ },
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaLambdaFunctionServiceRole3AFFEAA2",
+ "Arn"
+ ]
+ }
+ ]
+ },
+ "Resource": {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:es:",
+ {
+ "Ref": "AWS::Region"
+ },
+ ":",
+ {
+ "Ref": "AWS::AccountId"
+ },
+ ":domain/disabledzoneawareness/*"
+ ]
+ ]
+ }
+ }
+ ],
+ "Version": "2012-10-17"
+ },
+ "CognitoOptions": {
+ "Enabled": true,
+ "IdentityPoolId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoIdentityPoolC48068F0"
+ },
+ "RoleArn": {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaCognitoKibanaConfigureRole8F40C1A1",
+ "Arn"
+ ]
+ },
+ "UserPoolId": {
+ "Ref": "testlambdaelasticsearchkibanaCognitoUserPool9537802B"
+ }
+ },
+ "DomainName": "disabledzoneawareness",
+ "EBSOptions": {
+ "EBSEnabled": true,
+ "VolumeSize": 10
+ },
+ "ElasticsearchClusterConfig": {
+ "DedicatedMasterCount": 3,
+ "DedicatedMasterEnabled": true,
+ "InstanceCount": 3,
+ "ZoneAwarenessEnabled": false
+ },
+ "ElasticsearchVersion": "6.3",
+ "EncryptionAtRestOptions": {
+ "Enabled": true
+ },
+ "NodeToNodeEncryptionOptions": {
+ "Enabled": true
+ },
+ "SnapshotOptions": {
+ "AutomatedSnapshotStartHour": 1
+ },
+ "VPCOptions": {
+ "SecurityGroupIds": [
+ {
+ "Fn::GetAtt": [
+ "testlambdaelasticsearchkibanaReplaceDefaultSecurityGroupsecuritygroup9C517245",
+ "GroupId"
+ ]
+ }
+ ],
+ "SubnetIds": [
+ {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ }
+ ]
+ }
+ },
+ "Metadata": {
+ "cfn_nag": {
+ "rules_to_suppress": [
+ {
+ "id": "W28",
+ "reason": "The ES Domain is passed dynamically as as parameter and explicitly specified to ensure that IAM policies are configured to lockdown access to this specific ES instance only"
+ },
+ {
+ "id": "W90",
+ "reason": "This is not a rule for the general case, just for specific use cases/industries"
+ }
+ ]
+ }
+ }
+ },
+ "testlambdaelasticsearchkibanaStatusRedAlarmCFCDB629": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "At least one primary shard and its replicas are not allocated to a node. ",
+ "MetricName": "ClusterStatus.red",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibanaStatusYellowAlarm24B9D1CB": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "At least one replica shard is not allocated to a node.",
+ "MetricName": "ClusterStatus.yellow",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibanaFreeStorageSpaceTooLowAlarm0B4D4E35": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "LessThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "A node in your cluster is down to 20 GiB of free storage space.",
+ "MetricName": "FreeStorageSpace",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Minimum",
+ "Threshold": 20000
+ }
+ },
+ "testlambdaelasticsearchkibanaIndexWritesBlockedTooHighAlarmB8C0E99C": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Your cluster is blocking write requests.",
+ "MetricName": "ClusterIndexWritesBlocked",
+ "Namespace": "AWS/ES",
+ "Period": 300,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibanaAutomatedSnapshotFailureTooHighAlarm75F2676B": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "An automated snapshot failed. This failure is often the result of a red cluster health status.",
+ "MetricName": "AutomatedSnapshotFailure",
+ "Namespace": "AWS/ES",
+ "Period": 60,
+ "Statistic": "Maximum",
+ "Threshold": 1
+ }
+ },
+ "testlambdaelasticsearchkibanaCPUUtilizationTooHighAlarmF16BA5D9": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 3,
+ "AlarmDescription": "100% CPU utilization is not uncommon, but sustained high usage is problematic. Consider using larger instance types or adding instances.",
+ "MetricName": "CPUUtilization",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 80
+ }
+ },
+ "testlambdaelasticsearchkibanaJVMMemoryPressureTooHighAlarm18224533": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Average JVM memory pressure over last 15 minutes too high. Consider scaling vertically.",
+ "MetricName": "JVMMemoryPressure",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 80
+ }
+ },
+ "testlambdaelasticsearchkibanaMasterCPUUtilizationTooHighAlarmE5E5999F": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 3,
+ "AlarmDescription": "Average CPU utilization over last 45 minutes too high. Consider using larger instance types for your dedicated master nodes.",
+ "MetricName": "MasterCPUUtilization",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 50
+ }
+ },
+ "testlambdaelasticsearchkibanaMasterJVMMemoryPressureTooHighAlarm297FF1BE": {
+ "Type": "AWS::CloudWatch::Alarm",
+ "Properties": {
+ "ComparisonOperator": "GreaterThanOrEqualToThreshold",
+ "EvaluationPeriods": 1,
+ "AlarmDescription": "Average JVM memory pressure over last 15 minutes too high. Consider scaling vertically.",
+ "MetricName": "MasterJVMMemoryPressure",
+ "Namespace": "AWS/ES",
+ "Period": 900,
+ "Statistic": "Average",
+ "Threshold": 50
+ }
+ },
+ "Vpc8378EB38": {
+ "Type": "AWS::EC2::VPC",
+ "Properties": {
+ "CidrBlock": "10.0.0.0/16",
+ "EnableDnsHostnames": true,
+ "EnableDnsSupport": true,
+ "InstanceTenancy": "default",
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "disabledZoneAwareness/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": "disabledZoneAwareness/Vpc/isolatedSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1RouteTableE442650B": {
+ "Type": "AWS::EC2::RouteTable",
+ "Properties": {
+ "VpcId": {
+ "Ref": "Vpc8378EB38"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "disabledZoneAwareness/Vpc/isolatedSubnet1"
+ }
+ ]
+ }
+ },
+ "VpcisolatedSubnet1RouteTableAssociationD259E31A": {
+ "Type": "AWS::EC2::SubnetRouteTableAssociation",
+ "Properties": {
+ "RouteTableId": {
+ "Ref": "VpcisolatedSubnet1RouteTableE442650B"
+ },
+ "SubnetId": {
+ "Ref": "VpcisolatedSubnet1SubnetE62B1B9B"
+ }
+ }
+ },
+ "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": "disabledZoneAwareness/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": "disabledZoneAwareness/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",
+ "TrafficType": "ALL",
+ "DeliverLogsPermissionArn": {
+ "Fn::GetAtt": [
+ "VpcFlowLogIAMRole6A475D41",
+ "Arn"
+ ]
+ },
+ "LogDestinationType": "cloud-watch-logs",
+ "LogGroupName": {
+ "Ref": "VpcFlowLogLogGroup7B5C56B9"
+ },
+ "Tags": [
+ {
+ "Key": "Name",
+ "Value": "disabledZoneAwareness/Vpc"
+ }
+ ]
+ }
+ }
+ },
+ "Parameters": {
+ "BootstrapVersion": {
+ "Type": "AWS::SSM::Parameter::Value",
+ "Default": "/cdk-bootstrap/hnb659fds/version",
+ "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [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-elasticsearch-kibana/test/integ.disabledZoneAwareness.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.disabledZoneAwareness.ts
new file mode 100644
index 000000000..c041f599f
--- /dev/null
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.disabledZoneAwareness.ts
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2022 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.
+ */
+
+/// !cdk-integ *
+import { App, Stack } from "@aws-cdk/core";
+import { LambdaToElasticSearchAndKibana } from "../lib";
+import * as lambda from '@aws-cdk/aws-lambda';
+import * as defaults from '@aws-solutions-constructs/core';
+
+// Setup
+const app = new App();
+const stack = new Stack(app, defaults.generateIntegStackName(__filename), {});
+
+const lambdaProps: lambda.FunctionProps = {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_12_X,
+ handler: 'index.handler',
+};
+
+const esDomainProps = {
+ elasticsearchClusterConfig: {
+ dedicatedMasterCount: 3,
+ dedicatedMasterEnabled: true,
+ instanceCount: 3,
+ zoneAwarenessEnabled: false,
+ }
+};
+
+new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', {
+ lambdaFunctionProps: lambdaProps,
+ domainName: "disabledzoneawareness",
+ esDomainProps,
+ deployVpc: true,
+ vpcProps: {
+ maxAzs: 1
+ }
+});
+
+// Synth
+app.synth();
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/lambda-elasticsearch-kibana.test.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/lambda-elasticsearch-kibana.test.ts
index 20a374ba9..f19256510 100644
--- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/lambda-elasticsearch-kibana.test.ts
+++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/lambda-elasticsearch-kibana.test.ts
@@ -13,16 +13,14 @@
import { LambdaToElasticSearchAndKibana, LambdaToElasticSearchAndKibanaProps } from "../lib";
import * as lambda from '@aws-cdk/aws-lambda';
+import * as ec2 from '@aws-cdk/aws-ec2';
import * as cdk from "@aws-cdk/core";
import '@aws-cdk/assert/jest';
+import * as defaults from '@aws-solutions-constructs/core';
function deployNewFunc(stack: cdk.Stack) {
const props: LambdaToElasticSearchAndKibanaProps = {
- lambdaFunctionProps: {
- code: lambda.Code.fromAsset(`${__dirname}/lambda`),
- runtime: lambda.Runtime.NODEJS_14_X,
- handler: 'index.handler'
- },
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
domainName: 'test-domain'
};
@@ -78,11 +76,7 @@ test('check properties with no CW Alarms ', () => {
const stack = new cdk.Stack();
const props: LambdaToElasticSearchAndKibanaProps = {
- lambdaFunctionProps: {
- code: lambda.Code.fromAsset(`${__dirname}/lambda`),
- runtime: lambda.Runtime.NODEJS_14_X,
- handler: 'index.handler'
- },
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
domainName: 'test-domain',
createCloudWatchAlarms: false
};
@@ -98,14 +92,10 @@ test('check properties with no CW Alarms ', () => {
expect(construct.elasticsearchRole).toBeDefined();
});
-test('check lambda function ustom environment variable', () => {
+test('check lambda function custom environment variable', () => {
const stack = new cdk.Stack();
const props: LambdaToElasticSearchAndKibanaProps = {
- lambdaFunctionProps: {
- code: lambda.Code.fromAsset(`${__dirname}/lambda`),
- runtime: lambda.Runtime.NODEJS_14_X,
- handler: 'index.handler'
- },
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
domainName: 'test-domain',
domainEndpointEnvironmentVariableName: 'CUSTOM_DOMAIN_ENDPOINT'
};
@@ -132,11 +122,7 @@ test('check lambda function ustom environment variable', () => {
test('check override cognito domain name with provided cognito domain name', () => {
const stack = new cdk.Stack();
const props: LambdaToElasticSearchAndKibanaProps = {
- lambdaFunctionProps: {
- code: lambda.Code.fromAsset(`${__dirname}/lambda`),
- runtime: lambda.Runtime.NODEJS_14_X,
- handler: 'index.handler'
- },
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
domainName: 'test-domain',
cognitoDomainName: 'test-cognito-domain'
};
@@ -146,4 +132,410 @@ test('check override cognito domain name with provided cognito domain name', ()
expect(stack).toHaveResource('AWS::Cognito::UserPoolDomain', {
Domain: 'test-cognito-domain'
});
-});
\ No newline at end of file
+});
+
+test("Test minimal deployment that deploys a VPC in 2 AZ without vpcProps", () => {
+ const stack = new cdk.Stack(undefined, undefined, {});
+
+ new LambdaToElasticSearchAndKibana(stack, "lambda-elasticsearch-kibana-stack", {
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
+ domainName: 'test-domain',
+ deployVpc: true,
+ });
+
+ expect(stack).toHaveResource("AWS::Lambda::Function", {
+ VpcConfig: {
+ SecurityGroupIds: [
+ {
+ "Fn::GetAtt": [
+ "lambdaelasticsearchkibanastackReplaceDefaultSecurityGroupsecuritygroup4C50002B",
+ "GroupId",
+ ],
+ },
+ ],
+ SubnetIds: [
+ {
+ Ref: "VpcisolatedSubnet1SubnetE62B1B9B",
+ },
+ {
+ Ref: "VpcisolatedSubnet2Subnet39217055",
+ }
+ ],
+ },
+ });
+
+ expect(stack).toHaveResourceLike("AWS::Elasticsearch::Domain", {
+ VPCOptions: {
+ SubnetIds: [
+ {
+ Ref: "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ Ref: "VpcisolatedSubnet2Subnet39217055"
+ }
+ ]
+ }
+ });
+
+ expect(stack).toHaveResource("AWS::EC2::VPC", {
+ EnableDnsHostnames: true,
+ EnableDnsSupport: true,
+ });
+});
+
+test("Test minimal deployment that deploys a VPC in 3 AZ without vpcProps", () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ new LambdaToElasticSearchAndKibana(stack, "lambda-elasticsearch-kibana-stack", {
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
+ domainName: 'test-domain',
+ deployVpc: true,
+ });
+
+ expect(stack).toHaveResource("AWS::Lambda::Function", {
+ VpcConfig: {
+ SecurityGroupIds: [
+ {
+ "Fn::GetAtt": [
+ "lambdaelasticsearchkibanastackReplaceDefaultSecurityGroupsecuritygroup4C50002B",
+ "GroupId",
+ ],
+ },
+ ],
+ SubnetIds: [
+ {
+ Ref: "VpcisolatedSubnet1SubnetE62B1B9B",
+ },
+ {
+ Ref: "VpcisolatedSubnet2Subnet39217055",
+ },
+ {
+ Ref: "VpcisolatedSubnet3Subnet44F2537D",
+ },
+ ],
+ },
+ });
+
+ expect(stack).toHaveResourceLike("AWS::Elasticsearch::Domain", {
+ VPCOptions: {
+ SubnetIds: [
+ {
+ Ref: "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ Ref: "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ Ref: "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ]
+ }
+ });
+
+ expect(stack).toHaveResource("AWS::EC2::VPC", {
+ EnableDnsHostnames: true,
+ EnableDnsSupport: true,
+ });
+});
+
+test("Test ES cluster deploy to 1 AZ when user set zoneAwarenessEnabled to false", () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const esDomainProps = {
+ elasticsearchClusterConfig: {
+ dedicatedMasterCount: 3,
+ dedicatedMasterEnabled: true,
+ zoneAwarenessEnabled: false,
+ instanceCount: 3
+ }
+ };
+
+ new LambdaToElasticSearchAndKibana(stack, "lambda-elasticsearch-kibana-stack", {
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
+ domainName: 'test-domain',
+ esDomainProps,
+ deployVpc: true,
+ vpcProps: {
+ maxAzs: 1
+ }
+ });
+
+ expect(stack).toHaveResource("AWS::Elasticsearch::Domain", {
+ ElasticsearchClusterConfig: {
+ DedicatedMasterCount: 3,
+ DedicatedMasterEnabled: true,
+ InstanceCount: 3,
+ ZoneAwarenessEnabled: false,
+ }
+ });
+
+ expect(stack).toHaveResourceLike("AWS::Elasticsearch::Domain", {
+ VPCOptions: {
+ SubnetIds: [
+ {
+ Ref: "VpcisolatedSubnet1SubnetE62B1B9B"
+ }
+ ]
+ }
+ });
+});
+
+test("Test ES cluster deploy to 2 AZ when user set availabilityZoneCount to 2", () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const esDomainProps = {
+ elasticsearchClusterConfig: {
+ dedicatedMasterCount: 3,
+ dedicatedMasterEnabled: true,
+ instanceCount: 2,
+ zoneAwarenessEnabled: true,
+ zoneAwarenessConfig: {
+ availabilityZoneCount: 2
+ }
+ }
+ };
+
+ new LambdaToElasticSearchAndKibana(stack, "lambda-elasticsearch-kibana-stack", {
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
+ domainName: 'test-domain',
+ esDomainProps,
+ deployVpc: true,
+ vpcProps: {
+ maxAzs: 2
+ }
+ });
+
+ expect(stack).toHaveResource("AWS::Elasticsearch::Domain", {
+ ElasticsearchClusterConfig: {
+ DedicatedMasterCount: 3,
+ DedicatedMasterEnabled: true,
+ InstanceCount: 2,
+ ZoneAwarenessConfig: {
+ AvailabilityZoneCount: 2,
+ },
+ ZoneAwarenessEnabled: true,
+ }
+ });
+
+ expect(stack).toHaveResourceLike("AWS::Elasticsearch::Domain", {
+ VPCOptions: {
+ SubnetIds: [
+ {
+ Ref: "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ Ref: "VpcisolatedSubnet2Subnet39217055"
+ }
+ ]
+ }
+ });
+});
+
+test('Test minimal deployment with an existing isolated VPC', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const vpc = defaults.getTestVpc(stack, false, {
+ vpcName: "existing-isolated-vpc-test"
+ });
+
+ const construct = new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', {
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
+ domainName: "test",
+ existingVpc: vpc
+ });
+
+ expect(stack).toHaveResourceLike("AWS::EC2::VPC", {
+ Tags: [
+ {
+ Key: "Name",
+ Value: "existing-isolated-vpc-test"
+ }
+ ]
+ });
+
+ expect(stack).toHaveResourceLike("AWS::Elasticsearch::Domain", {
+ VPCOptions: {
+ SubnetIds: [
+ {
+ Ref: "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ Ref: "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ Ref: "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ]
+ }
+ });
+
+ expect(stack).toCountResources("AWS::EC2::VPC", 1);
+ expect(construct.vpc).toBeDefined();
+});
+
+test('Test minimal deployment with an existing private VPC', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const vpc = new ec2.Vpc(stack, 'existing-private-vpc-test', {
+ natGateways: 1,
+ subnetConfiguration: [
+ {
+ cidrMask: 24,
+ name: 'application',
+ subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
+ },
+ {
+ cidrMask: 24,
+ name: "public",
+ subnetType: ec2.SubnetType.PUBLIC,
+ }
+ ]
+ });
+
+ const construct = new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', {
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
+ domainName: "test",
+ existingVpc: vpc
+ });
+
+ expect(stack).toHaveResourceLike("AWS::EC2::VPC", {
+ Tags: [
+ {
+ Key: "Name",
+ Value: "Default/existing-private-vpc-test"
+ }
+ ]
+ });
+
+ expect(stack).toHaveResourceLike("AWS::Elasticsearch::Domain", {
+ VPCOptions: {
+ SubnetIds: [
+ {
+ Ref: "existingprivatevpctestapplicationSubnet1Subnet1F7744F0"
+ },
+ {
+ Ref: "existingprivatevpctestapplicationSubnet2SubnetF7B713AD"
+ },
+ {
+ Ref: "existingprivatevpctestapplicationSubnet3SubnetA519E038"
+ }
+ ]
+ }
+ });
+
+ expect(stack).toCountResources("AWS::EC2::VPC", 1);
+ expect(construct.vpc).toBeDefined();
+});
+
+test('Test minimal deployment with VPC construct props', () => {
+ const stack = new cdk.Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const construct = new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', {
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
+ domainName: "test",
+ deployVpc: true,
+ vpcProps: {
+ vpcName: "vpc-props-test"
+ }
+ });
+
+ expect(stack).toHaveResourceLike("AWS::EC2::VPC", {
+ Tags: [
+ {
+ Key: "Name",
+ Value: "vpc-props-test"
+ }
+ ]
+ });
+
+ expect(stack).toHaveResourceLike("AWS::Elasticsearch::Domain", {
+ VPCOptions: {
+ SubnetIds: [
+ {
+ Ref: "VpcisolatedSubnet1SubnetE62B1B9B"
+ },
+ {
+ Ref: "VpcisolatedSubnet2Subnet39217055"
+ },
+ {
+ Ref: "VpcisolatedSubnet3Subnet44F2537D"
+ }
+ ]
+ }
+ });
+
+ expect(stack).toCountResources("AWS::EC2::VPC", 1);
+ expect(construct.vpc).toBeDefined();
+});
+
+test('Test error for vpcProps and undefined deployVpc prop', () => {
+ const stack = new cdk.Stack();
+
+ const app = () => {
+ new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', {
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
+ domainName: "test",
+ vpcProps: {
+ vpcName: "existing-vpc-test"
+ }
+ });
+ };
+
+ expect(app).toThrowError("Error - deployVpc must be true when defining vpcProps");
+});
+
+test('Test error for Lambda function VPC props', () => {
+ const stack = new cdk.Stack();
+
+ const vpc = defaults.getTestVpc(stack);
+
+ const app = () => {
+ new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', {
+ lambdaFunctionProps: defaults.consolidateProps(getDefaultTestLambdaProps(), { vpc }),
+ domainName: "test",
+ deployVpc: true,
+ });
+ };
+
+ expect(app).toThrowError("Error - Define VPC using construct parameters not Lambda function props");
+});
+
+test('Test error for Elasticsearch domain VPC props', () => {
+ const stack = new cdk.Stack();
+
+ const app = () => {
+ new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', {
+ lambdaFunctionProps: getDefaultTestLambdaProps(),
+ esDomainProps: {
+ vpcOptions: {
+ subnetIds: ['fake-ids'],
+ securityGroupIds: ['fake-sgs']
+ }
+ },
+ domainName: "test",
+ deployVpc: true,
+ });
+ };
+
+ expect(app).toThrowError("Error - Define VPC using construct parameters not Elasticsearch props");
+});
+
+function getDefaultTestLambdaProps(): lambda.FunctionProps {
+ return {
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ runtime: lambda.Runtime.NODEJS_14_X,
+ handler: 'index.handler',
+ };
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/aws-route53-alb/README.md b/source/patterns/@aws-solutions-constructs/aws-route53-alb/README.md
index 99f4ae37d..b1c53c50c 100755
--- a/source/patterns/@aws-solutions-constructs/aws-route53-alb/README.md
+++ b/source/patterns/@aws-solutions-constructs/aws-route53-alb/README.md
@@ -128,7 +128,7 @@ Out of the box implementation of the Construct without any override will set the
* Adds an ALIAS record to the new or provided Hosted Zone that routes to the construct's ALB
### Application Load Balancer
-* Creates an Application Load Balancer with no Listener or target. The consruct can incorporate an existing, fully configured ALB if provided.
+* Creates an Application Load Balancer with no Listener or target. The construct can incorporate an existing, fully configured ALB if provided.
## Architecture
![Architecture Diagram](architecture.png)
diff --git a/source/patterns/@aws-solutions-constructs/core/lib/elasticsearch-defaults.ts b/source/patterns/@aws-solutions-constructs/core/lib/elasticsearch-defaults.ts
index 7764f471f..dfbe3167f 100644
--- a/source/patterns/@aws-solutions-constructs/core/lib/elasticsearch-defaults.ts
+++ b/source/patterns/@aws-solutions-constructs/core/lib/elasticsearch-defaults.ts
@@ -12,24 +12,18 @@
*/
import * as elasticsearch from '@aws-cdk/aws-elasticsearch';
-import * as cognito from '@aws-cdk/aws-cognito';
import * as iam from '@aws-cdk/aws-iam';
import * as cdk from '@aws-cdk/core';
+import { BuildElasticSearchProps } from './elasticsearch-helper';
-export interface CfnDomainOptions {
- readonly identitypool: cognito.CfnIdentityPool,
- readonly userpool: cognito.UserPool,
- readonly cognitoAuthorizedRoleARN: string,
- readonly serviceRoleARN?: string
-}
-
-export function DefaultCfnDomainProps(domainName: string, cognitoKibanaConfigureRole: iam.Role, options: CfnDomainOptions) {
+export function DefaultCfnDomainProps(domainName: string, cognitoKibanaConfigureRole: iam.Role, props: BuildElasticSearchProps):
+ elasticsearch.CfnDomainProps {
const roleARNs: iam.IPrincipal[] = [];
- roleARNs.push(new iam.ArnPrincipal(options.cognitoAuthorizedRoleARN));
+ roleARNs.push(new iam.ArnPrincipal(props.cognitoAuthorizedRoleARN));
- if (options.serviceRoleARN) {
- roleARNs.push(new iam.ArnPrincipal(options.serviceRoleARN));
+ if (props.serviceRoleARN) {
+ roleARNs.push(new iam.ArnPrincipal(props.serviceRoleARN));
}
return {
@@ -41,15 +35,6 @@ export function DefaultCfnDomainProps(domainName: string, cognitoKibanaConfigure
nodeToNodeEncryptionOptions: {
enabled: true
},
- elasticsearchClusterConfig: {
- dedicatedMasterEnabled: true,
- dedicatedMasterCount: 3,
- instanceCount: 3,
- zoneAwarenessEnabled: true,
- zoneAwarenessConfig: {
- availabilityZoneCount: 3
- }
- },
snapshotOptions: {
automatedSnapshotStartHour: 1
},
@@ -59,8 +44,8 @@ export function DefaultCfnDomainProps(domainName: string, cognitoKibanaConfigure
},
cognitoOptions: {
enabled: true,
- identityPoolId: options.identitypool.ref,
- userPoolId: options.userpool.userPoolId,
+ identityPoolId: props.identitypool.ref,
+ userPoolId: props.userpool.userPoolId,
roleArn: cognitoKibanaConfigureRole.roleArn
},
accessPolicies: new iam.PolicyDocument({
diff --git a/source/patterns/@aws-solutions-constructs/core/lib/elasticsearch-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/elasticsearch-helper.ts
index 0ab62d3d9..503e9976b 100644
--- a/source/patterns/@aws-solutions-constructs/core/lib/elasticsearch-helper.ts
+++ b/source/patterns/@aws-solutions-constructs/core/lib/elasticsearch-helper.ts
@@ -12,65 +12,65 @@
*/
import * as elasticsearch from '@aws-cdk/aws-elasticsearch';
-import { CfnDomainOptions, DefaultCfnDomainProps } from './elasticsearch-defaults';
+import { DefaultCfnDomainProps } from './elasticsearch-defaults';
import { consolidateProps, addCfnSuppressRules } from './utils';
import * as iam from '@aws-cdk/aws-iam';
import * as cdk from '@aws-cdk/core';
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
+import * as cognito from '@aws-cdk/aws-cognito';
+import * as ec2 from '@aws-cdk/aws-ec2';
// Note: To ensure CDKv2 compatibility, keep the import statement for Construct separate
import { Construct } from '@aws-cdk/core';
-export function buildElasticSearch(scope: Construct, domainName: string,
- options: CfnDomainOptions, cfnDomainProps?: elasticsearch.CfnDomainProps): [elasticsearch.CfnDomain, iam.Role] {
+const MaximumAzsInElasticsearchDomain = 3;
+
+export interface BuildElasticSearchProps {
+ readonly identitypool: cognito.CfnIdentityPool;
+ readonly userpool: cognito.UserPool;
+ readonly cognitoAuthorizedRoleARN: string;
+ readonly serviceRoleARN?: string;
+ readonly vpc?: ec2.IVpc;
+ readonly domainName: string;
+ readonly clientDomainProps?: elasticsearch.CfnDomainProps,
+ readonly securityGroupIds?: string[]
+}
+
+export function buildElasticSearch(scope: Construct, props: BuildElasticSearchProps): [elasticsearch.CfnDomain, iam.Role] {
+
+ let subnetIds: string[] = [];
+ const constructDrivenProps: any = {};
// Setup the IAM Role & policy for ES to configure Cognito User pool and Identity pool
- const cognitoKibanaConfigureRole = new iam.Role(scope, 'CognitoKibanaConfigureRole', {
- assumedBy: new iam.ServicePrincipal('es.amazonaws.com')
- });
-
- const cognitoKibanaConfigureRolePolicy = new iam.Policy(scope, 'CognitoKibanaConfigureRolePolicy', {
- statements: [
- new iam.PolicyStatement({
- actions: [
- "cognito-idp:DescribeUserPool",
- "cognito-idp:CreateUserPoolClient",
- "cognito-idp:DeleteUserPoolClient",
- "cognito-idp:DescribeUserPoolClient",
- "cognito-idp:AdminInitiateAuth",
- "cognito-idp:AdminUserGlobalSignOut",
- "cognito-idp:ListUserPoolClients",
- "cognito-identity:DescribeIdentityPool",
- "cognito-identity:UpdateIdentityPool",
- "cognito-identity:SetIdentityPoolRoles",
- "cognito-identity:GetIdentityPoolRoles",
- "es:UpdateElasticsearchDomainConfig"
- ],
- resources: [
- options.userpool.userPoolArn,
- `arn:aws:cognito-identity:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:identitypool/${options.identitypool.ref}`,
- `arn:aws:es:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:domain/${domainName}`
- ]
- }),
- new iam.PolicyStatement({
- actions: [
- "iam:PassRole"
- ],
- conditions: {
- StringLike: { 'iam:PassedToService': 'cognito-identity.amazonaws.com' }
- },
- resources: [
- cognitoKibanaConfigureRole.roleArn
- ]
- })
- ]
- });
- cognitoKibanaConfigureRolePolicy.attachToRole(cognitoKibanaConfigureRole);
+ const cognitoKibanaConfigureRole = createKibanaCognitoRole(scope, props.userpool, props.identitypool, props.domainName);
+
+ if (props.vpc) {
+ subnetIds = retrievePrivateSubnetIds(props.vpc);
+
+ if (subnetIds.length > MaximumAzsInElasticsearchDomain) {
+ subnetIds = subnetIds.slice(0, MaximumAzsInElasticsearchDomain);
+ }
- let _cfnDomainProps = DefaultCfnDomainProps(domainName, cognitoKibanaConfigureRole, options);
+ constructDrivenProps.vpcOptions = {
+ subnetIds,
+ securityGroupIds: props.securityGroupIds
+ };
- _cfnDomainProps = consolidateProps(_cfnDomainProps, cfnDomainProps);
+ // If the client did not submit a ClusterConfig, then we will create one
+ if (!props.clientDomainProps?.elasticsearchClusterConfig) {
+ constructDrivenProps.elasticsearchClusterConfig = createClusterConfiguration(subnetIds.length);
+ }
+ } else { // No VPC
+ // If the client did not submit a ClusterConfig, then we will create one based on the Region
+ if (!props.clientDomainProps?.elasticsearchClusterConfig) {
+ constructDrivenProps.elasticsearchClusterConfig = createClusterConfiguration(cdk.Stack.of(scope).availabilityZones.length);
+ }
+ }
+
+ const defaultCfnDomainProps = DefaultCfnDomainProps(props.domainName, cognitoKibanaConfigureRole, props);
+ const finalCfnDomainProps = consolidateProps(defaultCfnDomainProps, props.clientDomainProps, constructDrivenProps);
+
+ const esDomain = new elasticsearch.CfnDomain(scope, `ElasticsearchDomain`, finalCfnDomainProps);
- const esDomain = new elasticsearch.CfnDomain(scope, "ElasticsearchDomain", _cfnDomainProps);
addCfnSuppressRules(esDomain, [
{
id: "W28",
@@ -217,3 +217,92 @@ export function buildElasticSearchCWAlarms(scope: Construct): cloudwatch.Alarm[]
return alarms;
}
+
+function retrievePrivateSubnetIds(vpc: ec2.IVpc) {
+ let targetSubnetType;
+
+ if (vpc.isolatedSubnets.length) {
+ targetSubnetType = ec2.SubnetType.PRIVATE_ISOLATED;
+ } else if (vpc.privateSubnets.length) {
+ targetSubnetType = ec2.SubnetType.PRIVATE_WITH_NAT;
+ } else {
+ throw new Error('Error - ElasticSearch Domains can only be deployed in Isolated or Private subnets');
+ }
+
+ const subnetSelector = {
+ onePerAz: true,
+ subnetType: targetSubnetType
+ };
+
+ return vpc.selectSubnets(subnetSelector).subnetIds;
+}
+
+function createClusterConfiguration(numberOfAzs?: number): elasticsearch.CfnDomain.ElasticsearchClusterConfigProperty {
+ return {
+ dedicatedMasterEnabled: true,
+ dedicatedMasterCount: 3,
+ zoneAwarenessEnabled: true,
+ zoneAwarenessConfig: {
+ availabilityZoneCount: numberOfAzs
+ },
+ instanceCount: numberOfAzs,
+ };
+}
+
+function createKibanaCognitoRole(
+ scope: Construct,
+ userPool: cognito.UserPool,
+ identitypool: cognito.CfnIdentityPool,
+ domainName: string
+): iam.Role {
+ // Setup the IAM Role & policy for ES to configure Cognito User pool and Identity pool
+ const cognitoKibanaConfigureRole = new iam.Role(
+ scope,
+ "CognitoKibanaConfigureRole",
+ {
+ assumedBy: new iam.ServicePrincipal("es.amazonaws.com"),
+ }
+ );
+
+ const cognitoKibanaConfigureRolePolicy = new iam.Policy(
+ scope,
+ "CognitoKibanaConfigureRolePolicy",
+ {
+ statements: [
+ new iam.PolicyStatement({
+ actions: [
+ "cognito-idp:DescribeUserPool",
+ "cognito-idp:CreateUserPoolClient",
+ "cognito-idp:DeleteUserPoolClient",
+ "cognito-idp:DescribeUserPoolClient",
+ "cognito-idp:AdminInitiateAuth",
+ "cognito-idp:AdminUserGlobalSignOut",
+ "cognito-idp:ListUserPoolClients",
+ "cognito-identity:DescribeIdentityPool",
+ "cognito-identity:UpdateIdentityPool",
+ "cognito-identity:SetIdentityPoolRoles",
+ "cognito-identity:GetIdentityPoolRoles",
+ "es:UpdateElasticsearchDomainConfig",
+ ],
+ resources: [
+ userPool.userPoolArn,
+ `arn:aws:cognito-identity:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:identitypool/${identitypool.ref}`,
+ `arn:aws:es:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:domain/${domainName}`,
+ ],
+ }),
+ new iam.PolicyStatement({
+ actions: ["iam:PassRole"],
+ conditions: {
+ StringLike: {
+ "iam:PassedToService": "cognito-identity.amazonaws.com",
+ },
+ },
+ resources: [cognitoKibanaConfigureRole.roleArn],
+ }),
+ ],
+ }
+ );
+
+ cognitoKibanaConfigureRolePolicy.attachToRole(cognitoKibanaConfigureRole);
+ return cognitoKibanaConfigureRole;
+}
diff --git a/source/patterns/@aws-solutions-constructs/core/lib/lambda-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/lambda-helper.ts
index 272cb2545..8da6aabfd 100644
--- a/source/patterns/@aws-solutions-constructs/core/lib/lambda-helper.ts
+++ b/source/patterns/@aws-solutions-constructs/core/lib/lambda-helper.ts
@@ -134,7 +134,7 @@ export function deployLambdaFunction(scope: Construct,
);
finalLambdaFunctionProps = overrideProps(finalLambdaFunctionProps, {
- securityGroups: [ lambdaSecurityGroup ],
+ securityGroups: [lambdaSecurityGroup],
vpc,
}, true);
}
@@ -210,4 +210,12 @@ function GetNextId(children: IConstruct[], coreName: string): string {
});
return `${coreName}-${lastSuffix + 1}`;
+}
+
+export function getLambdaVpcSecurityGroupIds(lambdaFunction: lambda.Function): string[] {
+ const securityGroupIds: string[] = [];
+
+ lambdaFunction.connections.securityGroups.forEach(element => securityGroupIds.push(element.securityGroupId));
+
+ return securityGroupIds;
}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/core/lib/vpc-defaults.ts b/source/patterns/@aws-solutions-constructs/core/lib/vpc-defaults.ts
index 63a0d0734..acc2e7794 100644
--- a/source/patterns/@aws-solutions-constructs/core/lib/vpc-defaults.ts
+++ b/source/patterns/@aws-solutions-constructs/core/lib/vpc-defaults.ts
@@ -48,6 +48,11 @@ export function DefaultPrivateVpcProps(): ec2.VpcProps {
cidrMask: 18,
name: "private",
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
+ },
+ {
+ cidrMask: 24,
+ name: "public",
+ subnetType: ec2.SubnetType.PUBLIC,
}
]
} as ec2.VpcProps;
diff --git a/source/patterns/@aws-solutions-constructs/core/package.json b/source/patterns/@aws-solutions-constructs/core/package.json
index c5117c973..aae093975 100644
--- a/source/patterns/@aws-solutions-constructs/core/package.json
+++ b/source/patterns/@aws-solutions-constructs/core/package.json
@@ -154,4 +154,4 @@
"@aws-cdk/aws-ssm": "0.0.0",
"@aws-cdk/aws-wafv2": "0.0.0"
}
-}
+}
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/core/test/elasticsearch-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/elasticsearch-helper.test.ts
index 08b21d694..a5029db64 100644
--- a/source/patterns/@aws-solutions-constructs/core/test/elasticsearch-helper.test.ts
+++ b/source/patterns/@aws-solutions-constructs/core/test/elasticsearch-helper.test.ts
@@ -16,9 +16,10 @@ import * as elasticsearch from '@aws-cdk/aws-elasticsearch';
import * as defaults from '../index';
import '@aws-cdk/assert/jest';
import * as iam from '@aws-cdk/aws-iam';
+import * as ec2 from '@aws-cdk/aws-ec2';
-function deployES(stack: Stack, domainName: string, cfnDomainProps?: elasticsearch.CfnDomainProps,
- lambdaRoleARN?: string): [elasticsearch.CfnDomain, iam.Role] {
+function deployES(stack: Stack, domainName: string, clientDomainProps?: elasticsearch.CfnDomainProps,
+ lambdaRoleARN?: string, vpc?: ec2.IVpc): [elasticsearch.CfnDomain, iam.Role] {
const userpool = defaults.buildUserPool(stack);
const userpoolclient = defaults.buildUserPoolClient(stack, userpool, {
userPoolClientName: 'test',
@@ -33,23 +34,32 @@ function deployES(stack: Stack, domainName: string, cfnDomainProps?: elasticsear
});
if (lambdaRoleARN) {
- return defaults.buildElasticSearch(stack, domainName, {
+ return defaults.buildElasticSearch(stack, {
userpool,
identitypool,
cognitoAuthorizedRoleARN: cognitoAuthorizedRole.roleArn,
- serviceRoleARN: lambdaRoleARN
- }, cfnDomainProps);
+ serviceRoleARN: lambdaRoleARN,
+ vpc,
+ domainName,
+ clientDomainProps
+ });
} else {
- return defaults.buildElasticSearch(stack, domainName, {
+ return defaults.buildElasticSearch(stack, {
userpool,
identitypool,
- cognitoAuthorizedRoleARN: cognitoAuthorizedRole.roleArn
- }, cfnDomainProps);
+ cognitoAuthorizedRoleARN: cognitoAuthorizedRole.roleArn,
+ vpc,
+ domainName,
+ clientDomainProps
+ });
}
}
test('Test override SnapshotOptions for buildElasticSearch', () => {
- const stack = new Stack();
+ const stack = new Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
deployES(stack, 'test-domain', {
snapshotOptions: {
automatedSnapshotStartHour: 5
@@ -132,8 +142,138 @@ test('Test override SnapshotOptions for buildElasticSearch', () => {
});
});
+test('Test VPC with 1 AZ, Zone Awareness Disabled', () => {
+ const stack = new Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const vpc = defaults.getTestVpc(stack, false);
+
+ deployES(stack, 'test-domain', {
+ elasticsearchClusterConfig: {
+ dedicatedMasterEnabled: true,
+ dedicatedMasterCount: 3,
+ instanceCount: 3,
+ zoneAwarenessEnabled: false
+ }
+ }, undefined, vpc);
+
+ expect(stack).toHaveResourceLike('AWS::Elasticsearch::Domain', {
+ DomainName: "test-domain",
+ ElasticsearchClusterConfig: {
+ DedicatedMasterCount: 3,
+ DedicatedMasterEnabled: true,
+ InstanceCount: 3,
+ ZoneAwarenessEnabled: false
+ }
+ });
+});
+
+test('Test VPC with 2 AZ, Zone Awareness Enabled', () => {
+ // If no environment is specified, a VPC will use 2 AZs by default.
+ // If an environment is specified, a VPC will use 3 AZs by default.
+ const stack = new Stack(undefined, undefined, {});
+
+ const vpc: ec2.IVpc = defaults.getTestVpc(stack, false);
+
+ deployES(stack, 'test-domain', {}, undefined, vpc);
+
+ expect(stack).toHaveResourceLike('AWS::Elasticsearch::Domain', {
+ DomainName: "test-domain",
+ ElasticsearchClusterConfig: {
+ DedicatedMasterCount: 3,
+ DedicatedMasterEnabled: true,
+ InstanceCount: 2,
+ ZoneAwarenessEnabled: true
+ }
+ });
+});
+
+test('Test VPC with 3 AZ, Zone Awareness Enabled', () => {
+ // If no environment is specified, a VPC will use 2 AZs by default.
+ // If an environment is specified, a VPC will use 3 AZs by default.
+ const stack = new Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const vpc: ec2.IVpc = defaults.getTestVpc(stack);
+
+ deployES(stack, 'test-domain', {}, undefined, vpc);
+
+ expect(stack).toHaveResourceLike('AWS::Elasticsearch::Domain', {
+ DomainName: "test-domain",
+ ElasticsearchClusterConfig: {
+ DedicatedMasterCount: 3,
+ DedicatedMasterEnabled: true,
+ InstanceCount: 3,
+ ZoneAwarenessEnabled: true
+ }
+ });
+});
+
+test('Test deployment with an existing private VPC', () => {
+ const stack = new Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const vpc = new ec2.Vpc(stack, 'existing-private-vpc-test', {
+ natGateways: 1,
+ subnetConfiguration: [
+ {
+ cidrMask: 24,
+ name: 'application',
+ subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
+ },
+ {
+ cidrMask: 24,
+ name: "public",
+ subnetType: ec2.SubnetType.PUBLIC,
+ }
+ ]
+ });
+
+ deployES(stack, 'test-domain', {}, undefined, vpc);
+
+ expect(stack).toHaveResourceLike('AWS::Elasticsearch::Domain', {
+ DomainName: "test-domain",
+ ElasticsearchClusterConfig: {
+ DedicatedMasterCount: 3,
+ DedicatedMasterEnabled: true,
+ InstanceCount: 3,
+ ZoneAwarenessEnabled: true
+ }
+ });
+});
+
+test('Test error thrown with no private subnet configurations', () => {
+ const stack = new Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
+ const vpc = defaults.buildVpc(stack, {
+ defaultVpcProps: {
+ subnetConfiguration: [
+ {
+ cidrMask: 18,
+ name: "public",
+ subnetType: ec2.SubnetType.PUBLIC,
+ }
+ ]
+ }
+ });
+
+ const app = () => {
+ deployES(stack, 'test-domain', {}, undefined, vpc);
+ };
+
+ expect(app).toThrowError('Error - ElasticSearch Domains can only be deployed in Isolated or Private subnets');
+});
+
test('Test override ES version for buildElasticSearch', () => {
- const stack = new Stack();
+ const stack = new Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
deployES(stack, 'test-domain', {
elasticsearchVersion: '7.0'
});
@@ -216,7 +356,10 @@ test('Test override ES version for buildElasticSearch', () => {
});
test('Test ES with lambdaRoleARN', () => {
- const stack = new Stack();
+ const stack = new Stack(undefined, undefined, {
+ env: { account: "123456789012", region: 'us-east-1' },
+ });
+
deployES(stack, 'test-domain', {}, 'arn:aws:us-east-1:mylambdaRoleARN');
expect(stack).toHaveResource('AWS::Elasticsearch::Domain', {
diff --git a/source/patterns/@aws-solutions-constructs/core/test/lambda-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/lambda-helper.test.ts
index a47bf2ad5..efd4db97b 100644
--- a/source/patterns/@aws-solutions-constructs/core/test/lambda-helper.test.ts
+++ b/source/patterns/@aws-solutions-constructs/core/test/lambda-helper.test.ts
@@ -255,7 +255,7 @@ test("Test for error if VPC in arguments AND in Lambda Function properties", ()
vpc: fakeVpc,
};
- const app = () => {
+ const app = () => {
defaults.deployLambdaFunction(stack, lambdaFunctionProps, undefined, fakeVpc);
};
@@ -426,4 +426,25 @@ test('Test minimum deployment with an existing VPC as a vpc parameter in deployL
]
}
});
+});
+
+test("Test retrieving lambda vpc security group ids", () => {
+ const stack = new Stack();
+
+ const vpc = defaults.getTestVpc(stack);
+ const securityGroup1 = new ec2.SecurityGroup(stack, 'SecurityGroup1', { vpc });
+ const securityGroup2 = new ec2.SecurityGroup(stack, 'SecurityGroup2', { vpc });
+
+ const testLambdaFunction = new lambda.Function(stack, 'test-lamba', {
+ runtime: lambda.Runtime.NODEJS_14_X,
+ handler: "index.handler",
+ code: lambda.Code.fromAsset(`${__dirname}/lambda`),
+ securityGroups: [securityGroup1, securityGroup2],
+ vpc
+ });
+
+ const securityGroups = defaults.getLambdaVpcSecurityGroupIds(testLambdaFunction);
+
+ expect(securityGroups).toContain(securityGroup1.securityGroupId);
+ expect(securityGroups).toContain(securityGroup2.securityGroupId);
});
\ No newline at end of file
diff --git a/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts b/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts
index 189e58dd4..4ae7871b2 100644
--- a/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts
+++ b/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts
@@ -79,7 +79,7 @@ export function generateIntegStackName(filename: string): string {
}
// Helper Functions
-export function getTestVpc(stack: Stack, publicFacing: boolean = true) {
+export function getTestVpc(stack: Stack, publicFacing: boolean = true, userVpcProps?: ec2.VpcProps) {
return buildVpc(stack, {
defaultVpcProps: publicFacing ?
DefaultPublicPrivateVpcProps() :
@@ -89,6 +89,7 @@ export function getTestVpc(stack: Stack, publicFacing: boolean = true) {
enableDnsSupport: true,
cidr: '172.168.0.0/16',
},
+ userVpcProps
});
}