diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/.eslintignore b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/.eslintignore new file mode 100644 index 000000000..0819e2e65 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/.eslintignore @@ -0,0 +1,5 @@ +lib/*.js +test/*.js +*.d.ts +coverage +test/lambda/index.js \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/.gitignore b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/.gitignore new file mode 100644 index 000000000..8626f2274 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/.gitignore @@ -0,0 +1,16 @@ +lib/*.js +test/*.js +!test/lambda/* +*.js.map +*.d.ts +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/.npmignore b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/.npmignore new file mode 100644 index 000000000..f66791629 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/.npmignore @@ -0,0 +1,21 @@ +# Exclude typescript source and config +*.ts +tsconfig.json +coverage +.nyc_output +*.tgz +*.snk +*.tsbuildinfo + +# Include javascript files and typescript declarations +!*.js +!*.d.ts + +# Exclude jsii outdir +dist + +# Include .jsii +!.jsii + +# Include .jsii +!.jsii \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/README.md b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/README.md new file mode 100644 index 000000000..045675503 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/README.md @@ -0,0 +1,99 @@ +# aws-events-rule-kinesisfirehose-s3 module + + +--- + +![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge) + +> All classes are under active development and subject to non-backward compatible changes or removal in any +> future version. These are not subject to the [Semantic Versioning](https://semver.org/) model. +> This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package. + +--- + + +| **Reference Documentation**:| https://docs.aws.amazon.com/solutions/latest/constructs/| +|:-------------|:-------------| +
+ +| **Language** | **Package** | +|:-------------|-----------------| +|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_events_rule_kinesisfirehose_s3`| +|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3`| +|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.eventsrulekinesisfirehoses3`| + +This AWS Solutions Construct implements an Amazon CloudWatch Events rule to send data to an Amazon Kinesis Data Firehose delivery stream connected to an Amazon S3 bucket. + +Here is a minimal deployable pattern definition in Typescript: + +``` javascript +import * as cdk from '@aws-cdk/core'; +import { EventsRuleToKinesisFirehoseToS3, EventsRuleToKinesisFirehoseToS3Props } from '@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3'; + +const eventsRuleToKinesisFirehoseToS3Props: EventsRuleToKinesisFirehoseToS3Props = { + eventRuleProps: { + schedule: events.Schedule.rate(cdk.Duration.minutes(5)) + } +}; + +new EventsRuleToKinesisFirehoseToS3(this, 'test-events-rule-firehose-s3', eventsRuleToKinesisFirehoseToS3Props); + +``` + +## Initializer + +``` text +new EventsRuleToKinesisFirehoseToS3(scope: Construct, id: string, props: EventsRuleToKinesisFirehoseToS3Props); +``` + +_Parameters_ + +* scope [`Construct`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html) +* id `string` +* props [`EventsRuleToKinesisFirehoseToS3Props`](#pattern-construct-props) + +## Pattern Construct Props + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|eventRuleProps|[`events.RuleProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-events.RuleProps.html)|User provided eventRuleProps to override the defaults.| +|kinesisFirehoseProps?|[`kinesisfirehose.CfnDeliveryStreamProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesisfirehose.CfnDeliveryStreamProps.html)|Optional user provided props to override the default props for Kinesis Firehose Delivery Stream| +|existingBucketObj?|[`s3.IBucket`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.IBucket.html)|Existing instance of S3 Bucket object, if this is set then the bucketProps is ignored.| +|bucketProps?|[`s3.BucketProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.BucketProps.html)|User provided props to override the default props for the S3 Bucket.| + +## Pattern Properties + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|eventsRule|[`events.Rule`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-events.Rule.html)|Returns an instance of events.Rule created by the construct.| +|kinesisFirehose|[`kinesisfirehose.CfnDeliveryStream`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesisfirehose.CfnDeliveryStream.html)|Returns an instance of kinesisfirehose.CfnDeliveryStream created by the construct| +|s3Bucket?|[`s3.Bucket`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.Bucket.html)|Returns an instance of s3.Bucket created by the construct| +|s3LoggingBucket?|[`s3.Bucket`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.Bucket.html)|Returns an instance of s3.Bucket created by the construct as the logging bucket for the primary bucket.| +|eventsRole|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|Returns an instance of the iam.Role created by the construct for Events Rule| +|kinesisFirehoseRole|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|Returns an instance of the iam.Role created by the construct for Kinesis Data Firehose delivery stream| +|kinesisFirehoseLogGroup|[`logs.LogGroup`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-logs.LogGroup.html)|Returns an instance of the LogGroup created by the construct for Kinesis Data Firehose delivery stream| + +## Default settings + +Out of the box implementation of the Construct without any override will set the following defaults: + +### Amazon CloudWatch Events Rule +* Configure least privilege access IAM role for Events Rule to publish to the Kinesis Firehose Delivery Stream. + +### Amazon Kinesis Firehose +* Enable CloudWatch logging for Kinesis Firehose +* Configure least privilege access IAM role for Amazon Kinesis Firehose + +### Amazon S3 Bucket +* Configure Access logging for S3 Bucket +* Enable server-side encryption for S3 Bucket using AWS managed KMS Key +* Turn on the versioning for S3 Bucket +* Don't allow public access for S3 Bucket +* Retain the S3 Bucket when deleting the CloudFormation stack +* Applies Lifecycle rule to move noncurrent object versions to Glacier storage after 90 days + +## Architecture +![Architecture Diagram](architecture.png) + +*** +© Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/architecture.png b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/architecture.png new file mode 100644 index 000000000..4b01c8f39 Binary files /dev/null and b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/architecture.png differ diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/lib/index.ts new file mode 100755 index 000000000..f786b7e59 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/lib/index.ts @@ -0,0 +1,122 @@ +/** + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import * as events from '@aws-cdk/aws-events'; +import * as kinesisfirehose from '@aws-cdk/aws-kinesisfirehose'; +import * as defaults from '@aws-solutions-constructs/core'; +import * as iam from '@aws-cdk/aws-iam'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as logs from '@aws-cdk/aws-logs'; +import { Construct } from '@aws-cdk/core'; +import { overrideProps } from '@aws-solutions-constructs/core'; +import { KinesisFirehoseToS3 } from '@aws-solutions-constructs/aws-kinesisfirehose-s3'; + +/** + * @summary The properties for the EventsRuleToKinesisFirehoseToS3 Construct + */ +export interface EventsRuleToKinesisFirehoseToS3Props { + /** + * User provided eventRuleProps to override the defaults + * + * @default - None + */ + readonly eventRuleProps: events.RuleProps + /** + * User provided props to override the default props for the Kinesis Firehose. + * + * @default - Default props are used + */ + readonly kinesisFirehoseProps?: kinesisfirehose.CfnDeliveryStreamProps | any + /** + * Existing instance of S3 Bucket object, if this is set then the bucketProps is ignored. + * + * @default - None + */ + readonly existingBucketObj?: s3.IBucket, + /** + * User provided props to override the default props for the S3 Bucket. + * + * @default - Default props are used + */ + readonly bucketProps?: s3.BucketProps +} + +export class EventsRuleToKinesisFirehoseToS3 extends Construct { + public readonly eventsRule: events.Rule; + public readonly eventsRole: iam.Role; + public readonly kinesisFirehose: kinesisfirehose.CfnDeliveryStream; + public readonly kinesisFirehoseLogGroup: logs.LogGroup; + public readonly kinesisFirehoseRole: iam.Role; + public readonly s3Bucket?: s3.Bucket; + public readonly s3LoggingBucket?: s3.Bucket; + + /** + * @summary Constructs a new instance of the EventsRuleToKinesisFirehoseToS3 class. + * @param {cdk.App} scope - represents the scope for all the resources. + * @param {string} id - this is a a scope-unique id. + * @param {EventsRuleToKinesisFirehoseToS3Props} props - user provided props for the construct + * @since 0.8.0 + * @access public + */ + constructor(scope: Construct, id: string, props: EventsRuleToKinesisFirehoseToS3Props) { + super(scope, id); + + // Set up the Kinesis Firehose using KinesisFirehoseToS3 construct + const firehoseToS3 = new KinesisFirehoseToS3(this, 'KinesisFirehoseToS3', { + kinesisFirehoseProps: props.kinesisFirehoseProps, + existingBucketObj: props.existingBucketObj, + bucketProps: props.bucketProps + }); + this.kinesisFirehose = firehoseToS3.kinesisFirehose; + this.s3Bucket = firehoseToS3.s3Bucket; + this.kinesisFirehoseRole = firehoseToS3.kinesisFirehoseRole; + this.s3LoggingBucket = firehoseToS3.s3LoggingBucket; + this.kinesisFirehoseLogGroup = firehoseToS3.kinesisFirehoseLogGroup; + + // Create an events service role + this.eventsRole = new iam.Role(this, 'EventsRuleInvokeKinesisFirehoseRole', { + assumedBy: new iam.ServicePrincipal('events.amazonaws.com'), + description: 'Events Rule To Kinesis Firehose Role', + }); + + // Setup the IAM policy that grants events rule the permission to send cw events data to kinesis firehose + const eventsPolicy = new iam.Policy(this, 'EventsRuleInvokeKinesisFirehosePolicy', { + statements: [new iam.PolicyStatement({ + actions: [ + 'firehose:PutRecord', + 'firehose:PutRecordBatch' + ], + resources: [this.kinesisFirehose.attrArn] + }) + ]}); + + // Attach policy to role + eventsPolicy.attachToRole(this.eventsRole); + + // Set up the Kinesis Firehose as the target for event rule + const KinesisFirehoseEventTarget: events.IRuleTarget = { + bind: () => ({ + id: '', + arn: this.kinesisFirehose.attrArn, + role: this.eventsRole + }) + }; + + // Set up the events rule props + const defaultEventsRuleProps = defaults.DefaultEventsRuleProps([KinesisFirehoseEventTarget]); + const eventsRuleProps = overrideProps(defaultEventsRuleProps, props.eventRuleProps, true); + + this.eventsRule = new events.Rule(this, 'EventsRule', eventsRuleProps); + + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/package.json new file mode 100644 index 000000000..66098a047 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/package.json @@ -0,0 +1,87 @@ +{ + "name": "@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3", + "version": "1.69.0", + "description": "CDK Constructs for Amazon CloudWatch Events Rule to Amazon Kinesis Firehose to Amazon S3 integration.", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/awslabs/aws-solutions-constructs.git", + "directory": "source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3" + }, + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "scripts": { + "build": "tsc -b .", + "lint": "eslint -c ../eslintrc.yml --ext=.js,.ts . && tslint --project .", + "lint-fix": "eslint -c ../eslintrc.yml --ext=.js,.ts --fix .", + "test": "jest --coverage", + "clean": "tsc -b --clean", + "watch": "tsc -b -w", + "integ": "cdk-integ", + "integ-assert": "cdk-integ-assert", + "integ-no-clean": "cdk-integ --no-clean", + "jsii": "jsii", + "jsii-pacmak": "jsii-pacmak", + "build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert", + "snapshot-update": "npm run jsii && npm test -- -u && npm run integ-assert" + }, + "jsii": { + "outdir": "dist", + "targets": { + "java": { + "package": "software.amazon.awsconstructs.services.eventsrulekinesisfirehoses3", + "maven": { + "groupId": "software.amazon.awsconstructs", + "artifactId": "eventsrulekinesisfirehoses3" + } + }, + "dotnet": { + "namespace": "Amazon.Constructs.AWS.EventsRuleKinesisFirehoseS3", + "packageId": "Amazon.Constructs.AWS.EventsRuleKinesisFirehoseS3", + "signAssembly": true, + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "python": { + "distName": "aws-solutions-constructs.aws-events-rule-kinesis-firehose-s3", + "module": "aws_solutions_constructs.aws_events_rule_kinesis_firehose_s3" + } + } + }, + "dependencies": { + "@aws-cdk/aws-iam": "~1.69.0", + "@aws-cdk/aws-kinesisfirehose": "~1.69.0", + "@aws-cdk/aws-events": "~1.69.0", + "@aws-cdk/core": "~1.69.0", + "@aws-cdk/aws-s3": "~1.69.0", + "@aws-cdk/aws-logs": "~1.69.0", + "@aws-solutions-constructs/core": "~1.69.0", + "@aws-solutions-constructs/aws-kinesisfirehose-s3": "~1.69.0", + "constructs": "^3.0.4" + }, + "devDependencies": { + "@aws-cdk/assert": "~1.69.0", + "@types/jest": "^24.0.23", + "@types/node": "^10.3.0" + }, + "jest": { + "moduleFileExtensions": [ + "js" + ] + }, + "peerDependencies": { + "@aws-cdk/aws-iam": "~1.69.0", + "@aws-cdk/aws-kinesisfirehose": "~1.69.0", + "@aws-cdk/aws-events": "~1.69.0", + "@aws-cdk/aws-s3": "~1.69.0", + "@aws-cdk/core": "~1.69.0", + "@aws-solutions-constructs/core": "~1.69.0", + "@aws-solutions-constructs/aws-kinesisfirehose-s3": "~1.69.0", + "constructs": "^3.0.4", + "@aws-cdk/aws-logs": "~1.69.0" + } +} diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/__snapshots__/events-rule-kinesisfirehose-s3.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/__snapshots__/events-rule-kinesisfirehose-s3.test.js.snap new file mode 100644 index 000000000..876ac825b --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/__snapshots__/events-rule-kinesisfirehose-s3.test.js.snap @@ -0,0 +1,401 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Test snapshot match with default parameters 1`] = ` +Object { + "Resources": Object { + "testeventsrulekinesisfirehoses3defaultparametersEventsRule7C3C24DD": Object { + "Properties": Object { + "Description": "event rule props", + "ScheduleExpression": "rate(5 minutes)", + "State": "ENABLED", + "Targets": Array [ + Object { + "Arn": Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3KinesisFirehose5062AB01", + "Arn", + ], + }, + "Id": "Target0", + "RoleArn": Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisfirehoses3defaultparametersEventsRuleInvokeKinesisFirehoseRole62BBAEA9", + "Arn", + ], + }, + }, + ], + }, + "Type": "AWS::Events::Rule", + }, + "testeventsrulekinesisfirehoses3defaultparametersEventsRuleInvokeKinesisFirehosePolicy66344F2E": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "firehose:PutRecord", + "firehose:PutRecordBatch", + ], + "Effect": "Allow", + "Resource": Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3KinesisFirehose5062AB01", + "Arn", + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "testeventsrulekinesisfirehoses3defaultparametersEventsRuleInvokeKinesisFirehosePolicy66344F2E", + "Roles": Array [ + Object { + "Ref": "testeventsrulekinesisfirehoses3defaultparametersEventsRuleInvokeKinesisFirehoseRole62BBAEA9", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "testeventsrulekinesisfirehoses3defaultparametersEventsRuleInvokeKinesisFirehoseRole62BBAEA9": Object { + "Properties": Object { + "AssumeRolePolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": Object { + "Service": "events.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "Description": "Events Rule To Kinesis Firehose Role", + }, + "Type": "AWS::IAM::Role", + }, + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3KinesisFirehose5062AB01": Object { + "Properties": Object { + "ExtendedS3DestinationConfiguration": Object { + "BucketARN": Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3Bucket6EC968FC", + "Arn", + ], + }, + "BufferingHints": Object { + "IntervalInSeconds": 300, + "SizeInMBs": 5, + }, + "CloudWatchLoggingOptions": Object { + "Enabled": true, + "LogGroupName": Object { + "Ref": "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3firehoseloggroup6BC7E438", + }, + "LogStreamName": Object { + "Ref": "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3firehoseloggroupfirehoselogstream158BBACB", + }, + }, + "CompressionFormat": "GZIP", + "EncryptionConfiguration": Object { + "KMSEncryptionConfig": Object { + "AWSKMSKeyARN": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":kms:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":alias/aws/s3", + ], + ], + }, + }, + }, + "RoleARN": Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3KinesisFirehoseRole9A570638", + "Arn", + ], + }, + }, + }, + "Type": "AWS::KinesisFirehose::DeliveryStream", + }, + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3KinesisFirehosePolicyC8A844AA": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "s3:AbortMultipartUpload", + "s3:GetBucketLocation", + "s3:GetObject", + "s3:ListBucket", + "s3:ListBucketMultipartUploads", + "s3:PutObject", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3Bucket6EC968FC", + "Arn", + ], + }, + Object { + "Fn::Join": Array [ + "", + Array [ + Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3Bucket6EC968FC", + "Arn", + ], + }, + "/*", + ], + ], + }, + ], + }, + Object { + "Action": "logs:PutLogEvents", + "Effect": "Allow", + "Resource": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":logs:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":log-group:", + Object { + "Ref": "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3firehoseloggroup6BC7E438", + }, + ":log-stream:", + Object { + "Ref": "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3firehoseloggroupfirehoselogstream158BBACB", + }, + ], + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3KinesisFirehosePolicyC8A844AA", + "Roles": Array [ + Object { + "Ref": "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3KinesisFirehoseRole9A570638", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3KinesisFirehoseRole9A570638": Object { + "Properties": Object { + "AssumeRolePolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": Object { + "Service": "firehose.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::IAM::Role", + }, + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3Bucket6EC968FC": Object { + "DeletionPolicy": "Retain", + "Properties": Object { + "BucketEncryption": Object { + "ServerSideEncryptionConfiguration": Array [ + Object { + "ServerSideEncryptionByDefault": Object { + "SSEAlgorithm": "AES256", + }, + }, + ], + }, + "LifecycleConfiguration": Object { + "Rules": Array [ + Object { + "NoncurrentVersionTransitions": Array [ + Object { + "StorageClass": "GLACIER", + "TransitionInDays": 90, + }, + ], + "Status": "Enabled", + }, + ], + }, + "LoggingConfiguration": Object { + "DestinationBucketName": Object { + "Ref": "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3LoggingBucket4233ECBF", + }, + }, + "PublicAccessBlockConfiguration": Object { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true, + }, + "VersioningConfiguration": Object { + "Status": "Enabled", + }, + }, + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Retain", + }, + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3BucketPolicyB9432F88": Object { + "Properties": Object { + "Bucket": Object { + "Ref": "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3Bucket6EC968FC", + }, + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::Join": Array [ + "", + Array [ + Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3Bucket6EC968FC", + "Arn", + ], + }, + "/*", + ], + ], + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::S3::BucketPolicy", + }, + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3LoggingBucket4233ECBF": Object { + "DeletionPolicy": "Retain", + "Metadata": Object { + "cfn_nag": Object { + "rules_to_suppress": Array [ + Object { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket", + }, + ], + }, + }, + "Properties": Object { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": Object { + "ServerSideEncryptionConfiguration": Array [ + Object { + "ServerSideEncryptionByDefault": Object { + "SSEAlgorithm": "AES256", + }, + }, + ], + }, + "PublicAccessBlockConfiguration": Object { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true, + }, + }, + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Retain", + }, + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3LoggingBucketPolicy8FE1A9E5": Object { + "Properties": Object { + "Bucket": Object { + "Ref": "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3LoggingBucket4233ECBF", + }, + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::Join": Array [ + "", + Array [ + Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3S3LoggingBucket4233ECBF", + "Arn", + ], + }, + "/*", + ], + ], + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::S3::BucketPolicy", + }, + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3firehoseloggroup6BC7E438": Object { + "DeletionPolicy": "Retain", + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + }, + "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3firehoseloggroupfirehoselogstream158BBACB": Object { + "DeletionPolicy": "Retain", + "Properties": Object { + "LogGroupName": Object { + "Ref": "testeventsrulekinesisfirehoses3defaultparametersKinesisFirehoseToS3firehoseloggroup6BC7E438", + }, + }, + "Type": "AWS::Logs::LogStream", + "UpdateReplacePolicy": "Retain", + }, + }, +} +`; diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/events-rule-kinesisfirehose-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/events-rule-kinesisfirehose-s3.test.ts new file mode 100644 index 000000000..702e5d4f2 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/events-rule-kinesisfirehose-s3.test.ts @@ -0,0 +1,126 @@ +/** + * Copyright 2020 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 { SynthUtils } from '@aws-cdk/assert'; +import * as cdk from "@aws-cdk/core"; +import * as events from "@aws-cdk/aws-events"; +import '@aws-cdk/assert/jest'; +import {EventsRuleToKinesisFirehoseToS3, EventsRuleToKinesisFirehoseToS3Props} from '../lib'; + +// -------------------------------------------------------------- +// Test snapshot match with default parameters +// -------------------------------------------------------------- +function deployNewStack(stack: cdk.Stack) { + const props: EventsRuleToKinesisFirehoseToS3Props = { + eventRuleProps: { + description: 'event rule props', + schedule: events.Schedule.rate(cdk.Duration.minutes(5)) + } + }; + return new EventsRuleToKinesisFirehoseToS3(stack, 'test-events-rule-kinesis-firehose-s3-default-parameters', props); +} + +test('Test snapshot match with default parameters', () => { + const stack = new cdk.Stack(); + deployNewStack(stack); + + // Assertions + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); +}); + +// -------------------------------------------------------------- +// Test properties +// -------------------------------------------------------------- +test('Test properties', () => { + const stack = new cdk.Stack(); + const construct: EventsRuleToKinesisFirehoseToS3 = deployNewStack(stack); + + // Assertions + expect(construct.eventsRule !== null); + expect(construct.eventsRole !== null); + expect(construct.kinesisFirehose !== null); + expect(construct.kinesisFirehoseRole !== null); + expect(construct.kinesisFirehoseLogGroup !== null); + expect(construct.s3Bucket !== null); + expect(construct.s3LoggingBucket !== null); +}); + +// -------------------------------------------------------------- +// Test default server side s3 bucket encryption +// -------------------------------------------------------------- +test('Test default server side s3 bucket encryption', () => { + const stack = new cdk.Stack(); + deployNewStack(stack); + + // Assertions + expect(stack).toHaveResource('AWS::S3::Bucket', { + BucketEncryption: { + ServerSideEncryptionConfiguration: [ + { + ServerSideEncryptionByDefault: { + SSEAlgorithm: "AES256" + } + } + ] + } + }); +}); + +// -------------------------------------------------------------- +// Test property override +// -------------------------------------------------------------- +test('Test property override', () => { + const stack = new cdk.Stack(); + + // create properties + const props: EventsRuleToKinesisFirehoseToS3Props = { + eventRuleProps: { + description: 'event rule props', + schedule: events.Schedule.rate(cdk.Duration.minutes(5)) + }, + kinesisFirehoseProps: { + extendedS3DestinationConfiguration: { + bufferingHints: { + intervalInSeconds: 600, + sizeInMBs: 55 + }, + } + }, + bucketProps: { + blockPublicAccess: { + blockPublicAcls: false, + blockPublicPolicy: true, + ignorePublicAcls: false, + restrictPublicBuckets: true + } + } + }; + new EventsRuleToKinesisFirehoseToS3(stack, 'test-events-rule-firehose-s3', props); + + expect(stack).toHaveResource("AWS::S3::Bucket", { + PublicAccessBlockConfiguration: { + BlockPublicAcls: false, + BlockPublicPolicy: true, + IgnorePublicAcls: false, + RestrictPublicBuckets: true + }, + }); + + expect(stack).toHaveResourceLike("AWS::KinesisFirehose::DeliveryStream", { + ExtendedS3DestinationConfiguration: { + BufferingHints: { + IntervalInSeconds: 600, + SizeInMBs: 55 + } + }}); +}); \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/integ.events-rule-kinesisfirehose-s3-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/integ.events-rule-kinesisfirehose-s3-no-arguments.expected.json new file mode 100644 index 000000000..cddb1b97e --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/integ.events-rule-kinesisfirehose-s3-no-arguments.expected.json @@ -0,0 +1,397 @@ +{ + "Description": "Integration Test for aws-events-rule-kinesisfirehose-s3", + "Resources": { + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3LoggingBucket44AC2AFE": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3LoggingBucketPolicyF495B6C7": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3LoggingBucket44AC2AFE" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3LoggingBucket44AC2AFE", + "Arn" + ] + }, + "/*" + ] + ] + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + } + } + }, + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3BucketBFD4D764": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LifecycleConfiguration": { + "Rules": [ + { + "NoncurrentVersionTransitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 90 + } + ], + "Status": "Enabled" + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3LoggingBucket44AC2AFE" + } + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3BucketPolicyA55908CC": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3BucketBFD4D764" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3BucketBFD4D764", + "Arn" + ] + }, + "/*" + ] + ] + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + } + } + }, + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3firehoseloggroup1A52047F": { + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3firehoseloggroupfirehoselogstreamFEF0376E": { + "Type": "AWS::Logs::LogStream", + "Properties": { + "LogGroupName": { + "Ref": "testeventsrulekinesisfirehoses3KinesisFirehoseToS3firehoseloggroup1A52047F" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3KinesisFirehoseRole7DFE85DB": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "firehose.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3KinesisFirehosePolicy9E420F18": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:AbortMultipartUpload", + "s3:GetBucketLocation", + "s3:GetObject", + "s3:ListBucket", + "s3:ListBucketMultipartUploads", + "s3:PutObject" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3BucketBFD4D764", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3BucketBFD4D764", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "logs:PutLogEvents", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:", + { + "Ref": "testeventsrulekinesisfirehoses3KinesisFirehoseToS3firehoseloggroup1A52047F" + }, + ":log-stream:", + { + "Ref": "testeventsrulekinesisfirehoses3KinesisFirehoseToS3firehoseloggroupfirehoselogstreamFEF0376E" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testeventsrulekinesisfirehoses3KinesisFirehoseToS3KinesisFirehosePolicy9E420F18", + "Roles": [ + { + "Ref": "testeventsrulekinesisfirehoses3KinesisFirehoseToS3KinesisFirehoseRole7DFE85DB" + } + ] + } + }, + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3KinesisFirehose8BD45033": { + "Type": "AWS::KinesisFirehose::DeliveryStream", + "Properties": { + "ExtendedS3DestinationConfiguration": { + "BucketARN": { + "Fn::GetAtt": [ + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3S3BucketBFD4D764", + "Arn" + ] + }, + "BufferingHints": { + "IntervalInSeconds": 300, + "SizeInMBs": 5 + }, + "CloudWatchLoggingOptions": { + "Enabled": true, + "LogGroupName": { + "Ref": "testeventsrulekinesisfirehoses3KinesisFirehoseToS3firehoseloggroup1A52047F" + }, + "LogStreamName": { + "Ref": "testeventsrulekinesisfirehoses3KinesisFirehoseToS3firehoseloggroupfirehoselogstreamFEF0376E" + } + }, + "CompressionFormat": "GZIP", + "EncryptionConfiguration": { + "KMSEncryptionConfig": { + "AWSKMSKeyARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":kms:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":alias/aws/s3" + ] + ] + } + } + }, + "RoleARN": { + "Fn::GetAtt": [ + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3KinesisFirehoseRole7DFE85DB", + "Arn" + ] + } + } + } + }, + "testeventsrulekinesisfirehoses3EventsRuleInvokeKinesisFirehoseRole87540EFF": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Events Rule To Kinesis Firehose Role" + } + }, + "testeventsrulekinesisfirehoses3EventsRuleInvokeKinesisFirehosePolicy5B799D25": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "firehose:PutRecord", + "firehose:PutRecordBatch" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3KinesisFirehose8BD45033", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testeventsrulekinesisfirehoses3EventsRuleInvokeKinesisFirehosePolicy5B799D25", + "Roles": [ + { + "Ref": "testeventsrulekinesisfirehoses3EventsRuleInvokeKinesisFirehoseRole87540EFF" + } + ] + } + }, + "testeventsrulekinesisfirehoses3EventsRule1D7BA7FB": { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "rate(5 minutes)", + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "testeventsrulekinesisfirehoses3KinesisFirehoseToS3KinesisFirehose8BD45033", + "Arn" + ] + }, + "Id": "Target0", + "RoleArn": { + "Fn::GetAtt": [ + "testeventsrulekinesisfirehoses3EventsRuleInvokeKinesisFirehoseRole87540EFF", + "Arn" + ] + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/integ.events-rule-kinesisfirehose-s3-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/integ.events-rule-kinesisfirehose-s3-no-arguments.ts new file mode 100644 index 000000000..d351ff36a --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/integ.events-rule-kinesisfirehose-s3-no-arguments.ts @@ -0,0 +1,30 @@ +/** + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import * as events from '@aws-cdk/aws-events'; +import { App, Stack, Duration } from '@aws-cdk/core'; +import {EventsRuleToKinesisFirehoseToS3, EventsRuleToKinesisFirehoseToS3Props} from '../lib'; + +const app = new App(); +const stack = new Stack(app, 'test-events-rule-kinesisfirehose-s3'); +stack.templateOptions.description = 'Integration Test for aws-events-rule-kinesisfirehose-s3'; + +const props: EventsRuleToKinesisFirehoseToS3Props = { + eventRuleProps: { + schedule: events.Schedule.rate(Duration.minutes(5)) + } +}; + +new EventsRuleToKinesisFirehoseToS3(stack, 'test-events-rule-kinesisfirehose-s3', props); + +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/.eslintignore b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/.eslintignore new file mode 100644 index 000000000..e6f7801ea --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/.eslintignore @@ -0,0 +1,4 @@ +lib/*.js +test/*.js +*.d.ts +coverage diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/.gitignore b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/.gitignore new file mode 100644 index 000000000..963955620 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/.gitignore @@ -0,0 +1,15 @@ +lib/*.js +test/*.js +*.js.map +*.d.ts +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/.npmignore b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/.npmignore new file mode 100644 index 000000000..1ce9af351 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/.npmignore @@ -0,0 +1,21 @@ +# Exclude typescript source and config +*.ts +tsconfig.json +coverage +.nyc_output +*.tgz +*.snk +*.tsbuildinfo + +# Include javascript files and typescript declarations +!*.js +!*.d.ts + +# Exclude jsii outdir +dist + +# Include .jsii +!.jsii + +# Include .jsii +!.jsii diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/README.md b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/README.md new file mode 100644 index 000000000..32e498b75 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/README.md @@ -0,0 +1,85 @@ +# aws-events-rule-kinesisstreams module + + +--- + +![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge) + +> All classes are under active development and subject to non-backward compatible changes or removal in any +> future version. These are not subject to the [Semantic Versioning](https://semver.org/) model. +> This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package. + +--- + + +| **Reference Documentation**:| https://docs.aws.amazon.com/solutions/latest/constructs/| +|:-------------|:-------------| +
+ +| **Language** | **Package** | +|:-------------|-----------------| +|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_events_rule_kinesisstream`| +|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-events-rule-kinesisstreams`| +|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.eventsrulekinesisstream`| + +This AWS Solutions Construct implements an Amazon CloudWatch Events rule to send data to an Amazon Kinesis Data Stream + +Here is a minimal deployable pattern definition in Typescript: + +``` typescript +import * as cdk from '@aws-cdk/core'; +import {EventsRuleToKinesisStreams, EventsRuleToKinesisStreamsProps} from "@aws-solutions-constructs/aws-events-rule-kinesisstreams"; + +const props: EventsRuleToKinesisStreamsProps = { + eventRuleProps: { + schedule: events.Schedule.rate(Duration.minutes(5)), + } +}; + +new EventsRuleToKinesisStreams(this, 'test-events-rule-kinesis-stream', props); +``` + +## Initializer + +``` text +new EventsRuleToKinesisStreams(scope: Construct, id: string, props: EventsRuleToKinesisStreamsProps); +``` + +_Parameters_ + +* scope [`Construct`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html) +* id `string` +* props [`EventsRuleToKinesisStreamsProps`](#pattern-construct-props) + +## Pattern Construct Props + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|eventRuleProps|[`events.RuleProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-events.RuleProps.html)|User provided eventRuleProps to override the defaults. | +|existingStreamObj?|[`kinesis.Stream`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesis.Stream.html)|Existing instance of Kinesis Stream, if this is set then kinesisStreamProps is ignored.| +|kinesisStreamProps?|[`kinesis.StreamProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesis.StreamProps.html)|Optional user-provided props to override the default props for the Kinesis stream. | +|createCloudWatchAlarms|`boolean`|Whether to create recommended CloudWatch alarms. | + +## Pattern Properties + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|eventsRule|[`events.Rule`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-events.Rule.html)|Returns an instance of events.Rule created by the construct.| +|kinesisStream|[`kinesis.Stream`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesis.Stream.html)|Returns an instance of the Kinesis stream created by the pattern.| +|eventsRole?|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|Returns an instance of the iam.Role created by the construct for events rule.| + +## Default settings + +Out of the box implementation of the Construct without any override will set the following defaults: + +### Amazon CloudWatch Events Rule +* Configure least privilege access IAM role for Events Rule to publish to the Kinesis Data Stream. + +### Amazon Kinesis Stream +* Enable server-side encryption for Kinesis Data Stream using AWS Managed KMS Key. + +## Architecture +![Architecture Diagram](architecture.png) + +*** +© Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/architecture.png b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/architecture.png new file mode 100644 index 000000000..b2404e822 Binary files /dev/null and b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/architecture.png differ diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/lib/index.ts new file mode 100644 index 000000000..6275d616f --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/lib/index.ts @@ -0,0 +1,105 @@ +/** + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import * as events from '@aws-cdk/aws-events'; +import * as kinesis from '@aws-cdk/aws-kinesis'; +import * as iam from '@aws-cdk/aws-iam'; +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; +import { Construct } from '@aws-cdk/core'; +import * as defaults from '@aws-solutions-constructs/core'; +import { overrideProps } from '@aws-solutions-constructs/core'; + +/** + * @summary The properties for the EventsRuleToKinesisStreams Construct + */ +export interface EventsRuleToKinesisStreamsProps { +/** + * User provided eventRuleProps to override the defaults + * + * @default - None + */ +readonly eventRuleProps: events.RuleProps +/** + * Existing instance of Kinesis Stream object, if this is set then the KinesisStreamProps is ignored. + * + * @default - Default props are used + */ +readonly existingStreamObj?: kinesis.Stream, +/** + * User provided props to override the default props for the Kinesis Stream. + * + * @default - Default props are used + */ +readonly kinesisStreamProps?: kinesis.StreamProps | any +/** + * Whether to create recommended CloudWatch alarms + * + * @default - Alarms are created + */ +readonly createCloudWatchAlarms?: boolean +} + +export class EventsRuleToKinesisStreams extends Construct { + public readonly kinesisStream: kinesis.Stream; + public readonly eventsRule: events.Rule; + public readonly eventsRole: iam.Role; + public readonly cloudwatchAlarms?: cloudwatch.Alarm[]; + + /** + * @summary Constructs a new instance of the EventsRuleToKinesisStreams class. + * @param {cdk.App} scope - represents the scope for all the resources. + * @param {string} id - this is a a scope-unique id. + * @param {EventsRuleToKinesisStreamsProps} props - user provided props for the construct + * @since 0.8.0 + * @access public + */ + constructor(scope: Construct, id: string, props: EventsRuleToKinesisStreamsProps) { + super(scope, id); + + // Set up the Kinesis Stream + this.kinesisStream = defaults.buildKinesisStream(this, { + existingStreamObj: props.existingStreamObj, + kinesisStreamProps: props.kinesisStreamProps, + }); + + // Create an events service role + this.eventsRole = new iam.Role(this, 'eventsRole', { + assumedBy: new iam.ServicePrincipal('events.amazonaws.com'), + description: 'Events Rule Role', + }); + + // Grant permission to events service role to allow event rule to send events data to the kinesis stream + this.kinesisStream.grantWrite(this.eventsRole); + + // Set up the Kinesis Stream as the target for event rule + const kinesisStreamEventTarget: events.IRuleTarget = { + bind: () => ({ + id: '', + arn: this.kinesisStream.streamArn, + role: this.eventsRole + }) + }; + + // Set up the events rule props + const defaultEventsRuleProps = defaults.DefaultEventsRuleProps([kinesisStreamEventTarget]); + const eventsRuleProps = overrideProps(defaultEventsRuleProps, props.eventRuleProps, true); + + // Setup up the event rule + this.eventsRule = new events.Rule(this, 'EventsRule', eventsRuleProps); + + if (props.createCloudWatchAlarms === undefined || props.createCloudWatchAlarms) { + // Deploy best practices CW Alarms for Kinesis Stream + this.cloudwatchAlarms = defaults.buildKinesisStreamCWAlarms(this); + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/package.json b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/package.json new file mode 100644 index 000000000..ae8db1e5c --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/package.json @@ -0,0 +1,83 @@ +{ + "name": "@aws-solutions-constructs/aws-events-rule-kinesisstreams", + "version": "1.69.0", + "description": "CDK Constructs for deploying Amazon CloudWatch Events Rule that invokes Amazon Kinesis Data Stream", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "scripts": { + "build": "tsc -b .", + "lint": "eslint -c ../eslintrc.yml --ext=.js,.ts . && tslint --project .", + "lint-fix": "eslint -c ../eslintrc.yml --ext=.js,.ts --fix .", + "test": "jest --coverage", + "clean": "tsc -b --clean", + "watch": "tsc -b -w", + "integ": "cdk-integ", + "integ-no-clean": "cdk-integ --no-clean", + "integ-assert": "cdk-integ-assert", + "jsii": "jsii", + "jsii-pacmak": "jsii-pacmak", + "build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert", + "snapshot-update": "npm run jsii && npm test -- -u && npm run integ-assert" + }, + "repository": { + "type": "git", + "url": "https://github.com/awslabs/aws-solutions-constructs.git", + "directory": "source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams" + }, + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "jsii": { + "outdir": "dist", + "targets": { + "java": { + "package": "software.amazon.awsconstructs.services.eventsrulekinesisstreams", + "maven": { + "groupId": "software.amazon.awsconstructs", + "artifactId": "eventsrulekinesisstreams" + } + }, + "dotnet": { + "namespace": "Amazon.Constructs.AWS.EventsRuleKinesisStreams", + "packageId": "Amazon.Constructs.AWS.EventsRuleKinesisStreams", + "signAssembly": true, + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "python": { + "distName": "aws-solutions-constructs.aws-events-rule-kinesis-streams", + "module": "aws_solutions_constructs.aws_events_rule_kinesis_streams" + } + } + }, + "dependencies": { + "@aws-cdk/aws-events": "~1.69.0", + "@aws-cdk/aws-kinesis": "~1.69.0", + "@aws-cdk/aws-iam": "~1.69.0", + "@aws-cdk/aws-cloudwatch": "~1.69.0", + "@aws-cdk/core": "~1.69.0", + "@aws-solutions-constructs/core": "~1.69.0", + "constructs": "^3.0.4" + }, + "devDependencies": { + "@aws-cdk/assert": "~1.69.0", + "@types/jest": "^24.0.23", + "@types/node": "^10.3.0" + }, + "jest": { + "moduleFileExtensions": [ + "js" + ] + }, + "peerDependencies": { + "@aws-cdk/aws-events": "~1.69.0", + "@aws-cdk/aws-kinesis": "~1.69.0", + "@aws-cdk/aws-iam": "~1.69.0", + "@aws-cdk/aws-cloudwatch": "~1.69.0", + "@aws-cdk/core": "~1.69.0", + "@aws-solutions-constructs/core": "~1.69.0", + "constructs": "^3.0.4" + } +} diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/__snapshots__/events-rule-kinesisstreams.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/__snapshots__/events-rule-kinesisstreams.test.js.snap new file mode 100644 index 000000000..ddbf7d320 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/__snapshots__/events-rule-kinesisstreams.test.js.snap @@ -0,0 +1,232 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Test existing resources 1`] = ` +Object { + "Resources": Object { + "testeventsrulekinesisstreamexistingresourceEventsRule50C4E158": Object { + "Properties": Object { + "Description": "event rule props", + "ScheduleExpression": "rate(5 minutes)", + "State": "ENABLED", + "Targets": Array [ + Object { + "Arn": Object { + "Fn::GetAtt": Array [ + "testexistingstreamA6CF7AE7", + "Arn", + ], + }, + "Id": "Target0", + "RoleArn": Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisstreamexistingresourceeventsRole6431B874", + "Arn", + ], + }, + }, + ], + }, + "Type": "AWS::Events::Rule", + }, + "testeventsrulekinesisstreamexistingresourceKinesisStreamGetRecordsIteratorAgeAlarmCFA3A3CB": Object { + "Properties": Object { + "AlarmDescription": "Consumer Record Processing Falling Behind, there is risk for data loss due to record expiration.", + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "MetricName": "GetRecords.IteratorAgeMilliseconds", + "Namespace": "AWS/Kinesis", + "Period": 300, + "Statistic": "Maximum", + "Threshold": 2592000, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "testeventsrulekinesisstreamexistingresourceKinesisStreamReadProvisionedThroughputExceededAlarm2DAC9390": Object { + "Properties": Object { + "AlarmDescription": "Consumer Application is Reading at a Slower Rate Than Expected.", + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 1, + "MetricName": "ReadProvisionedThroughputExceeded", + "Namespace": "AWS/Kinesis", + "Period": 300, + "Statistic": "Average", + "Threshold": 0, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "testeventsrulekinesisstreamexistingresourceeventsRole6431B874": Object { + "Properties": Object { + "AssumeRolePolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": Object { + "Service": "events.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "Description": "Events Rule Role", + }, + "Type": "AWS::IAM::Role", + }, + "testeventsrulekinesisstreamexistingresourceeventsRoleDefaultPolicyDB76C6C1": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "kinesis:ListShards", + "kinesis:PutRecord", + "kinesis:PutRecords", + ], + "Effect": "Allow", + "Resource": Object { + "Fn::GetAtt": Array [ + "testexistingstreamA6CF7AE7", + "Arn", + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "testeventsrulekinesisstreamexistingresourceeventsRoleDefaultPolicyDB76C6C1", + "Roles": Array [ + Object { + "Ref": "testeventsrulekinesisstreamexistingresourceeventsRole6431B874", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "testexistingstreamA6CF7AE7": Object { + "Properties": Object { + "Name": "existing-stream", + "RetentionPeriodHours": 48, + "ShardCount": 5, + }, + "Type": "AWS::Kinesis::Stream", + }, + }, +} +`; + +exports[`Test snapshot match with default parameters 1`] = ` +Object { + "Resources": Object { + "testeventsrulekinesisstreamsdefaultparametersEventsRuleB892ACC8": Object { + "Properties": Object { + "Description": "event rule props", + "ScheduleExpression": "rate(5 minutes)", + "State": "ENABLED", + "Targets": Array [ + Object { + "Arn": Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisstreamsdefaultparametersKinesisStream289DD083", + "Arn", + ], + }, + "Id": "Target0", + "RoleArn": Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisstreamsdefaultparameterseventsRole3067A354", + "Arn", + ], + }, + }, + ], + }, + "Type": "AWS::Events::Rule", + }, + "testeventsrulekinesisstreamsdefaultparametersKinesisStream289DD083": Object { + "Properties": Object { + "RetentionPeriodHours": 24, + "ShardCount": 1, + "StreamEncryption": Object { + "EncryptionType": "KMS", + "KeyId": "alias/aws/kinesis", + }, + }, + "Type": "AWS::Kinesis::Stream", + }, + "testeventsrulekinesisstreamsdefaultparametersKinesisStreamGetRecordsIteratorAgeAlarmF78CCCCE": Object { + "Properties": Object { + "AlarmDescription": "Consumer Record Processing Falling Behind, there is risk for data loss due to record expiration.", + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "MetricName": "GetRecords.IteratorAgeMilliseconds", + "Namespace": "AWS/Kinesis", + "Period": 300, + "Statistic": "Maximum", + "Threshold": 2592000, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "testeventsrulekinesisstreamsdefaultparametersKinesisStreamReadProvisionedThroughputExceededAlarm07FC3EDE": Object { + "Properties": Object { + "AlarmDescription": "Consumer Application is Reading at a Slower Rate Than Expected.", + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 1, + "MetricName": "ReadProvisionedThroughputExceeded", + "Namespace": "AWS/Kinesis", + "Period": 300, + "Statistic": "Average", + "Threshold": 0, + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "testeventsrulekinesisstreamsdefaultparameterseventsRole3067A354": Object { + "Properties": Object { + "AssumeRolePolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": Object { + "Service": "events.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "Description": "Events Rule Role", + }, + "Type": "AWS::IAM::Role", + }, + "testeventsrulekinesisstreamsdefaultparameterseventsRoleDefaultPolicyF4C8EFFF": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "kinesis:ListShards", + "kinesis:PutRecord", + "kinesis:PutRecords", + ], + "Effect": "Allow", + "Resource": Object { + "Fn::GetAtt": Array [ + "testeventsrulekinesisstreamsdefaultparametersKinesisStream289DD083", + "Arn", + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "testeventsrulekinesisstreamsdefaultparameterseventsRoleDefaultPolicyF4C8EFFF", + "Roles": Array [ + Object { + "Ref": "testeventsrulekinesisstreamsdefaultparameterseventsRole3067A354", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + }, +} +`; diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/events-rule-kinesisstreams.test.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/events-rule-kinesisstreams.test.ts new file mode 100644 index 000000000..65d33628a --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/events-rule-kinesisstreams.test.ts @@ -0,0 +1,106 @@ +/** + * Copyright 2020 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 { SynthUtils } from '@aws-cdk/assert'; +import * as cdk from "@aws-cdk/core"; +import * as events from "@aws-cdk/aws-events"; +import * as kinesis from '@aws-cdk/aws-kinesis'; +import '@aws-cdk/assert/jest'; +import {EventsRuleToKinesisStreams, EventsRuleToKinesisStreamsProps} from '../lib'; + +// -------------------------------------------------------------- +// Test snapshot match with default parameters +// -------------------------------------------------------------- +function deployNewStack(stack: cdk.Stack) { + const props: EventsRuleToKinesisStreamsProps = { + eventRuleProps: { + description: 'event rule props', + schedule: events.Schedule.rate(cdk.Duration.minutes(5)) + } + }; + return new EventsRuleToKinesisStreams(stack, 'test-events-rule-kinesis-streams-default-parameters', props); +} + +test('Test snapshot match with default parameters', () => { + const stack = new cdk.Stack(); + deployNewStack(stack); + + // Assertions + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); +}); + +// -------------------------------------------------------------- +// Test properties +// -------------------------------------------------------------- +test('Test properties', () => { + const stack = new cdk.Stack(); + const construct: EventsRuleToKinesisStreams = deployNewStack(stack); + + // Assertions + expect(construct.eventsRule !== null); + expect(construct.kinesisStream !== null); + expect(construct.eventsRole !== null); +}); + +// -------------------------------------------------------------- +// Test default AWS managed encryption key property +// -------------------------------------------------------------- +test('Test default AWS managed encryption key property', () => { + const stack = new cdk.Stack(); + deployNewStack(stack); + + // Assertions + expect(stack).toHaveResource('AWS::Kinesis::Stream', { + StreamEncryption: { + EncryptionType: "KMS", + KeyId: "alias/aws/kinesis" + } + }); +}); + +// -------------------------------------------------------------- +// Test existing resources +// -------------------------------------------------------------- +test('Test existing resources', () => { + const stack = new cdk.Stack(); + + // create resource + const existingStream = new kinesis.Stream(stack, 'test-existing-stream', { + streamName: 'existing-stream', + shardCount: 5, + retentionPeriod: cdk.Duration.hours(48), + encryption: kinesis.StreamEncryption.UNENCRYPTED + }); + + new EventsRuleToKinesisStreams(stack, 'test-events-rule-kinesis-stream-existing-resource', { + existingStreamObj: existingStream, + // These properties will be ignored as existing object was provided + kinesisStreamProps: { + streamName: 'other-name-stream', + shardCount: 1, + retentionPeriod: cdk.Duration.hours(24) + }, + eventRuleProps: { + description: 'event rule props', + schedule: events.Schedule.rate(cdk.Duration.minutes(5)) + } + }); + + // Assertions + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + expect(stack).toHaveResource('AWS::Kinesis::Stream', { + Name: 'existing-stream', + ShardCount: 5, + RetentionPeriodHours: 48, + }); +}); \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-existing.expected.json b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-existing.expected.json new file mode 100644 index 000000000..218bff240 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-existing.expected.json @@ -0,0 +1,114 @@ +{ + "Description": "Integration Test for aws-events-rule-kinesisstreams with existing kinesis stream", + "Resources": { + "testeventsrulekinesisstreamexistingEventsRule07EC6002": { + "Properties": { + "ScheduleExpression": "rate(5 minutes)", + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "teststream04374A09", + "Arn" + ] + }, + "Id": "Target0", + "RoleArn": { + "Fn::GetAtt": [ + "testeventsrulekinesisstreamexistingeventsRole6BFA3F09", + "Arn" + ] + } + } + ] + }, + "Type": "AWS::Events::Rule" + }, + "testeventsrulekinesisstreamexistingKinesisStreamGetRecordsIteratorAgeAlarm6BBE2CE5": { + "Properties": { + "AlarmDescription": "Consumer Record Processing Falling Behind, there is risk for data loss due to record expiration.", + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "MetricName": "GetRecords.IteratorAgeMilliseconds", + "Namespace": "AWS/Kinesis", + "Period": 300, + "Statistic": "Maximum", + "Threshold": 2592000 + }, + "Type": "AWS::CloudWatch::Alarm" + }, + "testeventsrulekinesisstreamexistingKinesisStreamReadProvisionedThroughputExceededAlarmEFD57259": { + "Properties": { + "AlarmDescription": "Consumer Application is Reading at a Slower Rate Than Expected.", + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 1, + "MetricName": "ReadProvisionedThroughputExceeded", + "Namespace": "AWS/Kinesis", + "Period": 300, + "Statistic": "Average", + "Threshold": 0 + }, + "Type": "AWS::CloudWatch::Alarm" + }, + "testeventsrulekinesisstreamexistingeventsRole6BFA3F09": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Events Rule Role" + }, + "Type": "AWS::IAM::Role" + }, + "testeventsrulekinesisstreamexistingeventsRoleDefaultPolicy7B842A0C": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "kinesis:ListShards", + "kinesis:PutRecord", + "kinesis:PutRecords" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "teststream04374A09", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testeventsrulekinesisstreamexistingeventsRoleDefaultPolicy7B842A0C", + "Roles": [ + { + "Ref": "testeventsrulekinesisstreamexistingeventsRole6BFA3F09" + } + ] + }, + "Type": "AWS::IAM::Policy" + }, + "teststream04374A09": { + "Properties": { + "RetentionPeriodHours": 24, + "ShardCount": 2, + "StreamEncryption": { + "EncryptionType": "KMS", + "KeyId": "alias/aws/kinesis" + } + }, + "Type": "AWS::Kinesis::Stream" + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-existing.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-existing.ts new file mode 100644 index 000000000..6833bc388 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-existing.ts @@ -0,0 +1,37 @@ +/** + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import * as events from '@aws-cdk/aws-events'; +import * as kinesis from '@aws-cdk/aws-kinesis'; +import { App, Stack, Duration } from '@aws-cdk/core'; +import { EventsRuleToKinesisStreams, EventsRuleToKinesisStreamsProps } from '../lib'; + +const app = new App(); +const stack = new Stack(app, 'test-existing-rule-kinesisstream'); +stack.templateOptions.description = 'Integration Test for aws-events-rule-kinesisstreams with existing kinesis stream'; + +const stream = new kinesis.Stream(stack, 'test-stream', { + shardCount: 2, + encryption: kinesis.StreamEncryption.MANAGED +}); + +const props: EventsRuleToKinesisStreamsProps = { + eventRuleProps: { + schedule: events.Schedule.rate(Duration.minutes(5)) + }, + existingStreamObj: stream +}; + +new EventsRuleToKinesisStreams(stack, 'test-events-rule-kinesis-stream-existing', props); + +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-no-arguments.expected.json new file mode 100644 index 000000000..040404e62 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-no-arguments.expected.json @@ -0,0 +1,114 @@ +{ + "Description": "Integration Test for aws-events-rule-kinesisstreams", + "Resources": { + "testeventsrulekinesisstreamKinesisStream969B0ACC": { + "Type": "AWS::Kinesis::Stream", + "Properties": { + "ShardCount": 1, + "RetentionPeriodHours": 24, + "StreamEncryption": { + "EncryptionType": "KMS", + "KeyId": "alias/aws/kinesis" + } + } + }, + "testeventsrulekinesisstreameventsRole6E6DBF4D": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Events Rule Role" + } + }, + "testeventsrulekinesisstreameventsRoleDefaultPolicy1918648D": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "kinesis:ListShards", + "kinesis:PutRecord", + "kinesis:PutRecords" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "testeventsrulekinesisstreamKinesisStream969B0ACC", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testeventsrulekinesisstreameventsRoleDefaultPolicy1918648D", + "Roles": [ + { + "Ref": "testeventsrulekinesisstreameventsRole6E6DBF4D" + } + ] + } + }, + "testeventsrulekinesisstreamEventsRule4154831A": { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "rate(5 minutes)", + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "testeventsrulekinesisstreamKinesisStream969B0ACC", + "Arn" + ] + }, + "Id": "Target0", + "RoleArn": { + "Fn::GetAtt": [ + "testeventsrulekinesisstreameventsRole6E6DBF4D", + "Arn" + ] + } + } + ] + } + }, + "testeventsrulekinesisstreamKinesisStreamGetRecordsIteratorAgeAlarm61DF64E6": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "AlarmDescription": "Consumer Record Processing Falling Behind, there is risk for data loss due to record expiration.", + "MetricName": "GetRecords.IteratorAgeMilliseconds", + "Namespace": "AWS/Kinesis", + "Period": 300, + "Statistic": "Maximum", + "Threshold": 2592000 + } + }, + "testeventsrulekinesisstreamKinesisStreamReadProvisionedThroughputExceededAlarmCEDB1659": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 1, + "AlarmDescription": "Consumer Application is Reading at a Slower Rate Than Expected.", + "MetricName": "ReadProvisionedThroughputExceeded", + "Namespace": "AWS/Kinesis", + "Period": 300, + "Statistic": "Average", + "Threshold": 0 + } + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-no-arguments.ts new file mode 100644 index 000000000..9b3455f3c --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/integ.events-rule-kinesisstreams-no-arguments.ts @@ -0,0 +1,30 @@ +/** + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import * as events from '@aws-cdk/aws-events'; +import { App, Stack, Duration } from '@aws-cdk/core'; +import { EventsRuleToKinesisStreams, EventsRuleToKinesisStreamsProps } from '../lib'; + +const app = new App(); +const stack = new Stack(app, 'test-rule-kinesisstream'); +stack.templateOptions.description = 'Integration Test for aws-events-rule-kinesisstreams'; + +const props: EventsRuleToKinesisStreamsProps = { + eventRuleProps: { + schedule: events.Schedule.rate(Duration.minutes(5)) + } +}; + +new EventsRuleToKinesisStreams(stack, 'test-events-rule-kinesis-stream', props); + +app.synth(); \ No newline at end of file