Skip to content

Commit 5411ab7

Browse files
ericquinonesQuinonesbiffgaut
authored
feat(aws-iot-sqs): initial implementation (#267)
* Initial design commit for new pattern: aws-iot-sqs * Corrections to README. renamed to and removed a typo * feat(aws-iot-sqs): initial implementation fixes #266 Added implementation of new pattern Added unit and integration tests * Revert aws-iot-sqs versions in package.json * Update integration tests to resolve CFN Nag warnings * Refreshing unit and integration test snapshots after merging from main * PR feedback updates: * Removed references to lambda from .gitignore and .eslintignore * Use service namespaces instead of named imports * Updated architecture diagram Co-authored-by: Quinones <[email protected]> Co-authored-by: biffgaut <[email protected]>
1 parent b79664b commit 5411ab7

25 files changed

+6403
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
lib/*.js
2+
test/*.js
3+
*.d.ts
4+
coverage
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
lib/*.js
2+
test/*.js
3+
*.js.map
4+
*.d.ts
5+
node_modules
6+
*.generated.ts
7+
dist
8+
.jsii
9+
10+
.LAST_BUILD
11+
.nyc_output
12+
coverage
13+
.nycrc
14+
.LAST_PACKAGE
15+
*.snk
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Exclude typescript source and config
2+
*.ts
3+
tsconfig.json
4+
coverage
5+
.nyc_output
6+
*.tgz
7+
*.snk
8+
*.tsbuildinfo
9+
10+
# Include javascript files and typescript declarations
11+
!*.js
12+
!*.d.ts
13+
14+
# Exclude jsii outdir
15+
dist
16+
17+
# Include .jsii
18+
!.jsii
19+
20+
# Include .jsii
21+
!.jsii
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# aws-iot-sqs module
2+
<!--BEGIN STABILITY BANNER-->
3+
4+
---
5+
6+
![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge)
7+
8+
> All classes are under active development and subject to non-backward compatible changes or removal in any
9+
> future version. These are not subject to the [Semantic Versioning](https://semver.org/) model.
10+
> 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.
11+
12+
---
13+
<!--END STABILITY BANNER-->
14+
15+
| **Reference Documentation**:| <span style="font-weight: normal">https://docs.aws.amazon.com/solutions/latest/constructs/</span>|
16+
|:-------------|:-------------|
17+
<div style="height:8px"></div>
18+
19+
| **Language** | **Package** |
20+
|:-------------|-----------------|
21+
|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_iot_sqs`|
22+
|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-iot-sqs`|
23+
|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.iotsqs`|
24+
25+
This AWS Solutions Construct implements an AWS IoT MQTT topic rule and an AWS SQS Queue pattern.
26+
27+
Here is a minimal deployable pattern definition in Typescript:
28+
29+
``` typescript
30+
const { IotToSqsProps, IotToSqs } from '@aws-solutions-constructs/aws-iot-sqs';
31+
32+
const props: IotToSqsProps = {
33+
iotTopicRuleProps: {
34+
topicRulePayload: {
35+
ruleDisabled: false,
36+
description: "Testing the IotToSqs Pattern",
37+
sql: "SELECT * FROM 'iot/sqs/#'",
38+
actions: []
39+
}
40+
}
41+
};
42+
43+
new IotToSqs(this, 'test-iot-sqs-integration', props);
44+
```
45+
46+
## Initializer
47+
48+
``` text
49+
new IotToSqs(scope: Construct, id: string, props: IotToSqsProps);
50+
```
51+
52+
_Parameters_
53+
54+
* scope [`Construct`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html)
55+
* id `string`
56+
* props [`IotToSqsProps`](#pattern-construct-props)
57+
58+
## Pattern Construct Props
59+
60+
| **Name** | **Type** | **Description** |
61+
|:-------------|:----------------|-----------------|
62+
|existingQueueObj?|[`sqs.Queue`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.Queue.html)|Existing instance of SQS queue object, providing both this and `queueProps` will cause an error.|
63+
|queueProps?|[`sqs.QueueProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.QueueProps.html)|User provided props to override the default props for the SQS queue.|
64+
|deadLetterQueueProps?|[`sqs.QueueProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.QueueProps.html)|Optional user provided properties for the dead letter queue.|
65+
|deployDeadLetterQueue?|`boolean`|Whether to deploy a secondary queue to be used as a dead letter queue. Default `true`.|
66+
|maxReceiveCount?|`number`|The number of times a message can be unsuccessfully dequeued before being moved to the dead-letter queue. Required field if `deployDeadLetterQueue`=`true`.|
67+
|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.|
68+
|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.|
69+
|encryptionKeyProps?|[`kms.KeyProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kms.KeyProps.html)|Optional user-provided props to override the default props for the encryption key.|
70+
|iotTopicRuleProps?|[`iot.CfnTopicRuleProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iot.CfnTopicRuleProps.html)|User provided CfnTopicRuleProps to override the defaults|
71+
72+
## Pattern Properties
73+
74+
| **Name** | **Type** | **Description** |
75+
|:-------------|:----------------|-----------------|
76+
|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.|
77+
|iotActionsRole|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|Returns an instance of `iam.Role` created by the construct, which allows IoT to publish messages to the SQS Queue|
78+
|sqsQueue|[`sqs.Queue`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.Queue.html)|Returns an instance of `sqs.Queue` created by the construct|
79+
|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.|
80+
|iotTopicRule|[`iot.CfnTopicRule`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iot.CfnTopicRule.html)|Returns an instance of `iot.CfnTopicRule` created by the construct|
81+
82+
## Default settings
83+
84+
Out of the box implementation of the Construct without any override will set the following defaults:
85+
86+
### Amazon IoT Rule
87+
* Configure an IoT Rule to send messages to the SQS Queue
88+
89+
### Amazon IAM Role
90+
* Configure least privilege access IAM role for Amazon IoT to be able to publish messages to the SQS Queue
91+
92+
### Amazon SQS Queue
93+
* Deploy a dead-letter queue for the source queue.
94+
* Enable server-side encryption for the source queue using a customer-managed AWS KMS key.
95+
* Enforce encryption of data in transit.
96+
97+
## Architecture
98+
![Architecture Diagram](architecture.png)
99+
100+
***
101+
&copy; Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/**
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
5+
* with the License. A copy of the License is located at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
10+
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
11+
* and limitations under the License.
12+
*/
13+
14+
import * as cdk from '@aws-cdk/core';
15+
import * as sqs from '@aws-cdk/aws-sqs';
16+
import * as iot from '@aws-cdk/aws-iot';
17+
import * as kms from '@aws-cdk/aws-kms';
18+
import * as iam from '@aws-cdk/aws-iam';
19+
import * as defaults from '@aws-solutions-constructs/core';
20+
21+
/**
22+
* @summary The properties for the IotToSqs class.
23+
*/
24+
export interface IotToSqsProps {
25+
/**
26+
* Existing instance of SQS queue object, providing both this and queueProps will cause an error.
27+
*
28+
* @default - None
29+
*/
30+
readonly existingQueueObj?: sqs.Queue;
31+
32+
/**
33+
* User provided props to override the default props for the SQS queue.
34+
*
35+
* @default - Default props are used
36+
*/
37+
readonly queueProps?: sqs.QueueProps;
38+
39+
/**
40+
* Optional user provided properties for the dead letter queue
41+
*
42+
* @default - Default props are used
43+
*/
44+
readonly deadLetterQueueProps?: sqs.QueueProps;
45+
46+
/**
47+
* Whether to deploy a secondary queue to be used as a dead letter queue.
48+
*
49+
* @default - true.
50+
*/
51+
readonly deployDeadLetterQueue?: boolean;
52+
53+
/**
54+
* The number of times a message can be unsuccessfully dequeued before being moved to the dead-letter queue.
55+
*
56+
* @default - required field if deployDeadLetterQueue=true.
57+
*/
58+
readonly maxReceiveCount?: number;
59+
60+
/**
61+
* Use a KMS Key, either managed by this CDK app, or imported. If importing an encryption key, it must be specified in
62+
* the encryptionKey property for this construct.
63+
*
64+
* @default - true (encryption enabled, managed by this CDK app).
65+
*/
66+
readonly enableEncryptionWithCustomerManagedKey?: boolean;
67+
68+
/**
69+
* An optional, imported encryption key to encrypt the SQS queue, and SNS Topic.
70+
*
71+
* @default - not specified.
72+
*/
73+
readonly encryptionKey?: kms.Key;
74+
75+
/**
76+
* Optional user-provided props to override the default props for the encryption key.
77+
*
78+
* @default - Default props are used.
79+
*/
80+
readonly encryptionKeyProps?: kms.KeyProps;
81+
82+
/**
83+
* User provided CfnTopicRuleProps to override the defaults
84+
*
85+
* @default - None
86+
*/
87+
readonly iotTopicRuleProps: iot.CfnTopicRuleProps;
88+
}
89+
90+
export class IotToSqs extends cdk.Construct {
91+
public readonly sqsQueue: sqs.Queue;
92+
public readonly deadLetterQueue?: sqs.DeadLetterQueue;
93+
public readonly encryptionKey?: kms.IKey;
94+
public readonly iotActionsRole: iam.Role;
95+
public readonly iotTopicRule: iot.CfnTopicRule;
96+
97+
/**
98+
* @summary Constructs a new instance of the IotToSqs class.
99+
* @param {cdk.App} scope - represents the scope for all the resources.
100+
* @param {string} id - this is a a scope-unique id.
101+
* @param {IotToSqsProps} props - user provided props for the construct
102+
* @access public
103+
*/
104+
constructor(scope: cdk.Construct, id: string, props: IotToSqsProps) {
105+
super(scope, id);
106+
defaults.CheckProps(props);
107+
108+
// Setup the dead letter queue, if applicable
109+
this.deadLetterQueue = defaults.buildDeadLetterQueue(this, {
110+
existingQueueObj: props.existingQueueObj,
111+
deployDeadLetterQueue: props.deployDeadLetterQueue,
112+
deadLetterQueueProps: props.deadLetterQueueProps,
113+
maxReceiveCount: props.maxReceiveCount
114+
});
115+
116+
// Default to `true` if `enableEncryptionWithCustomerManagedKey` is undefined
117+
let enableEncryptionWithCustomerManagedKey = props.enableEncryptionWithCustomerManagedKey;
118+
if (enableEncryptionWithCustomerManagedKey === undefined) {
119+
enableEncryptionWithCustomerManagedKey = true;
120+
}
121+
122+
// Setup the queue
123+
[this.sqsQueue, this.encryptionKey] = defaults.buildQueue(this, 'queue', {
124+
existingQueueObj: props.existingQueueObj,
125+
queueProps: props.queueProps,
126+
deadLetterQueue: this.deadLetterQueue,
127+
enableEncryptionWithCustomerManagedKey,
128+
encryptionKey: props.encryptionKey,
129+
encryptionKeyProps: props.encryptionKeyProps
130+
});
131+
132+
if (this.sqsQueue.fifo) {
133+
throw new Error('The IoT SQS action doesn\'t support Amazon SQS FIFO (First-In-First-Out) queues');
134+
}
135+
136+
// Role to allow IoT to send messages to the SQS Queue
137+
this.iotActionsRole = new iam.Role(this, 'iot-actions-role', {
138+
assumedBy: new iam.ServicePrincipal('iot.amazonaws.com')
139+
});
140+
this.sqsQueue.grantSendMessages(this.iotActionsRole);
141+
142+
if (this.encryptionKey) {
143+
this.encryptionKey.grantEncrypt(this.iotActionsRole);
144+
}
145+
146+
const defaultIotTopicProps = defaults.DefaultCfnTopicRuleProps([{
147+
sqs: {
148+
queueUrl: this.sqsQueue.queueUrl,
149+
roleArn: this.iotActionsRole.roleArn
150+
}
151+
}]);
152+
const iotTopicProps = defaults.overrideProps(defaultIotTopicProps, props.iotTopicRuleProps, true);
153+
154+
// Create the IoT topic rule
155+
this.iotTopicRule = new iot.CfnTopicRule(this, 'IotTopicRule', iotTopicProps);
156+
}
157+
}

0 commit comments

Comments
 (0)