From ccd1d9ce0e0225ae3d1389534e9ab3cf37253298 Mon Sep 17 00:00:00 2001 From: Daniel Matuki da Cunha Date: Tue, 25 Aug 2020 10:31:55 -0700 Subject: [PATCH 1/5] feat(aws-sns-sqs): New aws-sns-sqs pattern implementation closes #24 --- .../aws-sns-sqs/.eslintignore | 5 + .../aws-sns-sqs/.gitignore | 15 + .../aws-sns-sqs/.npmignore | 21 + .../aws-sns-sqs/README.md | 92 ++ .../aws-sns-sqs/architecture.png | Bin 0 -> 171543 bytes .../aws-sns-sqs/lib/index.ts | 158 ++ .../aws-sns-sqs/package.json | 85 ++ .../test/__snapshots__/sns-sqs.test.js.snap | 1272 +++++++++++++++++ .../test/integ.existing-kms-key.expected.json | 357 +++++ .../test/integ.existing-kms-key.ts | 37 + .../test/integ.no-arguments.expected.json | 357 +++++ .../aws-sns-sqs/test/integ.no-arguments.ts | 29 + .../integ.sns-managed-kms-key.expected.json | 355 +++++ .../test/integ.sns-managed-kms-key.ts | 40 + .../aws-sns-sqs/test/sns-sqs.test.ts | 214 +++ .../core/lib/sns-helper.ts | 4 +- .../core/lib/sqs-helper.ts | 34 +- .../__snapshots__/sqs-helper.test.js.snap | 280 ++++ .../core/test/sqs-helper.test.ts | 53 +- 19 files changed, 3400 insertions(+), 8 deletions(-) create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/.eslintignore create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/.gitignore create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/.npmignore create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/README.md create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/architecture.png create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/lib/index.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/__snapshots__/sns-sqs.test.js.snap create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.no-arguments.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.no-arguments.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/.eslintignore b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/.eslintignore new file mode 100644 index 000000000..0819e2e65 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/.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-sns-sqs/.gitignore b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/.gitignore new file mode 100644 index 000000000..6773cabd2 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/.gitignore @@ -0,0 +1,15 @@ +lib/*.js +test/*.js +*.js.map +*.d.ts +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/.npmignore b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/.npmignore new file mode 100644 index 000000000..f66791629 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/.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-sns-sqs/README.md b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/README.md new file mode 100644 index 000000000..1427adcc9 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/README.md @@ -0,0 +1,92 @@ +# aws-sns-sqs 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_sns_sqs`| +|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-sns-sqs`| +|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.snssqs`| + +This AWS Solutions Construct implements an Amazon SNS topic connected to an Amazon SQS queue. + +Here is a minimal deployable pattern definition: + +``` javascript +const { SnsToSqs } = require('@aws-solutions-constructs/aws-sns-sqs'); + +const props: SnsToSqsProps = {}; + +new SnsToSqs(stack, 'SnsToSqsPattern', props); + +``` + +## Initializer + +``` text +new SnsToSqs(scope: Construct, id: string, props: SnsToSqsProps); +``` + +_Parameters_ + +* scope [`Construct`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html) +* id `string` +* props [`SnsToSqsProps`](#pattern-construct-props) + +## Pattern Construct Props + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|existingTopicObj?|[`sns.Topic`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sns.Topic.html)|An optional, existing SNS topic to be used instead of the default topic. If an existing topic is provided, the `topicProps` property will be ignored.| +|topicProps?|[`sns.TopicProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sns.TopicProps.html)|Optional user provided properties to override the default properties for the SNS topic.| +|existingQueueObj?|[`sqs.Queue`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.Queue.html)|An optional, existing SQS queue to be used instead of the default queue. If an existing queue is provided, the `queueProps` property will be ignored.| +|queueProps?|[`sqs.QueueProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.QueueProps.html)|Optional user provided properties to override the default properties for the SQS queue.| +|deployDeadLetterQueue?|`boolean`|Whether to create a secondary queue to be used as a dead letter queue. Defaults to true.| +|deadLetterQueueProps?|[`sqs.QueueProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.QueueProps.html)|Optional user-provided props to override the default props for the dead letter SQS queue.| +|maxReceiveCount?|`number`|The number of times a message can be unsuccessfully dequeued before being moved to the dead letter queue. Defaults to 15.| +|enableEncryption?|`boolean`|Use a KMS Key, either managed by this CDK app, or imported. If importing an encryption key, it must be specified in the encryptionKey property for this construct.| +|encryptionKey?|[`kms.Key`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kms.Key.html)|An optional, imported encryption key to encrypt the SQS queue, and SNS Topic.| + +## Pattern Properties + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|snsTopic|[`sns.Topic`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sns.Topic.html)|Returns an instance of the SNS topic created by the pattern.| +|encryptionKey|[`kms.Key`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kms.Key.html)|Returns an instance of kms.Key used for the SQS queue, and SNS Topic.| +|sqsQueue|[`sqs.Queue`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.Queue.html)|Returns an instance of the SQS queue created by the pattern.| +|deadLetterQueue?|[`sqs.Queue`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.Queue.html)|Returns an instance of the dead-letter SQS queue created by the pattern.| + +## Default settings + +Out of the box implementation of the Construct without any override will set the following defaults: + +### Amazon SNS Topic +* Configure least privilege access permissions for SNS Topic +* Enable server-side encryption for SNS Topic using Customer managed KMS Key +* Enforce encryption of data in transit + +### Amazon SQS Queue +* Configure least privilege access permissions for SQS Queue +* Deploy SQS dead-letter queue for the source SQS Queue +* Enable server-side encryption for SQS Queue using Customer managed KMS Key +* Enforce encryption of data in transit + +## Architecture +![Architecture Diagram](architecture.png) + +*** +© Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/architecture.png b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..0d8e77d9aee6ffe1659b2118c449d7ee2dcd4871 GIT binary patch literal 171543 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 z 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

