From b280868a2bce49bc0071101a0ffdfa45e8d4a458 Mon Sep 17 00:00:00 2001 From: yamatatsu Date: Fri, 29 Oct 2021 17:43:31 +0900 Subject: [PATCH 1/4] feat(iot-actions): Add the action to put CloudWatch Logs 1. add the action 2. add tests 3. describe to README --- packages/@aws-cdk/aws-iot-actions/README.md | 18 ++ .../lib/cloudwatch-logs-action.ts | 48 +++++ .../@aws-cdk/aws-iot-actions/lib/index.ts | 1 + .../aws-iot-actions/lib/private/role.ts | 25 +++ .../@aws-cdk/aws-iot-actions/package.json | 2 + .../cloudwatch-logs-action.test.ts | 202 ++++++++++++++++++ ...integ.cloudwatch-logs-action.expected.json | 83 +++++++ .../integ.cloudwatch-logs-action.ts | 25 +++ 8 files changed, 404 insertions(+) create mode 100644 packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts create mode 100644 packages/@aws-cdk/aws-iot-actions/lib/private/role.ts create mode 100644 packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts create mode 100644 packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.expected.json create mode 100644 packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.ts diff --git a/packages/@aws-cdk/aws-iot-actions/README.md b/packages/@aws-cdk/aws-iot-actions/README.md index 67d9b2924dd53..a816d88177652 100644 --- a/packages/@aws-cdk/aws-iot-actions/README.md +++ b/packages/@aws-cdk/aws-iot-actions/README.md @@ -48,3 +48,21 @@ new iot.TopicRule(this, 'TopicRule', { actions: [new actions.LambdaFunctionAction(func)], }); ``` + +## Put logs to CloudWatch Logs + +The code snippet below creates an AWS IoT Rule that put logs to CloudWatch Logs +when it is triggered. + +```ts +import * as iot from '@aws-cdk/aws-iot'; +import * as actions from '../../lib'; +import * as logs from '@aws-cdk/aws-logs'; + +const logGroup = new logs.LogGroup(this, 'MyLogGroup'); + +new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + actions: [new actions.CloudwatchLogsAction(logGroup)], +}); +``` diff --git a/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts b/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts new file mode 100644 index 0000000000000..150fb0e473ad9 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts @@ -0,0 +1,48 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as iot from '@aws-cdk/aws-iot'; +import * as logs from '@aws-cdk/aws-logs'; +import { singletonActionRole } from './private/role'; + +/** + * Configuration properties of an action for CloudWatch Logs. + */ +export interface CloudwatchLogsActionProps { + /** + * The IAM role that allows access to the CloudWatch log group. + * + * @default a new role will be created + */ + readonly role?: iam.IRole; +} + +/** + * The action to send data to Amazon CloudWatch Logs + */ +export class CloudwatchLogsAction implements iot.IAction { + private readonly role?: iam.IRole; + + /** + * @param logGroup The CloudWatch log group to which the action sends data + * @param props Optional properties to not use default + */ + constructor( + private readonly logGroup: logs.ILogGroup, + props: CloudwatchLogsActionProps = {}, + ) { + this.role = props.role; + } + + bind(rule: iot.ITopicRule): iot.ActionConfig { + const role = this.role ?? singletonActionRole(rule); + this.logGroup.grant(role, 'logs:CreateLogStream', 'logs:DescribeLogStreams', 'logs:PutLogEvents'); + + return { + configuration: { + cloudwatchLogs: { + logGroupName: this.logGroup.logGroupName, + roleArn: role.roleArn, + }, + }, + }; + } +} diff --git a/packages/@aws-cdk/aws-iot-actions/lib/index.ts b/packages/@aws-cdk/aws-iot-actions/lib/index.ts index 751863744ffe2..ef917fd0e2181 100644 --- a/packages/@aws-cdk/aws-iot-actions/lib/index.ts +++ b/packages/@aws-cdk/aws-iot-actions/lib/index.ts @@ -1 +1,2 @@ +export * from './cloudwatch-logs-action'; export * from './lambda-function-action'; diff --git a/packages/@aws-cdk/aws-iot-actions/lib/private/role.ts b/packages/@aws-cdk/aws-iot-actions/lib/private/role.ts new file mode 100644 index 0000000000000..614af44439767 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/lib/private/role.ts @@ -0,0 +1,25 @@ +import * as iam from '@aws-cdk/aws-iam'; +import { IConstruct, PhysicalName } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + +/** + * Obtain the Role for the TopicRule + * + * If a role already exists, it will be returned. This ensures that if a rule have multiple + * actions, they will share a role. + * @internal + */ +export function singletonActionRole(scope: IConstruct): iam.IRole { + const id = 'TopicRuleActionRole'; + const existing = scope.node.tryFindChild(id) as iam.IRole; + if (existing) return existing; + + const role = new iam.Role(scope as Construct, id, { + roleName: PhysicalName.GENERATE_IF_NEEDED, + assumedBy: new iam.ServicePrincipal('iot.amazonaws.com'), + }); + return role; +} diff --git a/packages/@aws-cdk/aws-iot-actions/package.json b/packages/@aws-cdk/aws-iot-actions/package.json index 3b66b73a1562e..d60ae4c5e1376 100644 --- a/packages/@aws-cdk/aws-iot-actions/package.json +++ b/packages/@aws-cdk/aws-iot-actions/package.json @@ -82,6 +82,7 @@ "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-iot": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69" }, @@ -90,6 +91,7 @@ "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-iot": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69" }, diff --git a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts new file mode 100644 index 0000000000000..d97b941e23a0d --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts @@ -0,0 +1,202 @@ +import { Template } from '@aws-cdk/assertions'; +import * as iam from '@aws-cdk/aws-iam'; +import * as iot from '@aws-cdk/aws-iot'; +import * as logs from '@aws-cdk/aws-logs'; +import * as cdk from '@aws-cdk/core'; +import * as actions from '../../lib'; + +test('Default cloudwatch logs action', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const logGroup = new logs.LogGroup(stack, 'MyLogGroup'); + + // WHEN + topicRule.addAction( + new actions.CloudwatchLogsAction(logGroup), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + { + CloudwatchLogs: { + LogGroupName: { Ref: 'MyLogGroup5C0DAD85' }, + RoleArn: { + 'Fn::GetAtt': [ + 'MyTopicRuleTopicRuleActionRoleCE2D05DA', + 'Arn', + ], + }, + }, + }, + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'iot.amazonaws.com', + }, + }, + ], + Version: '2012-10-17', + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: ['logs:CreateLogStream', 'logs:DescribeLogStreams', 'logs:PutLogEvents'], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'MyLogGroup5C0DAD85', + 'Arn', + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'MyTopicRuleTopicRuleActionRoleDefaultPolicy54A701F7', + Roles: [ + { Ref: 'MyTopicRuleTopicRuleActionRoleCE2D05DA' }, + ], + }); +}); + +test('can set role', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const logGroup = new logs.LogGroup(stack, 'MyLogGroup'); + const role = new iam.Role(stack, 'MyRole', { + assumedBy: new iam.ServicePrincipal('iot.amazonaws.com'), + }); + + // WHEN + topicRule.addAction( + new actions.CloudwatchLogsAction(logGroup, { + role, + }), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + { + CloudwatchLogs: { + LogGroupName: { Ref: 'MyLogGroup5C0DAD85' }, + RoleArn: { + 'Fn::GetAtt': [ + 'MyRoleF48FFE04', + 'Arn', + ], + }, + }, + }, + ], + }, + }); +}); + +test('The specified role is added a policy needed for sending data to logs', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const logGroup = new logs.LogGroup(stack, 'MyLogGroup'); + const role = new iam.Role(stack, 'MyRole', { + assumedBy: new iam.ServicePrincipal('iot.amazonaws.com'), + }); + + // WHEN + topicRule.addAction( + new actions.CloudwatchLogsAction(logGroup, { + role, + }), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: ['logs:CreateLogStream', 'logs:DescribeLogStreams', 'logs:PutLogEvents'], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'MyLogGroup5C0DAD85', + 'Arn', + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'MyRoleDefaultPolicyA36BE1DD', + Roles: [ + { Ref: 'MyRoleF48FFE04' }, + ], + }); +}); + + +test('When multiple actions are omitted role property, the actions use same one role', () => { + // GIVEN + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const logGroup1 = new logs.LogGroup(stack, 'MyLogGroup1'); + const logGroup2 = new logs.LogGroup(stack, 'MyLogGroup2'); + + // WHEN + topicRule.addAction(new actions.CloudwatchLogsAction(logGroup1)); + topicRule.addAction(new actions.CloudwatchLogsAction(logGroup2)); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + { + CloudwatchLogs: { + LogGroupName: { Ref: 'MyLogGroup14A6E382A' }, + RoleArn: { + 'Fn::GetAtt': [ + 'MyTopicRuleTopicRuleActionRoleCE2D05DA', + 'Arn', + ], + }, + }, + }, + { + CloudwatchLogs: { + LogGroupName: { Ref: 'MyLogGroup279D6359D' }, + RoleArn: { + 'Fn::GetAtt': [ + 'MyTopicRuleTopicRuleActionRoleCE2D05DA', + 'Arn', + ], + }, + }, + }, + ], + }, + }); +}); diff --git a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.expected.json b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.expected.json new file mode 100644 index 0000000000000..de237bc1ae22f --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.expected.json @@ -0,0 +1,83 @@ +{ + "Resources": { + "TopicRule40A4EA44": { + "Type": "AWS::IoT::TopicRule", + "Properties": { + "TopicRulePayload": { + "Actions": [ + { + "CloudwatchLogs": { + "LogGroupName": { + "Ref": "MyLogGroup5C0DAD85" + }, + "RoleArn": { + "Fn::GetAtt": [ + "TopicRuleTopicRuleActionRole246C4F77", + "Arn" + ] + } + } + } + ], + "AwsIotSqlVersion": "2016-03-23", + "Sql": "SELECT topic(2) as device_id FROM 'device/+/data'" + } + } + }, + "TopicRuleTopicRuleActionRole246C4F77": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "iot.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogStream", + "logs:DescribeLogStreams", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyLogGroup5C0DAD85", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687", + "Roles": [ + { + "Ref": "TopicRuleTopicRuleActionRole246C4F77" + } + ] + } + }, + "MyLogGroup5C0DAD85": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "RetentionInDays": 731 + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.ts b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.ts new file mode 100644 index 0000000000000..ec20dc44530c4 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.ts @@ -0,0 +1,25 @@ +/// !cdk-integ pragma:ignore-assets +import * as iot from '@aws-cdk/aws-iot'; +import * as logs from '@aws-cdk/aws-logs'; +import * as cdk from '@aws-cdk/core'; +import * as actions from '../../lib'; + +const app = new cdk.App(); + +class TestStack extends cdk.Stack { + constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const topicRule = new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + + const logGroup = new logs.LogGroup(this, 'MyLogGroup', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + topicRule.addAction(new actions.CloudwatchLogsAction(logGroup)); + } +} + +new TestStack(app, 'test-stack'); +app.synth(); From 4c341d5e5f80e60dad71a2aec0238b20e11ee633 Mon Sep 17 00:00:00 2001 From: yamatatsu Date: Tue, 2 Nov 2021 16:48:56 +0900 Subject: [PATCH 2/4] fix(iot-actions): address comments --- packages/@aws-cdk/aws-iot-actions/README.md | 2 +- .../lib/cloudwatch-logs-action.ts | 9 +++++---- .../aws-iot-actions/lib/private/role.ts | 4 +++- .../cloudwatch-logs-action.test.ts | 18 +++++++----------- .../integ.cloudwatch-logs-action.ts | 2 +- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/@aws-cdk/aws-iot-actions/README.md b/packages/@aws-cdk/aws-iot-actions/README.md index a816d88177652..a5aab386e2ed4 100644 --- a/packages/@aws-cdk/aws-iot-actions/README.md +++ b/packages/@aws-cdk/aws-iot-actions/README.md @@ -63,6 +63,6 @@ const logGroup = new logs.LogGroup(this, 'MyLogGroup'); new iot.TopicRule(this, 'TopicRule', { sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), - actions: [new actions.CloudwatchLogsAction(logGroup)], + actions: [new actions.CloudWatchLogsAction(logGroup)], }); ``` diff --git a/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts b/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts index 150fb0e473ad9..dda14de887774 100644 --- a/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts +++ b/packages/@aws-cdk/aws-iot-actions/lib/cloudwatch-logs-action.ts @@ -6,7 +6,7 @@ import { singletonActionRole } from './private/role'; /** * Configuration properties of an action for CloudWatch Logs. */ -export interface CloudwatchLogsActionProps { +export interface CloudWatchLogsActionProps { /** * The IAM role that allows access to the CloudWatch log group. * @@ -18,7 +18,7 @@ export interface CloudwatchLogsActionProps { /** * The action to send data to Amazon CloudWatch Logs */ -export class CloudwatchLogsAction implements iot.IAction { +export class CloudWatchLogsAction implements iot.IAction { private readonly role?: iam.IRole; /** @@ -27,14 +27,15 @@ export class CloudwatchLogsAction implements iot.IAction { */ constructor( private readonly logGroup: logs.ILogGroup, - props: CloudwatchLogsActionProps = {}, + props: CloudWatchLogsActionProps = {}, ) { this.role = props.role; } bind(rule: iot.ITopicRule): iot.ActionConfig { const role = this.role ?? singletonActionRole(rule); - this.logGroup.grant(role, 'logs:CreateLogStream', 'logs:DescribeLogStreams', 'logs:PutLogEvents'); + this.logGroup.grantWrite(role); + this.logGroup.grant(role, 'logs:DescribeLogStreams'); return { configuration: { diff --git a/packages/@aws-cdk/aws-iot-actions/lib/private/role.ts b/packages/@aws-cdk/aws-iot-actions/lib/private/role.ts index 614af44439767..78c72b914f56b 100644 --- a/packages/@aws-cdk/aws-iot-actions/lib/private/role.ts +++ b/packages/@aws-cdk/aws-iot-actions/lib/private/role.ts @@ -15,7 +15,9 @@ import { Construct } from '@aws-cdk/core'; export function singletonActionRole(scope: IConstruct): iam.IRole { const id = 'TopicRuleActionRole'; const existing = scope.node.tryFindChild(id) as iam.IRole; - if (existing) return existing; + if (existing) { + return existing; + }; const role = new iam.Role(scope as Construct, id, { roleName: PhysicalName.GENERATE_IF_NEEDED, diff --git a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts index d97b941e23a0d..37b87d6a81d7f 100644 --- a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts +++ b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts @@ -15,7 +15,7 @@ test('Default cloudwatch logs action', () => { // WHEN topicRule.addAction( - new actions.CloudwatchLogsAction(logGroup), + new actions.CloudWatchLogsAction(logGroup), ); // THEN @@ -82,13 +82,11 @@ test('can set role', () => { sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), }); const logGroup = new logs.LogGroup(stack, 'MyLogGroup'); - const role = new iam.Role(stack, 'MyRole', { - assumedBy: new iam.ServicePrincipal('iot.amazonaws.com'), - }); + const role = iam.Role.fromRoleArn(stack, 'MyRole', 'arn:aws:iam::123456789012:role/ForTest'); // WHEN topicRule.addAction( - new actions.CloudwatchLogsAction(logGroup, { + new actions.CloudWatchLogsAction(logGroup, { role, }), ); @@ -120,13 +118,11 @@ test('The specified role is added a policy needed for sending data to logs', () sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), }); const logGroup = new logs.LogGroup(stack, 'MyLogGroup'); - const role = new iam.Role(stack, 'MyRole', { - assumedBy: new iam.ServicePrincipal('iot.amazonaws.com'), - }); + const role = iam.Role.fromRoleArn(stack, 'MyRole', 'arn:aws:iam::123456789012:role/ForTest'); // WHEN topicRule.addAction( - new actions.CloudwatchLogsAction(logGroup, { + new actions.CloudWatchLogsAction(logGroup, { role, }), ); @@ -167,8 +163,8 @@ test('When multiple actions are omitted role property, the actions use same one const logGroup2 = new logs.LogGroup(stack, 'MyLogGroup2'); // WHEN - topicRule.addAction(new actions.CloudwatchLogsAction(logGroup1)); - topicRule.addAction(new actions.CloudwatchLogsAction(logGroup2)); + topicRule.addAction(new actions.CloudWatchLogsAction(logGroup1)); + topicRule.addAction(new actions.CloudWatchLogsAction(logGroup2)); // THEN Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { diff --git a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.ts b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.ts index ec20dc44530c4..802f485b77e37 100644 --- a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.ts +++ b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.ts @@ -17,7 +17,7 @@ class TestStack extends cdk.Stack { const logGroup = new logs.LogGroup(this, 'MyLogGroup', { removalPolicy: cdk.RemovalPolicy.DESTROY, }); - topicRule.addAction(new actions.CloudwatchLogsAction(logGroup)); + topicRule.addAction(new actions.CloudWatchLogsAction(logGroup)); } } From 3ba3e615a85c8fbb947f6db67cc738573332c2f2 Mon Sep 17 00:00:00 2001 From: yamatatsu Date: Tue, 2 Nov 2021 17:09:37 +0900 Subject: [PATCH 3/4] test(iot-actions): fix test errors --- .../cloudwatch-logs-action.test.ts | 41 ++++++++++--------- ...integ.cloudwatch-logs-action.expected.json | 11 ++++- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts index 37b87d6a81d7f..4e25f43367c31 100644 --- a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts +++ b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/cloudwatch-logs-action.test.ts @@ -56,13 +56,17 @@ test('Default cloudwatch logs action', () => { PolicyDocument: { Statement: [ { - Action: ['logs:CreateLogStream', 'logs:DescribeLogStreams', 'logs:PutLogEvents'], + Action: ['logs:CreateLogStream', 'logs:PutLogEvents'], Effect: 'Allow', Resource: { - 'Fn::GetAtt': [ - 'MyLogGroup5C0DAD85', - 'Arn', - ], + 'Fn::GetAtt': ['MyLogGroup5C0DAD85', 'Arn'], + }, + }, + { + Action: 'logs:DescribeLogStreams', + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': ['MyLogGroup5C0DAD85', 'Arn'], }, }, ], @@ -98,12 +102,7 @@ test('can set role', () => { { CloudwatchLogs: { LogGroupName: { Ref: 'MyLogGroup5C0DAD85' }, - RoleArn: { - 'Fn::GetAtt': [ - 'MyRoleF48FFE04', - 'Arn', - ], - }, + RoleArn: 'arn:aws:iam::123456789012:role/ForTest', }, }, ], @@ -132,22 +131,24 @@ test('The specified role is added a policy needed for sending data to logs', () PolicyDocument: { Statement: [ { - Action: ['logs:CreateLogStream', 'logs:DescribeLogStreams', 'logs:PutLogEvents'], + Action: ['logs:CreateLogStream', 'logs:PutLogEvents'], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': ['MyLogGroup5C0DAD85', 'Arn'], + }, + }, + { + Action: 'logs:DescribeLogStreams', Effect: 'Allow', Resource: { - 'Fn::GetAtt': [ - 'MyLogGroup5C0DAD85', - 'Arn', - ], + 'Fn::GetAtt': ['MyLogGroup5C0DAD85', 'Arn'], }, }, ], Version: '2012-10-17', }, - PolicyName: 'MyRoleDefaultPolicyA36BE1DD', - Roles: [ - { Ref: 'MyRoleF48FFE04' }, - ], + PolicyName: 'MyRolePolicy64AB00A5', + Roles: ['ForTest'], }); }); diff --git a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.expected.json b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.expected.json index de237bc1ae22f..7d1748a084c77 100644 --- a/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.expected.json +++ b/packages/@aws-cdk/aws-iot-actions/test/cloudwatch-logs/integ.cloudwatch-logs-action.expected.json @@ -49,7 +49,6 @@ { "Action": [ "logs:CreateLogStream", - "logs:DescribeLogStreams", "logs:PutLogEvents" ], "Effect": "Allow", @@ -59,6 +58,16 @@ "Arn" ] } + }, + { + "Action": "logs:DescribeLogStreams", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyLogGroup5C0DAD85", + "Arn" + ] + } } ], "Version": "2012-10-17" From 4cc2ee2b6db5cd57b51d1c8122c6b50127c6a86a Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Tue, 2 Nov 2021 09:42:08 -0700 Subject: [PATCH 4/4] Change import in ReadMe to not be local. --- packages/@aws-cdk/aws-iot-actions/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-iot-actions/README.md b/packages/@aws-cdk/aws-iot-actions/README.md index a5aab386e2ed4..b18182a80a9ad 100644 --- a/packages/@aws-cdk/aws-iot-actions/README.md +++ b/packages/@aws-cdk/aws-iot-actions/README.md @@ -56,7 +56,7 @@ when it is triggered. ```ts import * as iot from '@aws-cdk/aws-iot'; -import * as actions from '../../lib'; +import * as actions from '@aws-cdk/aws-iot-actions'; import * as logs from '@aws-cdk/aws-logs'; const logGroup = new logs.LogGroup(this, 'MyLogGroup');