1q5soA8 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 literal 0 HcmV?d00001 diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/lib/index.ts new file mode 100644 index 000000000..26454aa82 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/lib/index.ts @@ -0,0 +1,158 @@ +/** + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import * as sqs from '@aws-cdk/aws-sqs'; +import * as sns from '@aws-cdk/aws-sns'; +import * as subscriptions from '@aws-cdk/aws-sns-subscriptions'; +import * as kms from '@aws-cdk/aws-kms'; +import * as iam from '@aws-cdk/aws-iam'; +import * as defaults from '@aws-solutions-constructs/core'; +import { Construct } from '@aws-cdk/core'; +import {buildEncryptionKey} from "@aws-solutions-constructs/core"; + +/** + * @summary The properties for the SnsToSqs class. + */ +export interface SnsToSqsProps { + /** + * Existing instance of SNS topic object, if this is set then topicProps is ignored. + * + * @default - Default props are used + */ + readonly existingTopicObj?: sns.Topic, + /** + * Optional user provided properties to override the default properties for the SNS topic. + * + * @default - Default properties are used. + */ + readonly topicProps?: sns.TopicProps, + /** + * Existing instance of SQS queue object, if this is set then queueProps is ignored. + * + * @default - Default props are used + */ + readonly existingQueueObj?: sqs.Queue, + /** + * Optional user provided properties + * + * @default - Default props are used + */ + readonly queueProps?: sqs.QueueProps, + /** + * Optional user provided properties for the dead letter queue + * + * @default - Default props are used + */ + readonly deadLetterQueueProps?: sqs.QueueProps, + /** + * Whether to deploy a secondary queue to be used as a dead letter queue. + * + * @default - true. + */ + readonly deployDeadLetterQueue?: boolean, + /** + * The number of times a message can be unsuccessfully dequeued before being moved to the dead-letter queue. + * + * @default - required field if deployDeadLetterQueue=true. + */ + readonly maxReceiveCount?: number + /** + * Use a KMS Key, either managed by this CDK app, or imported. If importing an encryption key, it must be specified in + * the encryptionKey property for this construct. + * + * @default - true (encryption enabled, managed by this CDK app). + */ + readonly enableEncryption?: boolean + /** + * An optional, imported encryption key to encrypt the SQS queue, and SNS Topic. + * + * @default - not specified. + */ + readonly encryptionKey?: kms.Key +} + +/** + * @summary The SnsToSqs class. + */ +export class SnsToSqs extends Construct { + public readonly snsTopic: sns.Topic; + public readonly encryptionKey?: kms.Key; + public readonly sqsQueue: sqs.Queue; + public readonly deadLetterQueue?: sqs.DeadLetterQueue; + + /** + * @summary Constructs a new instance of the SnsToSqs class. + * @param {cdk.App} scope - represents the scope for all the resources. + * @param {string} id - this is a a scope-unique id. + * @param {SnsToSqsProps} props - user provided props for the construct. + * @since 0.8.0 + * @access public + */ + constructor(scope: Construct, id: string, props: SnsToSqsProps) { + super(scope, id); + + // Setup the dead letter queue, if applicable + if (props.deployDeadLetterQueue || props.deployDeadLetterQueue === undefined) { + const dlq: sqs.Queue = defaults.buildQueue(this, 'deadLetterQueue', { + queueProps: props.deadLetterQueueProps + }); + this.deadLetterQueue = defaults.buildDeadLetterQueue({ + deadLetterQueue: dlq, + maxReceiveCount: props.maxReceiveCount + }); + } + + let enableEncryptionParam = props.enableEncryption; + let encryptionKeyParam = props.encryptionKey; + // Create the encryptionKey if none was provided + if (props.enableEncryption !== false) { + enableEncryptionParam = true; + if (!props.encryptionKey) { + encryptionKeyParam = buildEncryptionKey(scope); + } + } + // Setup the SNS topic + if (!props.existingTopicObj) { + // If an existingTopicObj was not specified create new topic + [this.snsTopic, this.encryptionKey] = defaults.buildTopic(this, { + topicProps: props.topicProps, + enableEncryption: enableEncryptionParam, + encryptionKey: encryptionKeyParam + }); + } else { + // If an existingTopicObj was specified utilize the provided topic + this.snsTopic = props.existingTopicObj; + } + + // Setup the queue + this.sqsQueue = defaults.buildQueue(this, 'queue', { + existingQueueObj: props.existingQueueObj, + queueProps: props.queueProps, + deadLetterQueue: this.deadLetterQueue, + enableEncryption: enableEncryptionParam, + encryptionKey: encryptionKeyParam + }); + + // Setup the SQS queue subscription to the SNS topic + this.snsTopic.addSubscription(new subscriptions.SqsSubscription(this.sqsQueue)); + + // Grant SNS service access to the SQS queue encryption key + if (this.sqsQueue.encryptionMasterKey) { + this.sqsQueue.encryptionMasterKey.grant(new iam.ServicePrincipal("sns.amazonaws.com"), + 'kms:Decrypt', + 'kms:GenerateDataKey*', + ); + } + } +} diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json new file mode 100644 index 000000000..70de5c009 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json @@ -0,0 +1,85 @@ +{ + "name": "@aws-solutions-constructs/aws-sns-sqs", + "version": "1.57.0", + "description": "CDK constructs for defining an interaction between an Amazon SNS topic and an Amazon SQS queue.", + "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-sns-sqs" + }, + "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.snssqs", + "maven": { + "groupId": "software.amazon.awsconstructs", + "artifactId": "snssqs" + } + }, + "dotnet": { + "namespace": "Amazon.Constructs.AWS.SnsSqs", + "packageId": "Amazon.Constructs.AWS.SnsSqs", + "signAssembly": true, + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "python": { + "distName": "aws-solutions-constructs.aws-sns-sqs", + "module": "aws_solutions_constructs.aws_sns_sqs" + } + } + }, + "dependencies": { + "@aws-cdk/aws-iam": "~1.57.0", + "@aws-cdk/aws-sns": "~1.57.0", + "@aws-cdk/aws-sqs": "~1.57.0", + "@aws-cdk/aws-sns-subscriptions": "~1.57.0", + "@aws-cdk/aws-kms": "~1.57.0", + "@aws-cdk/core": "~1.57.0", + "@aws-solutions-constructs/core": "~1.57.0", + "constructs": "^3.0.2" + }, + "devDependencies": { + "@aws-cdk/assert": "~1.57.0", + "@types/jest": "^24.0.23", + "@types/node": "^10.3.0" + }, + "jest": { + "moduleFileExtensions": [ + "js" + ] + }, + "peerDependencies": { + "@aws-cdk/aws-iam": "~1.57.0", + "@aws-cdk/aws-sns": "~1.57.0", + "@aws-cdk/aws-sqs": "~1.57.0", + "@aws-cdk/aws-sns-subscriptions": "~1.57.0", + "@aws-cdk/aws-kms": "~1.57.0", + "@aws-cdk/core": "~1.57.0", + "@aws-solutions-constructs/core": "~1.57.0", + "constructs": "^3.0.2" + } +} diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/__snapshots__/sns-sqs.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/__snapshots__/sns-sqs.test.js.snap new file mode 100644 index 000000000..cf07f6261 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/__snapshots__/sns-sqs.test.js.snap @@ -0,0 +1,1272 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Pattern deployment w/ new Topic, new Queue and default props 1`] = ` +Object { + "Resources": Object { + "EncryptionKey1B843E66": Object { + "DeletionPolicy": "Retain", + "Properties": Object { + "EnableKeyRotation": true, + "KeyPolicy": Object { + "Statement": Array [ + Object { + "Action": Array [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": "*", + }, + Object { + "Action": Array [ + "kms:Decrypt", + "kms:GenerateDataKey*", + ], + "Effect": "Allow", + "Principal": Object { + "Service": "sns.amazonaws.com", + }, + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::KMS::Key", + "UpdateReplacePolicy": "Retain", + }, + "testsnssqsSnsTopic2CD0065B": Object { + "Properties": Object { + "KmsMasterKeyId": Object { + "Ref": "EncryptionKey1B843E66", + }, + }, + "Type": "AWS::SNS::Topic", + }, + "testsnssqsSnsTopicPolicy604079F2": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe", + ], + "Condition": Object { + "StringEquals": Object { + "AWS:SourceOwner": Object { + "Ref": "AWS::AccountId", + }, + }, + }, + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Ref": "testsnssqsSnsTopic2CD0065B", + }, + "Sid": "TopicOwnerOnlyAccess", + }, + Object { + "Action": Array [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe", + ], + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Ref": "testsnssqsSnsTopic2CD0065B", + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + "Topics": Array [ + Object { + "Ref": "testsnssqsSnsTopic2CD0065B", + }, + ], + }, + "Type": "AWS::SNS::TopicPolicy", + }, + "testsnssqsdeadLetterQueue8DACC0A1": Object { + "Properties": Object { + "KmsMasterKeyId": "alias/aws/sqs", + }, + "Type": "AWS::SQS::Queue", + }, + "testsnssqsdeadLetterQueuePolicyAB8A9883": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn", + ], + }, + "Sid": "QueueOwnerOnlyAccess", + }, + Object { + "Action": "SQS:*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::GetAtt": Array [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn", + ], + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + "Queues": Array [ + Object { + "Ref": "testsnssqsdeadLetterQueue8DACC0A1", + }, + ], + }, + "Type": "AWS::SQS::QueuePolicy", + }, + "testsnssqsqueueB02504BF": Object { + "Properties": Object { + "KmsMasterKeyId": Object { + "Fn::GetAtt": Array [ + "EncryptionKey1B843E66", + "Arn", + ], + }, + "RedrivePolicy": Object { + "deadLetterTargetArn": Object { + "Fn::GetAtt": Array [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn", + ], + }, + "maxReceiveCount": 15, + }, + }, + "Type": "AWS::SQS::Queue", + }, + "testsnssqsqueuePolicyE64464B6": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "testsnssqsqueueB02504BF", + "Arn", + ], + }, + "Sid": "QueueOwnerOnlyAccess", + }, + Object { + "Action": "SQS:*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::GetAtt": Array [ + "testsnssqsqueueB02504BF", + "Arn", + ], + }, + "Sid": "HttpsOnly", + }, + Object { + "Action": "sqs:SendMessage", + "Condition": Object { + "ArnEquals": Object { + "aws:SourceArn": Object { + "Ref": "testsnssqsSnsTopic2CD0065B", + }, + }, + }, + "Effect": "Allow", + "Principal": Object { + "Service": "sns.amazonaws.com", + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "testsnssqsqueueB02504BF", + "Arn", + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "Queues": Array [ + Object { + "Ref": "testsnssqsqueueB02504BF", + }, + ], + }, + "Type": "AWS::SQS::QueuePolicy", + }, + "testsnssqsqueuetestsnssqsSnsTopicE16CFFE0983CE231": Object { + "Properties": Object { + "Endpoint": Object { + "Fn::GetAtt": Array [ + "testsnssqsqueueB02504BF", + "Arn", + ], + }, + "Protocol": "sqs", + "TopicArn": Object { + "Ref": "testsnssqsSnsTopic2CD0065B", + }, + }, + "Type": "AWS::SNS::Subscription", + }, + }, +} +`; + +exports[`Test deployment w/ existing queue, and topic 1`] = ` +Object { + "Resources": Object { + "EncryptionKey1B843E66": Object { + "DeletionPolicy": "Retain", + "Properties": Object { + "EnableKeyRotation": true, + "KeyPolicy": Object { + "Statement": Array [ + Object { + "Action": Array [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::KMS::Key", + "UpdateReplacePolicy": "Retain", + }, + "existingqueueobjF8AF0ED1": Object { + "Properties": Object { + "QueueName": "existing-queue-obj", + }, + "Type": "AWS::SQS::Queue", + }, + "existingqueueobjPolicy847305AE": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "sqs:SendMessage", + "Condition": Object { + "ArnEquals": Object { + "aws:SourceArn": Object { + "Ref": "existingtopicobjF4A24735", + }, + }, + }, + "Effect": "Allow", + "Principal": Object { + "Service": "sns.amazonaws.com", + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "existingqueueobjF8AF0ED1", + "Arn", + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "Queues": Array [ + Object { + "Ref": "existingqueueobjF8AF0ED1", + }, + ], + }, + "Type": "AWS::SQS::QueuePolicy", + }, + "existingqueueobjexistingtopicobjF03E40E2": Object { + "Properties": Object { + "Endpoint": Object { + "Fn::GetAtt": Array [ + "existingqueueobjF8AF0ED1", + "Arn", + ], + }, + "Protocol": "sqs", + "TopicArn": Object { + "Ref": "existingtopicobjF4A24735", + }, + }, + "Type": "AWS::SNS::Subscription", + }, + "existingtopicobjF4A24735": Object { + "Properties": Object { + "TopicName": "existing-topic-obj", + }, + "Type": "AWS::SNS::Topic", + }, + "snstosqsstackdeadLetterQueueA02EB1B1": Object { + "Properties": Object { + "KmsMasterKeyId": "alias/aws/sqs", + }, + "Type": "AWS::SQS::Queue", + }, + "snstosqsstackdeadLetterQueuePolicy4E639DF8": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackdeadLetterQueueA02EB1B1", + "Arn", + ], + }, + "Sid": "QueueOwnerOnlyAccess", + }, + Object { + "Action": "SQS:*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackdeadLetterQueueA02EB1B1", + "Arn", + ], + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + "Queues": Array [ + Object { + "Ref": "snstosqsstackdeadLetterQueueA02EB1B1", + }, + ], + }, + "Type": "AWS::SQS::QueuePolicy", + }, + }, +} +`; + +exports[`Test deployment with SNS managed KMS key 1`] = ` +Object { + "Resources": Object { + "snstosqsstackSnsTopicB387685B": Object { + "Properties": Object { + "KmsMasterKeyId": "alias/aws/sns", + }, + "Type": "AWS::SNS::Topic", + }, + "snstosqsstackSnsTopicPolicy824AEFAD": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe", + ], + "Condition": Object { + "StringEquals": Object { + "AWS:SourceOwner": Object { + "Ref": "AWS::AccountId", + }, + }, + }, + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Ref": "snstosqsstackSnsTopicB387685B", + }, + "Sid": "TopicOwnerOnlyAccess", + }, + Object { + "Action": Array [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe", + ], + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Ref": "snstosqsstackSnsTopicB387685B", + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + "Topics": Array [ + Object { + "Ref": "snstosqsstackSnsTopicB387685B", + }, + ], + }, + "Type": "AWS::SNS::TopicPolicy", + }, + "snstosqsstackdeadLetterQueueA02EB1B1": Object { + "Properties": Object { + "KmsMasterKeyId": "alias/aws/sqs", + }, + "Type": "AWS::SQS::Queue", + }, + "snstosqsstackdeadLetterQueuePolicy4E639DF8": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackdeadLetterQueueA02EB1B1", + "Arn", + ], + }, + "Sid": "QueueOwnerOnlyAccess", + }, + Object { + "Action": "SQS:*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackdeadLetterQueueA02EB1B1", + "Arn", + ], + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + "Queues": Array [ + Object { + "Ref": "snstosqsstackdeadLetterQueueA02EB1B1", + }, + ], + }, + "Type": "AWS::SQS::QueuePolicy", + }, + "snstosqsstackqueue262BCE03": Object { + "Properties": Object { + "KmsMasterKeyId": Object { + "Fn::GetAtt": Array [ + "snstosqsstackqueueKey743636E7", + "Arn", + ], + }, + "RedrivePolicy": Object { + "deadLetterTargetArn": Object { + "Fn::GetAtt": Array [ + "snstosqsstackdeadLetterQueueA02EB1B1", + "Arn", + ], + }, + "maxReceiveCount": 15, + }, + }, + "Type": "AWS::SQS::Queue", + }, + "snstosqsstackqueueKey743636E7": Object { + "DeletionPolicy": "Retain", + "Properties": Object { + "Description": "Created by Default/sns-to-sqs-stack/queue", + "KeyPolicy": Object { + "Statement": Array [ + Object { + "Action": Array [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": "*", + }, + Object { + "Action": Array [ + "kms:Decrypt", + "kms:GenerateDataKey*", + ], + "Effect": "Allow", + "Principal": Object { + "Service": "sns.amazonaws.com", + }, + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::KMS::Key", + "UpdateReplacePolicy": "Retain", + }, + "snstosqsstackqueuePolicy4A9E8A77": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackqueue262BCE03", + "Arn", + ], + }, + "Sid": "QueueOwnerOnlyAccess", + }, + Object { + "Action": "SQS:*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackqueue262BCE03", + "Arn", + ], + }, + "Sid": "HttpsOnly", + }, + Object { + "Action": "sqs:SendMessage", + "Condition": Object { + "ArnEquals": Object { + "aws:SourceArn": Object { + "Ref": "snstosqsstackSnsTopicB387685B", + }, + }, + }, + "Effect": "Allow", + "Principal": Object { + "Service": "sns.amazonaws.com", + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackqueue262BCE03", + "Arn", + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "Queues": Array [ + Object { + "Ref": "snstosqsstackqueue262BCE03", + }, + ], + }, + "Type": "AWS::SQS::QueuePolicy", + }, + "snstosqsstackqueuesnstosqsstackSnsTopic1DC3C73AEA256098": Object { + "Properties": Object { + "Endpoint": Object { + "Fn::GetAtt": Array [ + "snstosqsstackqueue262BCE03", + "Arn", + ], + }, + "Protocol": "sqs", + "TopicArn": Object { + "Ref": "snstosqsstackSnsTopicB387685B", + }, + }, + "Type": "AWS::SNS::Subscription", + }, + }, +} +`; + +exports[`Test deployment with imported encryption key 1`] = ` +Object { + "Resources": Object { + "importedkey38675D68": Object { + "DeletionPolicy": "Retain", + "Properties": Object { + "EnableKeyRotation": false, + "KeyPolicy": Object { + "Statement": Array [ + Object { + "Action": Array [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": "*", + }, + Object { + "Action": Array [ + "kms:Decrypt", + "kms:GenerateDataKey*", + ], + "Effect": "Allow", + "Principal": Object { + "Service": "sns.amazonaws.com", + }, + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::KMS::Key", + "UpdateReplacePolicy": "Retain", + }, + "snstosqsstackSnsTopicB387685B": Object { + "Properties": Object { + "KmsMasterKeyId": Object { + "Ref": "importedkey38675D68", + }, + }, + "Type": "AWS::SNS::Topic", + }, + "snstosqsstackSnsTopicPolicy824AEFAD": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe", + ], + "Condition": Object { + "StringEquals": Object { + "AWS:SourceOwner": Object { + "Ref": "AWS::AccountId", + }, + }, + }, + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Ref": "snstosqsstackSnsTopicB387685B", + }, + "Sid": "TopicOwnerOnlyAccess", + }, + Object { + "Action": Array [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe", + ], + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Ref": "snstosqsstackSnsTopicB387685B", + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + "Topics": Array [ + Object { + "Ref": "snstosqsstackSnsTopicB387685B", + }, + ], + }, + "Type": "AWS::SNS::TopicPolicy", + }, + "snstosqsstackdeadLetterQueueA02EB1B1": Object { + "Properties": Object { + "KmsMasterKeyId": "alias/aws/sqs", + }, + "Type": "AWS::SQS::Queue", + }, + "snstosqsstackdeadLetterQueuePolicy4E639DF8": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackdeadLetterQueueA02EB1B1", + "Arn", + ], + }, + "Sid": "QueueOwnerOnlyAccess", + }, + Object { + "Action": "SQS:*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackdeadLetterQueueA02EB1B1", + "Arn", + ], + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + "Queues": Array [ + Object { + "Ref": "snstosqsstackdeadLetterQueueA02EB1B1", + }, + ], + }, + "Type": "AWS::SQS::QueuePolicy", + }, + "snstosqsstackqueue262BCE03": Object { + "Properties": Object { + "KmsMasterKeyId": Object { + "Fn::GetAtt": Array [ + "importedkey38675D68", + "Arn", + ], + }, + "RedrivePolicy": Object { + "deadLetterTargetArn": Object { + "Fn::GetAtt": Array [ + "snstosqsstackdeadLetterQueueA02EB1B1", + "Arn", + ], + }, + "maxReceiveCount": 15, + }, + }, + "Type": "AWS::SQS::Queue", + }, + "snstosqsstackqueuePolicy4A9E8A77": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackqueue262BCE03", + "Arn", + ], + }, + "Sid": "QueueOwnerOnlyAccess", + }, + Object { + "Action": "SQS:*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackqueue262BCE03", + "Arn", + ], + }, + "Sid": "HttpsOnly", + }, + Object { + "Action": "sqs:SendMessage", + "Condition": Object { + "ArnEquals": Object { + "aws:SourceArn": Object { + "Ref": "snstosqsstackSnsTopicB387685B", + }, + }, + }, + "Effect": "Allow", + "Principal": Object { + "Service": "sns.amazonaws.com", + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "snstosqsstackqueue262BCE03", + "Arn", + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "Queues": Array [ + Object { + "Ref": "snstosqsstackqueue262BCE03", + }, + ], + }, + "Type": "AWS::SQS::QueuePolicy", + }, + "snstosqsstackqueuesnstosqsstackSnsTopic1DC3C73AEA256098": Object { + "Properties": Object { + "Endpoint": Object { + "Fn::GetAtt": Array [ + "snstosqsstackqueue262BCE03", + "Arn", + ], + }, + "Protocol": "sqs", + "TopicArn": Object { + "Ref": "snstosqsstackSnsTopicB387685B", + }, + }, + "Type": "AWS::SNS::Subscription", + }, + }, +} +`; diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.expected.json b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.expected.json new file mode 100644 index 000000000..e4ef2a160 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.expected.json @@ -0,0 +1,357 @@ +{ + "Description": "Integration Test for aws-sns-sqs with existing KMS key", + "Resources": { + "ImportedEncryptionKeyBE10B2FC": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "EnableKeyRotation": true + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testsnssqsdeadLetterQueue8DACC0A1": { + "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": "alias/aws/sqs" + } + }, + "testsnssqsdeadLetterQueuePolicyAB8A9883": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Fn::GetAtt": [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn" + ] + }, + "Sid": "QueueOwnerOnlyAccess" + }, + { + "Action": "SQS:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Fn::GetAtt": [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn" + ] + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "testsnssqsdeadLetterQueue8DACC0A1" + } + ] + } + }, + "testsnssqsSnsTopic2CD0065B": { + "Type": "AWS::SNS::Topic", + "Properties": { + "KmsMasterKeyId": { + "Ref": "ImportedEncryptionKeyBE10B2FC" + } + } + }, + "testsnssqsSnsTopicPolicy604079F2": { + "Type": "AWS::SNS::TopicPolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe" + ], + "Condition": { + "StringEquals": { + "AWS:SourceOwner": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Ref": "testsnssqsSnsTopic2CD0065B" + }, + "Sid": "TopicOwnerOnlyAccess" + }, + { + "Action": [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe" + ], + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Ref": "testsnssqsSnsTopic2CD0065B" + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + }, + "Topics": [ + { + "Ref": "testsnssqsSnsTopic2CD0065B" + } + ] + } + }, + "testsnssqsqueueB02504BF": { + "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": { + "Fn::GetAtt": [ + "ImportedEncryptionKeyBE10B2FC", + "Arn" + ] + }, + "RedrivePolicy": { + "deadLetterTargetArn": { + "Fn::GetAtt": [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn" + ] + }, + "maxReceiveCount": 15 + } + } + }, + "testsnssqsqueuePolicyE64464B6": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + }, + "Sid": "QueueOwnerOnlyAccess" + }, + { + "Action": "SQS:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + }, + "Sid": "HttpsOnly" + }, + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "testsnssqsSnsTopic2CD0065B" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "testsnssqsqueueB02504BF" + } + ] + } + }, + "testsnssqsqueuetestsnssqsSnsTopic752C989B046CB7AE": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Ref": "testsnssqsSnsTopic2CD0065B" + }, + "Endpoint": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + } + } + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.ts new file mode 100644 index 000000000..143586de9 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.ts @@ -0,0 +1,37 @@ +/** + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from "@aws-cdk/core"; +import { SnsToSqs, SnsToSqsProps } from "../lib"; +import { KeyProps } from '@aws-cdk/aws-kms'; +import * as kms from '@aws-cdk/aws-kms'; + +// Setup +const app = new App(); +const stack = new Stack(app, 'test-sns-sqs'); +stack.templateOptions.description = 'Integration Test for aws-sns-sqs with existing KMS key'; + +// Definitions +const encryptionKeyProps: KeyProps = { + enableKeyRotation: true +}; +let key = new kms.Key(stack, 'ImportedEncryptionKey', encryptionKeyProps); +const props: SnsToSqsProps = { + enableEncryption: true, + encryptionKey: key +}; +new SnsToSqs(stack, 'test-sns-sqs', props); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.no-arguments.expected.json new file mode 100644 index 000000000..72400a6d2 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.no-arguments.expected.json @@ -0,0 +1,357 @@ +{ + "Description": "Integration Test for aws-sns-sqs", + "Resources": { + "testsnssqsdeadLetterQueue8DACC0A1": { + "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": "alias/aws/sqs" + } + }, + "testsnssqsdeadLetterQueuePolicyAB8A9883": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Fn::GetAtt": [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn" + ] + }, + "Sid": "QueueOwnerOnlyAccess" + }, + { + "Action": "SQS:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Fn::GetAtt": [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn" + ] + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "testsnssqsdeadLetterQueue8DACC0A1" + } + ] + } + }, + "testsnssqsSnsTopic2CD0065B": { + "Type": "AWS::SNS::Topic", + "Properties": { + "KmsMasterKeyId": { + "Ref": "EncryptionKey1B843E66" + } + } + }, + "testsnssqsSnsTopicPolicy604079F2": { + "Type": "AWS::SNS::TopicPolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe" + ], + "Condition": { + "StringEquals": { + "AWS:SourceOwner": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Ref": "testsnssqsSnsTopic2CD0065B" + }, + "Sid": "TopicOwnerOnlyAccess" + }, + { + "Action": [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe" + ], + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Ref": "testsnssqsSnsTopic2CD0065B" + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + }, + "Topics": [ + { + "Ref": "testsnssqsSnsTopic2CD0065B" + } + ] + } + }, + "testsnssqsqueueB02504BF": { + "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": { + "Fn::GetAtt": [ + "EncryptionKey1B843E66", + "Arn" + ] + }, + "RedrivePolicy": { + "deadLetterTargetArn": { + "Fn::GetAtt": [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn" + ] + }, + "maxReceiveCount": 15 + } + } + }, + "testsnssqsqueuePolicyE64464B6": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + }, + "Sid": "QueueOwnerOnlyAccess" + }, + { + "Action": "SQS:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + }, + "Sid": "HttpsOnly" + }, + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "testsnssqsSnsTopic2CD0065B" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "testsnssqsqueueB02504BF" + } + ] + } + }, + "testsnssqsqueuetestsnssqsSnsTopic752C989B046CB7AE": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Ref": "testsnssqsSnsTopic2CD0065B" + }, + "Endpoint": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + } + } + }, + "EncryptionKey1B843E66": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "EnableKeyRotation": true + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.no-arguments.ts new file mode 100644 index 000000000..b34018dfa --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.no-arguments.ts @@ -0,0 +1,29 @@ +/** + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from "@aws-cdk/core"; +import { SnsToSqs, SnsToSqsProps } from "../lib"; + +// Setup +const app = new App(); +const stack = new Stack(app, 'test-sns-sqs'); +stack.templateOptions.description = 'Integration Test for aws-sns-sqs'; + +// Definitions +const props: SnsToSqsProps = {}; + +new SnsToSqs(stack, 'test-sns-sqs', props); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.expected.json b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.expected.json new file mode 100644 index 000000000..365d91b15 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.expected.json @@ -0,0 +1,355 @@ +{ + "Description": "Integration Test for aws-sns-sqs with SNS managed KMS key", + "Resources": { + "testsnssqsdeadLetterQueue8DACC0A1": { + "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": "alias/aws/sqs" + } + }, + "testsnssqsdeadLetterQueuePolicyAB8A9883": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Fn::GetAtt": [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn" + ] + }, + "Sid": "QueueOwnerOnlyAccess" + }, + { + "Action": "SQS:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Fn::GetAtt": [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn" + ] + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "testsnssqsdeadLetterQueue8DACC0A1" + } + ] + } + }, + "testsnssqsSnsTopic2CD0065B": { + "Type": "AWS::SNS::Topic", + "Properties": { + "KmsMasterKeyId": "alias/aws/sns" + } + }, + "testsnssqsSnsTopicPolicy604079F2": { + "Type": "AWS::SNS::TopicPolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe" + ], + "Condition": { + "StringEquals": { + "AWS:SourceOwner": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Ref": "testsnssqsSnsTopic2CD0065B" + }, + "Sid": "TopicOwnerOnlyAccess" + }, + { + "Action": [ + "SNS:Publish", + "SNS:RemovePermission", + "SNS:SetTopicAttributes", + "SNS:DeleteTopic", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:Receive", + "SNS:AddPermission", + "SNS:Subscribe" + ], + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Ref": "testsnssqsSnsTopic2CD0065B" + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + }, + "Topics": [ + { + "Ref": "testsnssqsSnsTopic2CD0065B" + } + ] + } + }, + "testsnssqsqueueKey31B0457A": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "Description": "Created by test-sns-sqs/test-sns-sqs/queue" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testsnssqsqueueB02504BF": { + "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": { + "Fn::GetAtt": [ + "testsnssqsqueueKey31B0457A", + "Arn" + ] + }, + "RedrivePolicy": { + "deadLetterTargetArn": { + "Fn::GetAtt": [ + "testsnssqsdeadLetterQueue8DACC0A1", + "Arn" + ] + }, + "maxReceiveCount": 15 + } + } + }, + "testsnssqsqueuePolicyE64464B6": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + }, + "Sid": "QueueOwnerOnlyAccess" + }, + { + "Action": "SQS:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + }, + "Sid": "HttpsOnly" + }, + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "testsnssqsSnsTopic2CD0065B" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "testsnssqsqueueB02504BF" + } + ] + } + }, + "testsnssqsqueuetestsnssqsSnsTopic752C989B046CB7AE": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Ref": "testsnssqsSnsTopic2CD0065B" + }, + "Endpoint": { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + } + } + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts new file mode 100644 index 000000000..df28958b7 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts @@ -0,0 +1,40 @@ +/** + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from "@aws-cdk/core"; +import { SnsToSqs, SnsToSqsProps } from "../lib"; +import * as kms from '@aws-cdk/aws-kms'; +import * as sqs from '@aws-cdk/aws-sqs'; + +// Setup +const app = new App(); +const stack = new Stack(app, 'test-sns-sqs'); +stack.templateOptions.description = 'Integration Test for aws-sns-sqs with SNS managed KMS key'; + +// Definitions +const snsManagedKey = kms.Alias.fromAliasName(stack, 'sns-managed-key', 'alias/aws/sns'); +const props: SnsToSqsProps = { + topicProps: { + masterKey: snsManagedKey + }, + queueProps: { + encryption: sqs.QueueEncryption.KMS + }, + enableEncryption: false +}; + +new SnsToSqs(stack, 'test-sns-sqs', props); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts new file mode 100644 index 000000000..c2528acdc --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts @@ -0,0 +1,214 @@ +/** + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { Stack } from "@aws-cdk/core"; +import { SnsToSqs, SnsToSqsProps } from "../lib"; +import * as sqs from '@aws-cdk/aws-sqs'; +import * as sns from '@aws-cdk/aws-sns'; +import * as kms from '@aws-cdk/aws-kms'; +import { SynthUtils } from '@aws-cdk/assert'; +import '@aws-cdk/assert/jest'; + +// -------------------------------------------------------------- +// Pattern deployment w/ new Lambda function and +// default properties +// -------------------------------------------------------------- +test('Pattern deployment w/ new Topic, new Queue and default props', () => { + // Initial Setup + const stack = new Stack(); + const props: SnsToSqsProps = {}; + new SnsToSqs(stack, 'test-sns-sqs', props); + // Assertion 1 + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + // Assertion 2 + expect(stack).toHaveResource("AWS::SNS::Topic", { + KmsMasterKeyId: { + "Ref": "EncryptionKey1B843E66" + } + }); + // Assertion 3 + expect(stack).toHaveResource("AWS::SQS::Queue", { + KmsMasterKeyId: { + "Fn::GetAtt": [ + "EncryptionKey1B843E66", + "Arn" + ] + } + }); + // Assertion 4 + expect(stack).toHaveResource("AWS::SNS::Subscription", { + Protocol: "sqs", + TopicArn: { + "Ref": "testsnssqsSnsTopic2CD0065B" + }, + Endpoint: { + "Fn::GetAtt": [ + "testsnssqsqueueB02504BF", + "Arn" + ] + } + }); +}); + +// -------------------------------------------------------------- +// Pattern deployment w/ new Topic, new Queue, and +// overridden properties +// -------------------------------------------------------------- +test('Pattern deployment w/ new topic, new queue, and overridden props', () => { + // Initial Setup + const stack = new Stack(); + const props: SnsToSqsProps = { + topicProps: { + topicName: "new-topic", + }, + queueProps: { + queueName: "new-queue.fifo", + fifo: true + }, + enableEncryption: true, + deployDeadLetterQueue: false, + maxReceiveCount: 0 + }; + new SnsToSqs(stack, 'test-sns-sqs', props); + // Assertion 1 + expect(stack).toHaveResource("AWS::SNS::Topic", { + TopicName: "new-topic", + KmsMasterKeyId: { + "Ref": "EncryptionKey1B843E66" + } + }); + // Assertion 2 + expect(stack).toHaveResource("AWS::SQS::Queue", { + QueueName: "new-queue.fifo", + FifoQueue: true + }); +}); + +// -------------------------------------------------------------- +// Test the getter methods +// -------------------------------------------------------------- +test('Test getter methods', () => { + // Initial Setup + const stack = new Stack(); + const props: SnsToSqsProps = { + topicProps: {}, + enableEncryption: true, + deployDeadLetterQueue: true, + maxReceiveCount: 0, + queueProps: {} + }; + const app = new SnsToSqs(stack, 'test-sns-sqs', props); + // Assertion 1 + expect(app.snsTopic !== null); + // Assertion 2 + expect(app.encryptionKey !== null); + // Assertion 3 + expect(app.sqsQueue !== null); + // Assertion 4 + expect(app.deadLetterQueue !== null); +}); + +// -------------------------------------------------------------- +// Test deployment w/ existing queue, and topic +// -------------------------------------------------------------- +test('Test deployment w/ existing queue, and topic', () => { + // Stack + const stack = new Stack(); + // Helper declaration + const topic = new sns.Topic(stack, "existing-topic-obj", { + topicName: 'existing-topic-obj' + }); + const queue = new sqs.Queue(stack, 'existing-queue-obj', { + queueName: 'existing-queue-obj' + }); + const app = new SnsToSqs(stack, 'sns-to-sqs-stack', { + existingTopicObj: topic, + existingQueueObj: queue + }); + // Assertion 1 + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + // Assertion 2 + expect(app.snsTopic !== null); + // Assertion 3 + expect(stack).toHaveResource("AWS::SNS::Topic", { + TopicName: "existing-topic-obj" + }); + // Assertion 4 + expect(stack).toHaveResource("AWS::SQS::Queue", { + QueueName: "existing-queue-obj" + }); +}); + +// -------------------------------------------------------------- +// Test deployment with imported encryption key +// -------------------------------------------------------------- +test('Test deployment with imported encryption key', () => { + // Stack + const stack = new Stack(); + // Setup + const kmsKey = new kms.Key(stack, 'imported-key', { + enableKeyRotation: false + }); + // Helper declaration + new SnsToSqs(stack, 'sns-to-sqs-stack', { + topicProps: {}, + enableEncryption: true, + encryptionKey: kmsKey + }); + // Assertion 1 + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + // Assertion 2 + expect(stack).toHaveResource("AWS::KMS::Key", { + EnableKeyRotation: false + }); + // Assertion 3 + expect(stack).toHaveResource("AWS::SNS::Topic", { + KmsMasterKeyId: { + "Ref": "importedkey38675D68" + } + }); +}); + +// -------------------------------------------------------------- +// Test deployment with SNS managed KMS key +// -------------------------------------------------------------- +test('Test deployment with SNS managed KMS key', () => { + // Stack + const stack = new Stack(); + // Helper declaration + new SnsToSqs(stack, 'sns-to-sqs-stack', { + topicProps: { + masterKey: kms.Alias.fromAliasName(stack, 'sns-managed-key', 'alias/aws/sns') + }, + queueProps: { + encryption: sqs.QueueEncryption.KMS + }, + enableEncryption: false + }); + // Assertion 1 + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + // Assertion 2 + expect(stack).toHaveResource("AWS::SNS::Topic", { + KmsMasterKeyId: "alias/aws/sns" + }); + // Assertion 3 + expect(stack).toHaveResource("AWS::SQS::Queue", { + KmsMasterKeyId: { + "Fn::GetAtt": [ + "snstosqsstackqueueKey743636E7", + "Arn" + ] + } + }); +}); diff --git a/source/patterns/@aws-solutions-constructs/core/lib/sns-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/sns-helper.ts index 5e259528a..e18598b37 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/sns-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/sns-helper.ts @@ -118,7 +118,7 @@ export function buildTopic(scope: cdk.Construct, props?: BuildTopicProps): [sns. } // Set encryption properties // TODO: Look into using the AWS managed CMK by using 'alias/aws/sns' - if (!props.enableEncryption || props.enableEncryption === true) { + if (props.enableEncryption === undefined || props.enableEncryption === true) { if (props.encryptionKey) { snsTopicProps.masterKey = props.encryptionKey; } else { @@ -132,4 +132,4 @@ export function buildTopic(scope: cdk.Construct, props?: BuildTopicProps): [sns. applySecureTopicPolicy(topic); return [topic, snsTopicProps.masterKey]; -} \ No newline at end of file +} diff --git a/source/patterns/@aws-solutions-constructs/core/lib/sqs-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/sqs-helper.ts index c42052e8a..f758aacb7 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/sqs-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/sqs-helper.ts @@ -15,9 +15,11 @@ import * as sqs from '@aws-cdk/aws-sqs'; import * as defaults from './sqs-defaults'; import * as cdk from '@aws-cdk/core'; +import * as kms from '@aws-cdk/aws-kms'; import { overrideProps } from './utils'; import { AccountPrincipal, Effect, PolicyStatement, AnyPrincipal } from '@aws-cdk/aws-iam'; import { Stack } from '@aws-cdk/core'; +import {buildEncryptionKey} from "./kms-helper"; export interface BuildQueueProps { /** @@ -38,6 +40,19 @@ export interface BuildQueueProps { * @default - Default props are used. */ readonly deadLetterQueue?: sqs.DeadLetterQueue + /** + * Use a KMS Key, either managed by this CDK app, or imported. If importing an encryption key, it must be specified in + * the encryptionKey property for this construct. + * + * @default - false (encryption enabled with a KMS key managed by SQS). + */ + readonly enableEncryption?: boolean + /** + * An optional, imported encryption key to encrypt the SQS queue with. + * + * @default - not specified. + */ + readonly encryptionKey?: kms.Key } export function buildQueue(scope: cdk.Construct, id: string, props?: BuildQueueProps): sqs.Queue { @@ -47,17 +62,19 @@ export function buildQueue(scope: cdk.Construct, id: string, props?: BuildQueueP // If an existingQueueObj is not specified if (!props.existingQueueObj) { // Deploy the queue - return deployQueue(scope, id, props.queueProps, props.deadLetterQueue); + return deployQueue(scope, id, props.queueProps, props.deadLetterQueue, props.enableEncryption, props.encryptionKey); // If an existingQueueObj is specified, return that object as the queue to be used } else { - return props.existingQueueObj; + return props.existingQueueObj } } function deployQueue(scope: cdk.Construct, id: string, queuePropsParam?: sqs.QueueProps, - deadLetterQueueParam?: sqs.DeadLetterQueue): sqs.Queue { + deadLetterQueueParam?: sqs.DeadLetterQueue, + enableEncryptionParam?: boolean, + encryptionKeyParam?: kms.Key): sqs.Queue { // Setup the queue let queueProps; @@ -72,7 +89,14 @@ function deployQueue(scope: cdk.Construct, if (deadLetterQueueParam) { queueProps.deadLetterQueue = deadLetterQueueParam; } - + // Set encryption properties + if (enableEncryptionParam === true) { + if (encryptionKeyParam) { + queueProps.encryptionMasterKey = encryptionKeyParam; + } else { + queueProps.encryptionMasterKey = buildEncryptionKey(scope); + } + } const queue = new sqs.Queue(scope, id, queueProps); applySecureQueuePolicy(queue); @@ -150,4 +174,4 @@ function applySecureQueuePolicy(queue: sqs.Queue): void { } }) ); -} \ No newline at end of file +} diff --git a/source/patterns/@aws-solutions-constructs/core/test/__snapshots__/sqs-helper.test.js.snap b/source/patterns/@aws-solutions-constructs/core/test/__snapshots__/sqs-helper.test.js.snap index c6a989cd2..ad6c9de4c 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/__snapshots__/sqs-helper.test.js.snap +++ b/source/patterns/@aws-solutions-constructs/core/test/__snapshots__/sqs-helper.test.js.snap @@ -588,6 +588,286 @@ Object { } `; +exports[`Test deployment w/ imported encryption key 1`] = ` +Object { + "Resources": Object { + "EncryptionKey1B843E66": Object { + "DeletionPolicy": "Retain", + "Properties": Object { + "EnableKeyRotation": true, + "KeyPolicy": Object { + "Statement": Array [ + Object { + "Action": Array [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::KMS::Key", + "UpdateReplacePolicy": "Retain", + }, + "existingqueue03D57A53": Object { + "Properties": Object { + "KmsMasterKeyId": Object { + "Fn::GetAtt": Array [ + "EncryptionKey1B843E66", + "Arn", + ], + }, + "QueueName": "existing-queue", + }, + "Type": "AWS::SQS::Queue", + }, + "existingqueuePolicy8BCB024D": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "existingqueue03D57A53", + "Arn", + ], + }, + "Sid": "QueueOwnerOnlyAccess", + }, + Object { + "Action": "SQS:*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::GetAtt": Array [ + "existingqueue03D57A53", + "Arn", + ], + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + "Queues": Array [ + Object { + "Ref": "existingqueue03D57A53", + }, + ], + }, + "Type": "AWS::SQS::QueuePolicy", + }, + }, +} +`; + +exports[`Test deployment without imported encryption key 1`] = ` +Object { + "Resources": Object { + "EncryptionKey1B843E66": Object { + "DeletionPolicy": "Retain", + "Properties": Object { + "EnableKeyRotation": true, + "KeyPolicy": Object { + "Statement": Array [ + Object { + "Action": Array [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::KMS::Key", + "UpdateReplacePolicy": "Retain", + }, + "existingqueue03D57A53": Object { + "Properties": Object { + "KmsMasterKeyId": Object { + "Fn::GetAtt": Array [ + "EncryptionKey1B843E66", + "Arn", + ], + }, + "QueueName": "existing-queue", + }, + "Type": "AWS::SQS::Queue", + }, + "existingqueuePolicy8BCB024D": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes", + ], + "Effect": "Allow", + "Principal": Object { + "AWS": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":iam::", + Object { + "Ref": "AWS::AccountId", + }, + ":root", + ], + ], + }, + }, + "Resource": Object { + "Fn::GetAtt": Array [ + "existingqueue03D57A53", + "Arn", + ], + }, + "Sid": "QueueOwnerOnlyAccess", + }, + Object { + "Action": "SQS:*", + "Condition": Object { + "Bool": Object { + "aws:SecureTransport": "false", + }, + }, + "Effect": "Deny", + "Principal": "*", + "Resource": Object { + "Fn::GetAtt": Array [ + "existingqueue03D57A53", + "Arn", + ], + }, + "Sid": "HttpsOnly", + }, + ], + "Version": "2012-10-17", + }, + "Queues": Array [ + Object { + "Ref": "existingqueue03D57A53", + }, + ], + }, + "Type": "AWS::SQS::QueuePolicy", + }, + }, +} +`; + exports[`Test existingQueueObj 1`] = ` Object { "Resources": Object { diff --git a/source/patterns/@aws-solutions-constructs/core/test/sqs-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/sqs-helper.test.ts index dd343e7e1..b7fc41dda 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/sqs-helper.test.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/sqs-helper.test.ts @@ -119,4 +119,55 @@ test('Test existingQueueObj', () => { }); // Assertion 1 expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); -}); \ No newline at end of file +}); + +// -------------------------------------------------------------- +// Test deployment w/ imported encryption key +// -------------------------------------------------------------- +test('Test deployment w/ imported encryption key', () => { + // Stack + const stack = new Stack(); + // Helper declaration + defaults.buildQueue(stack, 'existing-queue', { + queueProps: { + queueName: 'existing-queue' + }, + enableEncryption: true, + encryptionKey: defaults.buildEncryptionKey(stack) + }); + // Assertion 1 + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + // Assertion 2 + expect(stack).toHaveResource("AWS::SQS::Queue", { + QueueName: "existing-queue" + }); + // Assertion 3 + expect(stack).toHaveResource("AWS::KMS::Key", { + EnableKeyRotation: true + }); +}); + +// -------------------------------------------------------------- +// Test deployment without imported encryption key +// -------------------------------------------------------------- +test('Test deployment without imported encryption key', () => { + // Stack + const stack = new Stack(); + // Helper declaration + defaults.buildQueue(stack, 'existing-queue', { + queueProps: { + queueName: 'existing-queue' + }, + enableEncryption: true + }); + // Assertion 1 + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + // Assertion 2 + expect(stack).toHaveResource("AWS::SQS::Queue", { + QueueName: "existing-queue" + }); + // Assertion 3 + expect(stack).toHaveResource("AWS::KMS::Key", { + EnableKeyRotation: true + }); +}); From 409fdb305300022cef7994f89789cd952cabfd8a Mon Sep 17 00:00:00 2001 From: Daniel Matuki da Cunha Date: Tue, 25 Aug 2020 12:50:43 -0700 Subject: [PATCH 2/5] fix(aws-sns-sqs): Update aws-sns-sqs package configuration to utilize latest cdk construct version 1.60.0 --- .../aws-sns-sqs/package.json | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json index 70de5c009..bc2da27af 100644 --- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-sns-sqs", - "version": "1.57.0", + "version": "1.60.0", "description": "CDK constructs for defining an interaction between an Amazon SNS topic and an Amazon SQS queue.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,17 +53,17 @@ } }, "dependencies": { - "@aws-cdk/aws-iam": "~1.57.0", - "@aws-cdk/aws-sns": "~1.57.0", - "@aws-cdk/aws-sqs": "~1.57.0", - "@aws-cdk/aws-sns-subscriptions": "~1.57.0", - "@aws-cdk/aws-kms": "~1.57.0", - "@aws-cdk/core": "~1.57.0", - "@aws-solutions-constructs/core": "~1.57.0", + "@aws-cdk/aws-iam": "~1.60.0", + "@aws-cdk/aws-sns": "~1.60.0", + "@aws-cdk/aws-sqs": "~1.60.0", + "@aws-cdk/aws-sns-subscriptions": "~1.60.0", + "@aws-cdk/aws-kms": "~1.60.0", + "@aws-cdk/core": "~1.60.0", + "@aws-solutions-constructs/core": "~1.60.0", "constructs": "^3.0.2" }, "devDependencies": { - "@aws-cdk/assert": "~1.57.0", + "@aws-cdk/assert": "~1.60.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -73,13 +73,13 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-iam": "~1.57.0", - "@aws-cdk/aws-sns": "~1.57.0", - "@aws-cdk/aws-sqs": "~1.57.0", - "@aws-cdk/aws-sns-subscriptions": "~1.57.0", - "@aws-cdk/aws-kms": "~1.57.0", - "@aws-cdk/core": "~1.57.0", - "@aws-solutions-constructs/core": "~1.57.0", + "@aws-cdk/aws-iam": "~1.60.0", + "@aws-cdk/aws-sns": "~1.60.0", + "@aws-cdk/aws-sqs": "~1.60.0", + "@aws-cdk/aws-sns-subscriptions": "~1.60.0", + "@aws-cdk/aws-kms": "~1.60.0", + "@aws-cdk/core": "~1.60.0", + "@aws-solutions-constructs/core": "~1.60.0", "constructs": "^3.0.2" } } From 3e9cb3d364d4b5183b599b2dd95cef398137c0fc Mon Sep 17 00:00:00 2001 From: Daniel Matuki da Cunha Date: Tue, 25 Aug 2020 17:26:43 -0700 Subject: [PATCH 3/5] fix(aws-sns-sqs): Update managed KMS key integration test to utilize a KMS key with rotation to perform SQS encryption. --- .../integ.sns-managed-kms-key.expected.json | 128 +++++++++--------- .../test/integ.sns-managed-kms-key.ts | 14 +- 2 files changed, 76 insertions(+), 66 deletions(-) diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.expected.json b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.expected.json index 365d91b15..3408036e3 100644 --- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.expected.json @@ -1,6 +1,69 @@ { "Description": "Integration Test for aws-sns-sqs with SNS managed KMS key", "Resources": { + "ImportedSQSEncryptionKey29533C9A": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "EnableKeyRotation": true + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, "testsnssqsdeadLetterQueue8DACC0A1": { "Type": "AWS::SQS::Queue", "Properties": { @@ -164,75 +227,12 @@ ] } }, - "testsnssqsqueueKey31B0457A": { - "Type": "AWS::KMS::Key", - "Properties": { - "KeyPolicy": { - "Statement": [ - { - "Action": [ - "kms:Create*", - "kms:Describe*", - "kms:Enable*", - "kms:List*", - "kms:Put*", - "kms:Update*", - "kms:Revoke*", - "kms:Disable*", - "kms:Get*", - "kms:Delete*", - "kms:ScheduleKeyDeletion", - "kms:CancelKeyDeletion", - "kms:GenerateDataKey", - "kms:TagResource", - "kms:UntagResource" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::", - { - "Ref": "AWS::AccountId" - }, - ":root" - ] - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "Service": "sns.amazonaws.com" - }, - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "Description": "Created by test-sns-sqs/test-sns-sqs/queue" - }, - "UpdateReplacePolicy": "Retain", - "DeletionPolicy": "Retain" - }, "testsnssqsqueueB02504BF": { "Type": "AWS::SQS::Queue", "Properties": { "KmsMasterKeyId": { "Fn::GetAtt": [ - "testsnssqsqueueKey31B0457A", + "ImportedSQSEncryptionKey29533C9A", "Arn" ] }, diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts index df28958b7..aabbb5649 100644 --- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts @@ -14,8 +14,8 @@ // Imports import { App, Stack } from "@aws-cdk/core"; import { SnsToSqs, SnsToSqsProps } from "../lib"; +import { KeyProps } from '@aws-cdk/aws-kms'; import * as kms from '@aws-cdk/aws-kms'; -import * as sqs from '@aws-cdk/aws-sqs'; // Setup const app = new App(); @@ -23,13 +23,23 @@ const stack = new Stack(app, 'test-sns-sqs'); stack.templateOptions.description = 'Integration Test for aws-sns-sqs with SNS managed KMS key'; // Definitions + +// Retrieve SNS managed key to encrypt the SNS Topic const snsManagedKey = kms.Alias.fromAliasName(stack, 'sns-managed-key', 'alias/aws/sns'); + +// Create customer managed KMS CMK to encrypt the SQS Queue +const sqsEncryptionKeyProps: KeyProps = { + enableKeyRotation: true +}; +let sqsEncryptionKey = new kms.Key(stack, 'ImportedSQSEncryptionKey', sqsEncryptionKeyProps); + +// Create the SNS to SQS construct const props: SnsToSqsProps = { topicProps: { masterKey: snsManagedKey }, queueProps: { - encryption: sqs.QueueEncryption.KMS + encryptionMasterKey: sqsEncryptionKey }, enableEncryption: false }; From a2ead48fcb1d48dafbebb6fd2b91788cc6a54ec9 Mon Sep 17 00:00:00 2001 From: Daniel Matuki da Cunha Date: Thu, 27 Aug 2020 15:03:17 -0700 Subject: [PATCH 4/5] fix(aws-sns-sqs): code review changes for new aws-sns-sqs construct --- .../aws-sns-sqs/README.md | 3 +- .../aws-sns-sqs/lib/index.ts | 24 +++++++++---- .../aws-sns-sqs/package.json | 36 +++++++++---------- .../test/integ.existing-kms-key.ts | 2 +- .../test/integ.sns-managed-kms-key.ts | 2 +- .../aws-sns-sqs/test/sns-sqs.test.ts | 22 +++++++----- 6 files changed, 52 insertions(+), 37 deletions(-) diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/README.md b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/README.md index 1427adcc9..41b48c6a2 100644 --- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/README.md +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/README.md @@ -58,8 +58,9 @@ _Parameters_ |deployDeadLetterQueue?|`boolean`|Whether to create a secondary queue to be used as a dead letter queue. Defaults to true.| |deadLetterQueueProps?|[`sqs.QueueProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.QueueProps.html)|Optional user-provided props to override the default props for the dead letter SQS queue.| |maxReceiveCount?|`number`|The number of times a message can be unsuccessfully dequeued before being moved to the dead letter queue. Defaults to 15.| -|enableEncryption?|`boolean`|Use a KMS Key, either managed by this CDK app, or imported. If importing an encryption key, it must be specified in the encryptionKey property for this construct.| +|enableEncryptionWithCustomerManagedKey?|`boolean`|Use a KMS Key, either managed by this CDK app, or imported. If importing an encryption key, it must be specified in the encryptionKey property for this construct.| |encryptionKey?|[`kms.Key`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kms.Key.html)|An optional, imported encryption key to encrypt the SQS queue, and SNS Topic.| +|encryptionKeyProps?|[`kms.KeyProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kms.KeyProps.html)|An optional, user provided properties to override the default properties for the KMS encryption key.| ## Pattern Properties diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/lib/index.ts index 26454aa82..af39074ce 100644 --- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/lib/index.ts @@ -73,13 +73,19 @@ export interface SnsToSqsProps { * * @default - true (encryption enabled, managed by this CDK app). */ - readonly enableEncryption?: boolean + readonly enableEncryptionWithCustomerManagedKey?: boolean /** * An optional, imported encryption key to encrypt the SQS queue, and SNS Topic. * * @default - not specified. */ readonly encryptionKey?: kms.Key + /** + * Optional user-provided props to override the default props for the encryption key. + * + * @default - Default props are used. + */ + readonly encryptionKeyProps?: kms.KeyProps } /** @@ -96,7 +102,7 @@ export class SnsToSqs extends Construct { * @param {cdk.App} scope - represents the scope for all the resources. * @param {string} id - this is a a scope-unique id. * @param {SnsToSqsProps} props - user provided props for the construct. - * @since 0.8.0 + * @since 1.61.0 * @access public */ constructor(scope: Construct, id: string, props: SnsToSqsProps) { @@ -113,13 +119,17 @@ export class SnsToSqs extends Construct { }); } - let enableEncryptionParam = props.enableEncryption; - let encryptionKeyParam = props.encryptionKey; - // Create the encryptionKey if none was provided - if (props.enableEncryption !== false) { + let enableEncryptionParam:boolean | undefined = props.enableEncryptionWithCustomerManagedKey; + let encryptionKeyParam:kms.Key | undefined = props.encryptionKey; + + if (props.enableEncryptionWithCustomerManagedKey === undefined || + props.enableEncryptionWithCustomerManagedKey === true) { enableEncryptionParam = true; + // Create the encryptionKey if none was provided if (!props.encryptionKey) { - encryptionKeyParam = buildEncryptionKey(scope); + encryptionKeyParam = buildEncryptionKey(scope, { + encryptionKeyProps: props.encryptionKeyProps + }); } } // Setup the SNS topic diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json index bc2da27af..f79f4484d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-sns-sqs", - "version": "1.60.0", + "version": "1.61.0", "description": "CDK constructs for defining an interaction between an Amazon SNS topic and an Amazon SQS queue.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,17 +53,17 @@ } }, "dependencies": { - "@aws-cdk/aws-iam": "~1.60.0", - "@aws-cdk/aws-sns": "~1.60.0", - "@aws-cdk/aws-sqs": "~1.60.0", - "@aws-cdk/aws-sns-subscriptions": "~1.60.0", - "@aws-cdk/aws-kms": "~1.60.0", - "@aws-cdk/core": "~1.60.0", - "@aws-solutions-constructs/core": "~1.60.0", - "constructs": "^3.0.2" + "@aws-cdk/aws-iam": "~1.61.0", + "@aws-cdk/aws-sns": "~1.61.0", + "@aws-cdk/aws-sqs": "~1.61.0", + "@aws-cdk/aws-sns-subscriptions": "~1.61.0", + "@aws-cdk/aws-kms": "~1.61.0", + "@aws-cdk/core": "~1.61.0", + "@aws-solutions-constructs/core": "~1.61.0", + "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.60.0", + "@aws-cdk/assert": "~1.61.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -73,13 +73,13 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-iam": "~1.60.0", - "@aws-cdk/aws-sns": "~1.60.0", - "@aws-cdk/aws-sqs": "~1.60.0", - "@aws-cdk/aws-sns-subscriptions": "~1.60.0", - "@aws-cdk/aws-kms": "~1.60.0", - "@aws-cdk/core": "~1.60.0", - "@aws-solutions-constructs/core": "~1.60.0", - "constructs": "^3.0.2" + "@aws-cdk/aws-iam": "~1.61.0", + "@aws-cdk/aws-sns": "~1.61.0", + "@aws-cdk/aws-sqs": "~1.61.0", + "@aws-cdk/aws-sns-subscriptions": "~1.61.0", + "@aws-cdk/aws-kms": "~1.61.0", + "@aws-cdk/core": "~1.61.0", + "@aws-solutions-constructs/core": "~1.61.0", + "constructs": "^3.0.4" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.ts index 143586de9..5ecdb5db0 100644 --- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.ts +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.existing-kms-key.ts @@ -28,7 +28,7 @@ const encryptionKeyProps: KeyProps = { }; let key = new kms.Key(stack, 'ImportedEncryptionKey', encryptionKeyProps); const props: SnsToSqsProps = { - enableEncryption: true, + enableEncryptionWithCustomerManagedKey: true, encryptionKey: key }; new SnsToSqs(stack, 'test-sns-sqs', props); diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts index aabbb5649..32b2914f6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/integ.sns-managed-kms-key.ts @@ -41,7 +41,7 @@ const props: SnsToSqsProps = { queueProps: { encryptionMasterKey: sqsEncryptionKey }, - enableEncryption: false + enableEncryptionWithCustomerManagedKey: false }; new SnsToSqs(stack, 'test-sns-sqs', props); diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts index c2528acdc..b3078f0f3 100644 --- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts @@ -21,7 +21,7 @@ import { SynthUtils } from '@aws-cdk/assert'; import '@aws-cdk/assert/jest'; // -------------------------------------------------------------- -// Pattern deployment w/ new Lambda function and +// Pattern deployment w/ new Topic, new Queue and // default properties // -------------------------------------------------------------- test('Pattern deployment w/ new Topic, new Queue and default props', () => { @@ -76,7 +76,10 @@ test('Pattern deployment w/ new topic, new queue, and overridden props', () => { queueName: "new-queue.fifo", fifo: true }, - enableEncryption: true, + enableEncryptionWithCustomerManagedKey: true, + encryptionKeyProps: { + enableKeyRotation: false + }, deployDeadLetterQueue: false, maxReceiveCount: 0 }; @@ -93,6 +96,10 @@ test('Pattern deployment w/ new topic, new queue, and overridden props', () => { QueueName: "new-queue.fifo", FifoQueue: true }); + // Assertion 3 + expect(stack).toHaveResource("AWS::KMS::Key", { + EnableKeyRotation: false + }); }); // -------------------------------------------------------------- @@ -102,11 +109,9 @@ test('Test getter methods', () => { // Initial Setup const stack = new Stack(); const props: SnsToSqsProps = { - topicProps: {}, - enableEncryption: true, + enableEncryptionWithCustomerManagedKey: true, deployDeadLetterQueue: true, - maxReceiveCount: 0, - queueProps: {} + maxReceiveCount: 0 }; const app = new SnsToSqs(stack, 'test-sns-sqs', props); // Assertion 1 @@ -162,8 +167,7 @@ test('Test deployment with imported encryption key', () => { }); // Helper declaration new SnsToSqs(stack, 'sns-to-sqs-stack', { - topicProps: {}, - enableEncryption: true, + enableEncryptionWithCustomerManagedKey: true, encryptionKey: kmsKey }); // Assertion 1 @@ -194,7 +198,7 @@ test('Test deployment with SNS managed KMS key', () => { queueProps: { encryption: sqs.QueueEncryption.KMS }, - enableEncryption: false + enableEncryptionWithCustomerManagedKey: false }); // Assertion 1 expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); From dc2790033a296f0e6165d5df43b98679bd584aed Mon Sep 17 00:00:00 2001 From: Daniel Matuki da Cunha Date: Fri, 28 Aug 2020 08:33:11 -0700 Subject: [PATCH 5/5] fix(aws-sns-sqs): code review changes for new aws-sns-sqs construct --- .../aws-sns-sqs/test/sns-sqs.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts index b3078f0f3..18a67813a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-sns-sqs/test/sns-sqs.test.ts @@ -21,7 +21,7 @@ import { SynthUtils } from '@aws-cdk/assert'; import '@aws-cdk/assert/jest'; // -------------------------------------------------------------- -// Pattern deployment w/ new Topic, new Queue and +// Pattern deployment with new Topic, new Queue and // default properties // -------------------------------------------------------------- test('Pattern deployment w/ new Topic, new Queue and default props', () => { @@ -62,7 +62,7 @@ test('Pattern deployment w/ new Topic, new Queue and default props', () => { }); // -------------------------------------------------------------- -// Pattern deployment w/ new Topic, new Queue, and +// Pattern deployment with new Topic, new Queue, and // overridden properties // -------------------------------------------------------------- test('Pattern deployment w/ new topic, new queue, and overridden props', () => { @@ -125,7 +125,7 @@ test('Test getter methods', () => { }); // -------------------------------------------------------------- -// Test deployment w/ existing queue, and topic +// Test deployment with existing queue, and topic // -------------------------------------------------------------- test('Test deployment w/ existing queue, and topic', () => { // Stack