From 71888fe6902abdf9f91da306d3bfca5c9f9a05a8 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Fri, 14 Aug 2020 22:37:17 +0800
Subject: [PATCH 01/13] Support L2 construct for SlackChannelConfiguration of
 chatbot. 1. add L2 construct 2. add unit tests 3. add integration test 4.
 update package.json

---
 packages/@aws-cdk/aws-chatbot/lib/index.ts    |   1 +
 .../lib/slack-channel-configuration.ts        | 283 ++++++++++++++
 packages/@aws-cdk/aws-chatbot/package.json    |  11 +-
 .../@aws-cdk/aws-chatbot/test/chatbot.test.ts |   6 -
 .../test/integ.chatbot.expected.json          | 170 +++++++++
 .../aws-chatbot/test/integ.chatbot.ts         |  35 ++
 .../test/slack-channel-configuration.test.ts  | 361 ++++++++++++++++++
 7 files changed, 859 insertions(+), 8 deletions(-)
 create mode 100644 packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
 delete mode 100644 packages/@aws-cdk/aws-chatbot/test/chatbot.test.ts
 create mode 100644 packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json
 create mode 100644 packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
 create mode 100644 packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts

diff --git a/packages/@aws-cdk/aws-chatbot/lib/index.ts b/packages/@aws-cdk/aws-chatbot/lib/index.ts
index 312fee0796b78..da60385a5ce93 100644
--- a/packages/@aws-cdk/aws-chatbot/lib/index.ts
+++ b/packages/@aws-cdk/aws-chatbot/lib/index.ts
@@ -1,2 +1,3 @@
 // AWS::Chatbot CloudFormation Resources:
 export * from './chatbot.generated';
+export * from './slack-channel-configuration';
diff --git a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
new file mode 100644
index 0000000000000..0e04d45306df5
--- /dev/null
+++ b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
@@ -0,0 +1,283 @@
+import * as iam from '@aws-cdk/aws-iam';
+import * as sns from '@aws-cdk/aws-sns';
+import * as cdk from '@aws-cdk/core';
+import { CfnSlackChannelConfiguration } from './chatbot.generated';
+
+/**
+ * Properties for a new Slack channel configuration
+ */
+export interface SlackChannelConfigurationProps {
+
+  /**
+   * The name of Slack channel configuration
+   */
+  readonly slackChannelConfigurationName: string;
+
+  /**
+   * The permission role of Slack channel configuration
+   *
+   * @default - A role will be created.
+   */
+  readonly configurationRole?: iam.IRole;
+
+  /**
+   * The ID of the Slack workspace authorized with AWS Chatbot.
+   *
+   * To get the workspace ID, you must perform the initial authorization flow with Slack in the AWS Chatbot console.
+   * Then you can copy and paste the workspace ID from the console.
+   * For more details, see steps 1-4 in Setting Up AWS Chatbot with Slack in the AWS Chatbot User Guide.
+   * @see https://docs.aws.amazon.com/chatbot/latest/adminguide/setting-up.html#Setup_intro
+   */
+  readonly slackWorkspaceId: string;
+
+  /**
+   * The ID of the Slack channel.
+   *
+   * To get the ID, open Slack, right click on the channel name in the left pane, then choose Copy Link.
+   * The channel ID is the 9-character string at the end of the URL. For example, ABCBBLZZZ.
+   */
+  readonly slackChannelId: string;
+
+  /**
+   * The SNS topics that deliver notifications to AWS Chatbot.
+   *
+   * @default None
+   */
+  readonly notificationTopics?: sns.ITopic[];
+
+  /**
+   * Specifies the logging level for this configuration.
+   * This property affects the log entries pushed to Amazon CloudWatch Logs.
+   *
+   * @default LoggingLevel.NONE
+   */
+  readonly loggingLevel?: LoggingLevel;
+}
+
+/**
+ * Logging levels include ERROR, INFO, or NONE.
+ */
+export enum LoggingLevel {
+  /**
+   * ERROR
+   */
+  ERROR = 'ERROR',
+
+  /**
+   * INFO
+   */
+  INFO = 'INFO',
+
+  /**
+   * NONE
+   */
+  NONE = 'NONE',
+}
+
+/**
+ * Represents a Slack channel configuration
+ */
+export interface ISlackChannelConfiguration extends cdk.IResource {
+
+  /**
+   * The ARN of the Slack channel configuration
+   * @attribute
+   */
+  readonly slackChannelConfigurationArn: string;
+
+  /**
+   * The name of Slack channel configuration
+   * @attribute
+   */
+  readonly configurationName: string;
+
+  /**
+   * The permission role of Slack channel configuration
+   * @attribute
+   *
+   * @default - A role will be created.
+   */
+  readonly configurationRole?: iam.IRole;
+}
+
+/**
+ * Either a new or imported Slack channel configuration
+ */
+abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISlackChannelConfiguration {
+  abstract readonly slackChannelConfigurationArn: string;
+
+  abstract readonly configurationName: string;
+
+  abstract readonly configurationRole?: iam.IRole;
+
+  /**
+   * Adds extra permission to iam-role of Slack channel configuration
+   * @param statement
+   */
+  public addToPrincipalPolicy(statement: iam.PolicyStatement): void {
+    if (!this.configurationRole) {
+      return;
+    }
+
+    this.configurationRole!.addToPrincipalPolicy(statement);
+  }
+
+  /**
+   * Allows AWS chatbot to retrieve metric graphs from AWS CloudWatch.
+   */
+  public addNotificationPermissions(): void {
+    this.configurationRole!.addManagedPolicy(new iam.ManagedPolicy(this, 'NotificationsOnlyPolicy', {
+      managedPolicyName: 'AWS-Chatbot-NotificationsOnly-Policy',
+      description: 'NotificationsOnly policy for AWS Chatbot',
+      statements: [
+        new iam.PolicyStatement({
+          effect: iam.Effect.ALLOW,
+          actions: [
+            'cloudwatch:Describe*',
+            'cloudwatch:Get*',
+            'cloudwatch:List*',
+          ],
+          resources: ['*'],
+        }),
+      ],
+    }));
+  }
+
+  /**
+   * Allows read-only commands in supported clients.
+   */
+  public addReadOnlyCommandPermissions(): void {
+    this.configurationRole!.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess'));
+
+    this.configurationRole!.addManagedPolicy(new iam.ManagedPolicy(this, 'ReadonlyCommandsPolicy', {
+      managedPolicyName: 'AWS-Chatbot-ReadonlyCommands',
+      description: 'ReadonlyCommands policy for AWS Chatbot',
+      statements: [
+        new iam.PolicyStatement({
+          effect: iam.Effect.DENY,
+          actions: [
+            'iam:*',
+            's3:GetBucketPolicy',
+            'ssm:*',
+            'sts:*',
+            'kms:*',
+            'cognito-idp:GetSigningCertificate',
+            'ec2:GetPasswordData',
+            'ecr:GetAuthorizationToken',
+            'gamelift:RequestUploadCredentials',
+            'gamelift:GetInstanceAccess',
+            'lightsail:DownloadDefaultKeyPair',
+            'lightsail:GetInstanceAccessDetails',
+            'lightsail:GetKeyPair',
+            'lightsail:GetKeyPairs',
+            'redshift:GetClusterCredentials',
+            'storagegateway:DescribeChapCredentials',
+          ],
+          resources: ['*'],
+        }),
+      ],
+    }));
+  }
+
+  /**
+   * Allows Lambda-invoke commands in supported clients.
+   */
+  public addLambdaInvokeCommandPermissions(): void {
+    this.configurationRole!.addManagedPolicy(new iam.ManagedPolicy(this, 'LambdaInvokePolicy', {
+      managedPolicyName: 'AWS-Chatbot-LambdaInvoke-Policy',
+      description: 'LambdaInvoke policy for AWS Chatbot',
+      statements: [
+        new iam.PolicyStatement({
+          effect: iam.Effect.ALLOW,
+          actions: [
+            'lambda:invokeAsync',
+            'lambda:invokeFunction',
+          ],
+          resources: ['*'],
+        }),
+      ],
+    }));
+  }
+
+  /**
+   * Allows calling AWS Support APIs in supported clients.
+   */
+  public addSupportCommandPermissions(): void {
+    this.configurationRole!.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AWSSupportAccess'));
+  }
+}
+
+/**
+ * A new Slack channel configuration
+ */
+export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
+
+  /**
+   * Import an existing Slack channel configuration provided an ARN
+   * @param scope The parent creating construct
+   * @param id The construct's name
+   * @param slackChannelConfigurationArn configuration ARN (i.e. arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack)
+   *
+   * @returns a reference to the existing Slack channel configuration
+   */
+  public static fromSlackChannelConfigurationArn(scope: cdk.Construct, id: string, slackChannelConfigurationArn: string): ISlackChannelConfiguration {
+    class Import extends SlackChannelConfigurationBase {
+
+      /**
+       * @attribute
+       */
+      readonly slackChannelConfigurationArn = slackChannelConfigurationArn;
+      readonly configurationRole?: iam.IRole = undefined;
+
+      /**
+       * For example: arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack
+       * The ArnComponents API will return `slack-channel/my-slack`
+       * So i need to handle that to gets a correct name.`my-slack`
+       */
+      readonly configurationName = (() => {
+        const resourceName = cdk
+          .Stack
+          .of(scope)
+          .parseArn(slackChannelConfigurationArn)
+          .resourceName as string;
+
+        return resourceName.replace(/^slack-channel\//, '');
+      })();
+    }
+
+    return new Import(scope, id);
+  }
+
+  readonly slackChannelConfigurationArn: string;
+
+  readonly configurationName: string;
+
+  readonly configurationRole?: iam.IRole;
+
+  constructor(scope: cdk.Construct, id: string, props: SlackChannelConfigurationProps) {
+    super(scope, id, {
+      physicalName: props.slackChannelConfigurationName,
+    });
+
+    this.configurationRole = props.configurationRole || new iam.Role(this, 'ConfigurationRole', {
+      assumedBy: new iam.ServicePrincipal('chatbot.amazonaws.com'),
+    });
+
+    const topicArns = !!props.notificationTopics
+      ? props.notificationTopics.map((topic) => topic.topicArn)
+      : undefined;
+
+    const configuration = new CfnSlackChannelConfiguration(this, 'Resource', {
+      configurationName: props.slackChannelConfigurationName,
+      iamRoleArn: this.configurationRole.roleArn,
+      slackWorkspaceId: props.slackWorkspaceId,
+      slackChannelId: props.slackChannelId,
+      snsTopicArns: topicArns,
+      loggingLevel: props.loggingLevel || LoggingLevel.NONE,
+    });
+
+    this.slackChannelConfigurationArn = configuration.ref;
+    this.configurationName = props.slackChannelConfigurationName;
+  }
+}
+
diff --git a/packages/@aws-cdk/aws-chatbot/package.json b/packages/@aws-cdk/aws-chatbot/package.json
index a2e805d6dea09..63dafe33c7c9f 100644
--- a/packages/@aws-cdk/aws-chatbot/package.json
+++ b/packages/@aws-cdk/aws-chatbot/package.json
@@ -67,14 +67,21 @@
   "devDependencies": {
     "@aws-cdk/assert": "0.0.0",
     "cdk-build-tools": "0.0.0",
+    "cdk-integ-tools": "0.0.0",
     "cfn2ts": "0.0.0",
     "pkglint": "0.0.0"
   },
   "dependencies": {
-    "@aws-cdk/core": "0.0.0"
+    "@aws-cdk/aws-iam": "0.0.0",
+    "@aws-cdk/aws-sns": "0.0.0",
+    "@aws-cdk/core": "0.0.0",
+    "constructs": "^3.0.2"
   },
   "peerDependencies": {
-    "@aws-cdk/core": "0.0.0"
+    "@aws-cdk/aws-iam": "0.0.0",
+    "@aws-cdk/aws-sns": "0.0.0",
+    "@aws-cdk/core": "0.0.0",
+    "constructs": "^3.0.2"
   },
   "engines": {
     "node": ">= 10.13.0 <13 || >=13.7.0"
diff --git a/packages/@aws-cdk/aws-chatbot/test/chatbot.test.ts b/packages/@aws-cdk/aws-chatbot/test/chatbot.test.ts
deleted file mode 100644
index e394ef336bfb4..0000000000000
--- a/packages/@aws-cdk/aws-chatbot/test/chatbot.test.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import '@aws-cdk/assert/jest';
-import {} from '../lib';
-
-test('No tests are specified for this package', () => {
-  expect(true).toBe(true);
-});
diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json
new file mode 100644
index 0000000000000..67297d57e4f3f
--- /dev/null
+++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json
@@ -0,0 +1,170 @@
+{
+  "Resources": {
+    "MySlackChannelConfigurationRole1D3F23AE": {
+      "Type": "AWS::IAM::Role",
+      "Properties": {
+        "AssumeRolePolicyDocument": {
+          "Statement": [
+            {
+              "Action": "sts:AssumeRole",
+              "Effect": "Allow",
+              "Principal": {
+                "Service": "chatbot.amazonaws.com"
+              }
+            }
+          ],
+          "Version": "2012-10-17"
+        },
+        "ManagedPolicyArns": [
+          {
+            "Ref": "MySlackChannelLambdaInvokePolicy276508B8"
+          },
+          {
+            "Ref": "MySlackChannelNotificationsOnlyPolicy54A7013D"
+          },
+          {
+            "Fn::Join": [
+              "",
+              [
+                "arn:",
+                {
+                  "Ref": "AWS::Partition"
+                },
+                ":iam::aws:policy/AWSSupportAccess"
+              ]
+            ]
+          },
+          {
+            "Fn::Join": [
+              "",
+              [
+                "arn:",
+                {
+                  "Ref": "AWS::Partition"
+                },
+                ":iam::aws:policy/ReadOnlyAccess"
+              ]
+            ]
+          },
+          {
+            "Ref": "MySlackChannelReadonlyCommandsPolicyD69F9CE1"
+          }
+        ]
+      }
+    },
+    "MySlackChannelConfigurationRoleDefaultPolicyE4C1FA62": {
+      "Type": "AWS::IAM::Policy",
+      "Properties": {
+        "PolicyDocument": {
+          "Statement": [
+            {
+              "Action": "s3:GetObject",
+              "Effect": "Allow",
+              "Resource": "arn:aws:s3:::abc/xyz/123.txt"
+            }
+          ],
+          "Version": "2012-10-17"
+        },
+        "PolicyName": "MySlackChannelConfigurationRoleDefaultPolicyE4C1FA62",
+        "Roles": [
+          {
+            "Ref": "MySlackChannelConfigurationRole1D3F23AE"
+          }
+        ]
+      }
+    },
+    "MySlackChannelA8E0B56C": {
+      "Type": "AWS::Chatbot::SlackChannelConfiguration",
+      "Properties": {
+        "ConfigurationName": "test-channel",
+        "IamRoleArn": {
+          "Fn::GetAtt": [
+            "MySlackChannelConfigurationRole1D3F23AE",
+            "Arn"
+          ]
+        },
+        "SlackChannelId": "C0187JABUE9",
+        "SlackWorkspaceId": "T49239U4W",
+        "LoggingLevel": "NONE"
+      }
+    },
+    "MySlackChannelLambdaInvokePolicy276508B8": {
+      "Type": "AWS::IAM::ManagedPolicy",
+      "Properties": {
+        "PolicyDocument": {
+          "Statement": [
+            {
+              "Action": [
+                "lambda:invokeAsync",
+                "lambda:invokeFunction"
+              ],
+              "Effect": "Allow",
+              "Resource": "*"
+            }
+          ],
+          "Version": "2012-10-17"
+        },
+        "Description": "LambdaInvoke policy for AWS Chatbot",
+        "ManagedPolicyName": "AWS-Chatbot-LambdaInvoke-Policy",
+        "Path": "/"
+      }
+    },
+    "MySlackChannelNotificationsOnlyPolicy54A7013D": {
+      "Type": "AWS::IAM::ManagedPolicy",
+      "Properties": {
+        "PolicyDocument": {
+          "Statement": [
+            {
+              "Action": [
+                "cloudwatch:Describe*",
+                "cloudwatch:Get*",
+                "cloudwatch:List*"
+              ],
+              "Effect": "Allow",
+              "Resource": "*"
+            }
+          ],
+          "Version": "2012-10-17"
+        },
+        "Description": "NotificationsOnly policy for AWS Chatbot",
+        "ManagedPolicyName": "AWS-Chatbot-NotificationsOnly-Policy",
+        "Path": "/"
+      }
+    },
+    "MySlackChannelReadonlyCommandsPolicyD69F9CE1": {
+      "Type": "AWS::IAM::ManagedPolicy",
+      "Properties": {
+        "PolicyDocument": {
+          "Statement": [
+            {
+              "Action": [
+                "iam:*",
+                "s3:GetBucketPolicy",
+                "ssm:*",
+                "sts:*",
+                "kms:*",
+                "cognito-idp:GetSigningCertificate",
+                "ec2:GetPasswordData",
+                "ecr:GetAuthorizationToken",
+                "gamelift:RequestUploadCredentials",
+                "gamelift:GetInstanceAccess",
+                "lightsail:DownloadDefaultKeyPair",
+                "lightsail:GetInstanceAccessDetails",
+                "lightsail:GetKeyPair",
+                "lightsail:GetKeyPairs",
+                "redshift:GetClusterCredentials",
+                "storagegateway:DescribeChapCredentials"
+              ],
+              "Effect": "Deny",
+              "Resource": "*"
+            }
+          ],
+          "Version": "2012-10-17"
+        },
+        "Description": "ReadonlyCommands policy for AWS Chatbot",
+        "ManagedPolicyName": "AWS-Chatbot-ReadonlyCommands",
+        "Path": "/"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
new file mode 100644
index 0000000000000..c8e21c5d17c5e
--- /dev/null
+++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
@@ -0,0 +1,35 @@
+import * as iam from '@aws-cdk/aws-iam';
+import * as cdk from '@aws-cdk/core';
+import * as chatbot from '../lib';
+
+class ChatbotInteg extends cdk.Stack {
+  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
+    super(scope, id, props);
+
+    const slackChannel = new chatbot.SlackChannelConfiguration(this, 'MySlackChannel', {
+      slackChannelConfigurationName: 'test-channel',
+      slackWorkspaceId: 'T49239U4W', // modify to your slack workspace id
+      slackChannelId: 'C0187JABUE9',   // modify to your slack channel id
+    });
+
+    slackChannel.addLambdaInvokeCommandPermissions();
+    slackChannel.addNotificationPermissions();
+    slackChannel.addSupportCommandPermissions();
+    slackChannel.addReadOnlyCommandPermissions();
+
+    slackChannel.addToPrincipalPolicy(new iam.PolicyStatement({
+      effect: iam.Effect.ALLOW,
+      actions: [
+        's3:GetObject',
+      ],
+      resources: ['arn:aws:s3:::abc/xyz/123.txt'],
+    }));
+  }
+}
+
+const app = new cdk.App();
+
+new ChatbotInteg(app, 'ChatbotInteg');
+
+app.synth();
+
diff --git a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
new file mode 100644
index 0000000000000..fce23aa49ba4b
--- /dev/null
+++ b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
@@ -0,0 +1,361 @@
+import * as cdkAssert from '@aws-cdk/assert';
+import * as iam from '@aws-cdk/aws-iam';
+import * as sns from '@aws-cdk/aws-sns';
+import * as cdk from '@aws-cdk/core';
+import * as chatbot from '../lib';
+
+describe('slack channel configuration tests', () => {
+  test('new configuration with least properties', () => {
+    const stack = new cdk.Stack();
+
+    new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
+      slackWorkspaceId: 'ABC123',
+      slackChannelId: 'DEF456',
+      slackChannelConfigurationName: 'Test',
+    });
+
+    cdkAssert.expect(stack).toMatch({
+      Resources: {
+        MySlackChannelConfigurationRole1D3F23AE: {
+          Type: 'AWS::IAM::Role',
+          Properties: {
+            AssumeRolePolicyDocument: {
+              Statement: [
+                {
+                  Action: 'sts:AssumeRole',
+                  Effect: 'Allow',
+                  Principal: {
+                    Service: 'chatbot.amazonaws.com',
+                  },
+                },
+              ],
+              Version: '2012-10-17',
+            },
+          },
+        },
+        MySlackChannelA8E0B56C: {
+          Type: 'AWS::Chatbot::SlackChannelConfiguration',
+          Properties: {
+            ConfigurationName: 'Test',
+            IamRoleArn: {
+              'Fn::GetAtt': [
+                'MySlackChannelConfigurationRole1D3F23AE',
+                'Arn',
+              ],
+            },
+            SlackChannelId: 'DEF456',
+            SlackWorkspaceId: 'ABC123',
+            LoggingLevel: 'NONE',
+          },
+        },
+      },
+    });
+  });
+
+  test('new configuration with new sns topic', () => {
+    const stack = new cdk.Stack();
+    const topic = new sns.Topic(stack, 'MyTopic');
+
+    new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
+      slackWorkspaceId: 'ABC123',
+      slackChannelId: 'DEF456',
+      slackChannelConfigurationName: 'Test',
+      notificationTopics: [topic],
+    });
+
+    cdkAssert.expect(stack).toMatch({
+      Resources: {
+        MyTopic86869434: {
+          Type: 'AWS::SNS::Topic',
+        },
+        MySlackChannelConfigurationRole1D3F23AE: {
+          Type: 'AWS::IAM::Role',
+          Properties: {
+            AssumeRolePolicyDocument: {
+              Statement: [
+                {
+                  Action: 'sts:AssumeRole',
+                  Effect: 'Allow',
+                  Principal: {
+                    Service: 'chatbot.amazonaws.com',
+                  },
+                },
+              ],
+              Version: '2012-10-17',
+            },
+          },
+        },
+        MySlackChannelA8E0B56C: {
+          Type: 'AWS::Chatbot::SlackChannelConfiguration',
+          Properties: {
+            ConfigurationName: 'Test',
+            IamRoleArn: {
+              'Fn::GetAtt': [
+                'MySlackChannelConfigurationRole1D3F23AE',
+                'Arn',
+              ],
+            },
+            SlackChannelId: 'DEF456',
+            SlackWorkspaceId: 'ABC123',
+            LoggingLevel: 'NONE',
+            SnsTopicArns: [
+              {
+                Ref: 'MyTopic86869434',
+              },
+            ],
+          },
+        },
+      },
+    });
+  });
+
+  test('new configuration with existing role', () => {
+    const stack = new cdk.Stack();
+    const role = iam.Role.fromRoleArn(stack, 'Role', 'arn:aws:iam:::role/test-role');
+
+    new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
+      slackWorkspaceId: 'ABC123',
+      slackChannelId: 'DEF456',
+      slackChannelConfigurationName: 'Test',
+      configurationRole: role,
+    });
+
+    cdkAssert.expect(stack).to(cdkAssert.countResources('AWS::IAM::Role', 0));
+  });
+
+  test('new configuration with new role and add notification permissions', () => {
+    const stack = new cdk.Stack();
+
+    const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
+      slackWorkspaceId: 'ABC123',
+      slackChannelId: 'DEF456',
+      slackChannelConfigurationName: 'Test',
+    });
+
+    slackChannel.addNotificationPermissions();
+
+    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::ManagedPolicy', {
+      PolicyDocument: {
+        Statement: [
+          {
+            Action: [
+              'cloudwatch:Describe*',
+              'cloudwatch:Get*',
+              'cloudwatch:List*',
+            ],
+            Effect: 'Allow',
+            Resource: '*',
+          },
+        ],
+        Version: '2012-10-17',
+      },
+      Description: 'NotificationsOnly policy for AWS Chatbot',
+      ManagedPolicyName: 'AWS-Chatbot-NotificationsOnly-Policy',
+      Path: '/',
+    }));
+  });
+
+  test('new configuration with new role and add read-only command permissions', () => {
+    const stack = new cdk.Stack();
+
+    const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
+      slackWorkspaceId: 'ABC123',
+      slackChannelId: 'DEF456',
+      slackChannelConfigurationName: 'Test',
+    });
+
+    slackChannel.addReadOnlyCommandPermissions();
+
+    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::Role', {
+      AssumeRolePolicyDocument: {
+        Statement: [
+          {
+            Action: 'sts:AssumeRole',
+            Effect: 'Allow',
+            Principal: {
+              Service: 'chatbot.amazonaws.com',
+            },
+          },
+        ],
+        Version: '2012-10-17',
+      },
+      ManagedPolicyArns: [
+        {
+          'Fn::Join': [
+            '',
+            [
+              'arn:',
+              {
+                Ref: 'AWS::Partition',
+              },
+              ':iam::aws:policy/ReadOnlyAccess',
+            ],
+          ],
+        },
+        {
+          Ref: 'MySlackChannelReadonlyCommandsPolicyD69F9CE1',
+        },
+      ],
+    }));
+
+    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::ManagedPolicy', {
+      PolicyDocument: {
+        Statement: [
+          {
+            Action: [
+              'iam:*',
+              's3:GetBucketPolicy',
+              'ssm:*',
+              'sts:*',
+              'kms:*',
+              'cognito-idp:GetSigningCertificate',
+              'ec2:GetPasswordData',
+              'ecr:GetAuthorizationToken',
+              'gamelift:RequestUploadCredentials',
+              'gamelift:GetInstanceAccess',
+              'lightsail:DownloadDefaultKeyPair',
+              'lightsail:GetInstanceAccessDetails',
+              'lightsail:GetKeyPair',
+              'lightsail:GetKeyPairs',
+              'redshift:GetClusterCredentials',
+              'storagegateway:DescribeChapCredentials',
+            ],
+            Effect: 'Deny',
+            Resource: '*',
+          },
+        ],
+        Version: '2012-10-17',
+      },
+      Description: 'ReadonlyCommands policy for AWS Chatbot',
+      ManagedPolicyName: 'AWS-Chatbot-ReadonlyCommands',
+      Path: '/',
+    }));
+  });
+
+  test('new configuration with new role and add lambda invoke command permissions', () => {
+    const stack = new cdk.Stack();
+
+    const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
+      slackWorkspaceId: 'ABC123',
+      slackChannelId: 'DEF456',
+      slackChannelConfigurationName: 'Test',
+    });
+
+    slackChannel.addLambdaInvokeCommandPermissions();
+
+    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::ManagedPolicy', {
+      PolicyDocument: {
+        Statement: [
+          {
+            Action: [
+              'lambda:invokeAsync',
+              'lambda:invokeFunction',
+            ],
+            Effect: 'Allow',
+            Resource: '*',
+          },
+        ],
+        Version: '2012-10-17',
+      },
+      Description: 'LambdaInvoke policy for AWS Chatbot',
+      ManagedPolicyName: 'AWS-Chatbot-LambdaInvoke-Policy',
+      Path: '/',
+    }));
+  });
+
+  test('new configuration with new role and add support command permissions', () => {
+    const stack = new cdk.Stack();
+
+    const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
+      slackWorkspaceId: 'ABC123',
+      slackChannelId: 'DEF456',
+      slackChannelConfigurationName: 'Test',
+    });
+
+    slackChannel.addSupportCommandPermissions();
+
+    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::Role', {
+      AssumeRolePolicyDocument: {
+        Statement: [
+          {
+            Action: 'sts:AssumeRole',
+            Effect: 'Allow',
+            Principal: {
+              Service: 'chatbot.amazonaws.com',
+            },
+          },
+        ],
+        Version: '2012-10-17',
+      },
+      ManagedPolicyArns: [
+        {
+          'Fn::Join': [
+            '',
+            [
+              'arn:',
+              {
+                Ref: 'AWS::Partition',
+              },
+              ':iam::aws:policy/AWSSupportAccess',
+            ],
+          ],
+        },
+      ],
+    }));
+  });
+
+  test('new configuration with new role and extra iam policies', () => {
+    const stack = new cdk.Stack();
+
+    const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
+      slackWorkspaceId: 'ABC123',
+      slackChannelId: 'DEF456',
+      slackChannelConfigurationName: 'Test',
+    });
+
+    slackChannel.addToPrincipalPolicy(new iam.PolicyStatement({
+      effect: iam.Effect.ALLOW,
+      actions: [
+        's3:GetObject',
+      ],
+      resources: ['arn:aws:s3:::abc/xyz/123.txt'],
+    }));
+
+    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::Policy', {
+      PolicyDocument: {
+        Statement: [
+          {
+            Action: 's3:GetObject',
+            Effect: 'Allow',
+            Resource: 'arn:aws:s3:::abc/xyz/123.txt',
+          },
+        ],
+        Version: '2012-10-17',
+      },
+    }));
+  });
+
+  test('added a iam policy to a from slack channel configuration ARN will nothing to do', () => {
+    const stack = new cdk.Stack();
+    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+
+    (imported as chatbot.SlackChannelConfiguration).addToPrincipalPolicy(new iam.PolicyStatement({
+      effect: iam.Effect.ALLOW,
+      actions: [
+        's3:GetObject',
+      ],
+      resources: ['arn:aws:s3:::abc/xyz/123.txt'],
+    }));
+
+    cdkAssert.expect(stack).to(cdkAssert.countResources('AWS::IAM::Role', 0));
+    cdkAssert.expect(stack).to(cdkAssert.countResources('AWS::IAM::Policy', 0));
+  });
+
+  test('from slack channel configuration ARN', () => {
+    const stack = new cdk.Stack();
+    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+
+    expect(imported.configurationName).toEqual('my-slack');
+    expect(imported.slackChannelConfigurationArn).toEqual('arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+  });
+});
\ No newline at end of file

From a42112af7ee1fdfad9d8c6d29ee071df5ad0b250 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Fri, 14 Aug 2020 23:05:12 +0800
Subject: [PATCH 02/13] update README

---
 packages/@aws-cdk/aws-chatbot/README.md | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/packages/@aws-cdk/aws-chatbot/README.md b/packages/@aws-cdk/aws-chatbot/README.md
index a1fe64575ebf8..83d6afcef7773 100644
--- a/packages/@aws-cdk/aws-chatbot/README.md
+++ b/packages/@aws-cdk/aws-chatbot/README.md
@@ -9,8 +9,29 @@
 ---
 <!--END STABILITY BANNER-->
 
+AWS Chatbot is an AWS service that enables DevOps and software development teams to use Slack chat rooms to monitor and respond to operational events in their AWS Cloud. AWS Chatbot processes AWS service notifications from Amazon Simple Notification Service (Amazon SNS), and forwards them to Slack chat rooms so teams can analyze and act on them immediately, regardless of location.
+
 This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project.
 
 ```ts
 import * as chatbot from '@aws-cdk/aws-chatbot';
+
+const slackChannel = new chatbot.SlackChannelConfiguration(this, 'MySlackChannel', {
+  slackChannelConfigurationName: 'YOUR_CHANNEL_NAME',
+  slackWorkspaceId: 'YOUR_SLACK_WORKSPACE_ID',
+  slackChannelId: 'YOUR_SLACK_CHANNEL_ID',
+});
+
+slackChannel.addLambdaInvokeCommandPermissions();
+slackChannel.addNotificationPermissions();
+slackChannel.addSupportCommandPermissions();
+slackChannel.addReadOnlyCommandPermissions();
+
+slackChannel.addToPrincipalPolicy(new iam.PolicyStatement({
+  effect: iam.Effect.ALLOW,
+  actions: [
+    's3:GetObject',
+  ],
+  resources: ['arn:aws:s3:::abc/xyz/123.txt'],
+}));
 ```

From c60d82d056160d28af1ed0830aeabba5dfa7fdc7 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Sat, 15 Aug 2020 22:50:37 +0800
Subject: [PATCH 03/13] rename configurationRole to role

---
 .../@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts   | 4 ++--
 .../aws-chatbot/test/slack-channel-configuration.test.ts      | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
index 0e04d45306df5..b2b6622c17e7d 100644
--- a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
+++ b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
@@ -18,7 +18,7 @@ export interface SlackChannelConfigurationProps {
    *
    * @default - A role will be created.
    */
-  readonly configurationRole?: iam.IRole;
+  readonly role?: iam.IRole;
 
   /**
    * The ID of the Slack workspace authorized with AWS Chatbot.
@@ -259,7 +259,7 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
       physicalName: props.slackChannelConfigurationName,
     });
 
-    this.configurationRole = props.configurationRole || new iam.Role(this, 'ConfigurationRole', {
+    this.configurationRole = props.role || new iam.Role(this, 'ConfigurationRole', {
       assumedBy: new iam.ServicePrincipal('chatbot.amazonaws.com'),
     });
 
diff --git a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
index fce23aa49ba4b..2c4c79e67a98e 100644
--- a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
@@ -117,7 +117,7 @@ describe('slack channel configuration tests', () => {
       slackWorkspaceId: 'ABC123',
       slackChannelId: 'DEF456',
       slackChannelConfigurationName: 'Test',
-      configurationRole: role,
+      role: role,
     });
 
     cdkAssert.expect(stack).to(cdkAssert.countResources('AWS::IAM::Role', 0));

From d94a1c8f1847ae764d8216f7dd623d39670ca128 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Sat, 15 Aug 2020 22:58:47 +0800
Subject: [PATCH 04/13] remove the double !

---
 .../@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
index b2b6622c17e7d..e9b4ab504b8dc 100644
--- a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
+++ b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
@@ -263,7 +263,7 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
       assumedBy: new iam.ServicePrincipal('chatbot.amazonaws.com'),
     });
 
-    const topicArns = !!props.notificationTopics
+    const topicArns = props.notificationTopics
       ? props.notificationTopics.map((topic) => topic.topicArn)
       : undefined;
 

From 1af27b56937d8538900eeddd539b9e9dff53f9a2 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Sat, 15 Aug 2020 23:11:58 +0800
Subject: [PATCH 05/13] rename configurationRole to role

---
 .../lib/slack-channel-configuration.ts        | 26 +++++++++----------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
index e9b4ab504b8dc..e7be7540e1746 100644
--- a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
+++ b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
@@ -97,7 +97,7 @@ export interface ISlackChannelConfiguration extends cdk.IResource {
    *
    * @default - A role will be created.
    */
-  readonly configurationRole?: iam.IRole;
+  readonly role?: iam.IRole;
 }
 
 /**
@@ -108,25 +108,25 @@ abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISl
 
   abstract readonly configurationName: string;
 
-  abstract readonly configurationRole?: iam.IRole;
+  abstract readonly role?: iam.IRole;
 
   /**
    * Adds extra permission to iam-role of Slack channel configuration
    * @param statement
    */
   public addToPrincipalPolicy(statement: iam.PolicyStatement): void {
-    if (!this.configurationRole) {
+    if (!this.role) {
       return;
     }
 
-    this.configurationRole!.addToPrincipalPolicy(statement);
+    this.role!.addToPrincipalPolicy(statement);
   }
 
   /**
    * Allows AWS chatbot to retrieve metric graphs from AWS CloudWatch.
    */
   public addNotificationPermissions(): void {
-    this.configurationRole!.addManagedPolicy(new iam.ManagedPolicy(this, 'NotificationsOnlyPolicy', {
+    this.role!.addManagedPolicy(new iam.ManagedPolicy(this, 'NotificationsOnlyPolicy', {
       managedPolicyName: 'AWS-Chatbot-NotificationsOnly-Policy',
       description: 'NotificationsOnly policy for AWS Chatbot',
       statements: [
@@ -147,9 +147,9 @@ abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISl
    * Allows read-only commands in supported clients.
    */
   public addReadOnlyCommandPermissions(): void {
-    this.configurationRole!.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess'));
+    this.role!.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess'));
 
-    this.configurationRole!.addManagedPolicy(new iam.ManagedPolicy(this, 'ReadonlyCommandsPolicy', {
+    this.role!.addManagedPolicy(new iam.ManagedPolicy(this, 'ReadonlyCommandsPolicy', {
       managedPolicyName: 'AWS-Chatbot-ReadonlyCommands',
       description: 'ReadonlyCommands policy for AWS Chatbot',
       statements: [
@@ -183,7 +183,7 @@ abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISl
    * Allows Lambda-invoke commands in supported clients.
    */
   public addLambdaInvokeCommandPermissions(): void {
-    this.configurationRole!.addManagedPolicy(new iam.ManagedPolicy(this, 'LambdaInvokePolicy', {
+    this.role!.addManagedPolicy(new iam.ManagedPolicy(this, 'LambdaInvokePolicy', {
       managedPolicyName: 'AWS-Chatbot-LambdaInvoke-Policy',
       description: 'LambdaInvoke policy for AWS Chatbot',
       statements: [
@@ -203,7 +203,7 @@ abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISl
    * Allows calling AWS Support APIs in supported clients.
    */
   public addSupportCommandPermissions(): void {
-    this.configurationRole!.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AWSSupportAccess'));
+    this.role!.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AWSSupportAccess'));
   }
 }
 
@@ -227,7 +227,7 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
        * @attribute
        */
       readonly slackChannelConfigurationArn = slackChannelConfigurationArn;
-      readonly configurationRole?: iam.IRole = undefined;
+      readonly role?: iam.IRole = undefined;
 
       /**
        * For example: arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack
@@ -252,14 +252,14 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
 
   readonly configurationName: string;
 
-  readonly configurationRole?: iam.IRole;
+  readonly role?: iam.IRole;
 
   constructor(scope: cdk.Construct, id: string, props: SlackChannelConfigurationProps) {
     super(scope, id, {
       physicalName: props.slackChannelConfigurationName,
     });
 
-    this.configurationRole = props.role || new iam.Role(this, 'ConfigurationRole', {
+    this.role = props.role || new iam.Role(this, 'ConfigurationRole', {
       assumedBy: new iam.ServicePrincipal('chatbot.amazonaws.com'),
     });
 
@@ -269,7 +269,7 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
 
     const configuration = new CfnSlackChannelConfiguration(this, 'Resource', {
       configurationName: props.slackChannelConfigurationName,
-      iamRoleArn: this.configurationRole.roleArn,
+      iamRoleArn: this.role.roleArn,
       slackWorkspaceId: props.slackWorkspaceId,
       slackChannelId: props.slackChannelId,
       snsTopicArns: topicArns,

From eda9a7104a449300d46dd3535ce62a05987af0f7 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Sun, 16 Aug 2020 00:14:01 +0800
Subject: [PATCH 06/13] Adjust for default value and some tests 1. change
 default `loggingLevel` from `LoggingLevel.NONE` to `undefined`. 2. extract
 `new cdk.Stack()` to a `beforeEach()`. 3. change from `toMatch` to
 `haveResourceLike` for unit test asserting method. 4. add a test case to
 increase code coverage.

---
 .../lib/slack-channel-configuration.ts        |   2 +-
 .../aws-chatbot/test/integ.chatbot.ts         |   1 +
 .../test/slack-channel-configuration.test.ts  | 170 ++++++++----------
 3 files changed, 74 insertions(+), 99 deletions(-)

diff --git a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
index e7be7540e1746..0cadbfcbebaa9 100644
--- a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
+++ b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
@@ -273,7 +273,7 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
       slackWorkspaceId: props.slackWorkspaceId,
       slackChannelId: props.slackChannelId,
       snsTopicArns: topicArns,
-      loggingLevel: props.loggingLevel || LoggingLevel.NONE,
+      loggingLevel: props.loggingLevel?.toString(),
     });
 
     this.slackChannelConfigurationArn = configuration.ref;
diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
index c8e21c5d17c5e..324a591068569 100644
--- a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
@@ -10,6 +10,7 @@ class ChatbotInteg extends cdk.Stack {
       slackChannelConfigurationName: 'test-channel',
       slackWorkspaceId: 'T49239U4W', // modify to your slack workspace id
       slackChannelId: 'C0187JABUE9',   // modify to your slack channel id
+      loggingLevel: chatbot.LoggingLevel.NONE,
     });
 
     slackChannel.addLambdaInvokeCommandPermissions();
diff --git a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
index 2c4c79e67a98e..81e39ca452162 100644
--- a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
@@ -5,55 +5,70 @@ import * as cdk from '@aws-cdk/core';
 import * as chatbot from '../lib';
 
 describe('slack channel configuration tests', () => {
-  test('new configuration with least properties', () => {
-    const stack = new cdk.Stack();
+  let stack: cdk.Stack;
 
+  beforeEach(() => {
+    stack = new cdk.Stack();
+  });
+
+  test('created with minimal properties creates a new IAM Role', () => {
     new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
       slackWorkspaceId: 'ABC123',
       slackChannelId: 'DEF456',
       slackChannelConfigurationName: 'Test',
     });
 
-    cdkAssert.expect(stack).toMatch({
-      Resources: {
-        MySlackChannelConfigurationRole1D3F23AE: {
-          Type: 'AWS::IAM::Role',
-          Properties: {
-            AssumeRolePolicyDocument: {
-              Statement: [
-                {
-                  Action: 'sts:AssumeRole',
-                  Effect: 'Allow',
-                  Principal: {
-                    Service: 'chatbot.amazonaws.com',
-                  },
-                },
-              ],
-              Version: '2012-10-17',
-            },
-          },
-        },
-        MySlackChannelA8E0B56C: {
-          Type: 'AWS::Chatbot::SlackChannelConfiguration',
-          Properties: {
-            ConfigurationName: 'Test',
-            IamRoleArn: {
-              'Fn::GetAtt': [
-                'MySlackChannelConfigurationRole1D3F23AE',
-                'Arn',
-              ],
+    cdkAssert.expect(stack).to(cdkAssert.haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
+      ConfigurationName: 'Test',
+      IamRoleArn: {
+        'Fn::GetAtt': [
+          'MySlackChannelConfigurationRole1D3F23AE',
+          'Arn',
+        ],
+      },
+      SlackChannelId: 'DEF456',
+      SlackWorkspaceId: 'ABC123',
+    }));
+
+    cdkAssert.expect(stack).to(cdkAssert.haveResourceLike('AWS::IAM::Role', {
+      AssumeRolePolicyDocument: {
+        Statement: [
+          {
+            Action: 'sts:AssumeRole',
+            Effect: 'Allow',
+            Principal: {
+              Service: 'chatbot.amazonaws.com',
             },
-            SlackChannelId: 'DEF456',
-            SlackWorkspaceId: 'ABC123',
-            LoggingLevel: 'NONE',
           },
-        },
+        ],
+        Version: '2012-10-17',
       },
+    }));
+  });
+
+  test('created and pass loggingLevel parameter [LoggingLevel.ERROR], it should be set [ERROR] logging level in Cloudformation', () => {
+    new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
+      slackWorkspaceId: 'ABC123',
+      slackChannelId: 'DEF456',
+      slackChannelConfigurationName: 'Test',
+      loggingLevel: chatbot.LoggingLevel.ERROR,
     });
+
+    cdkAssert.expect(stack).to(cdkAssert.haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
+      ConfigurationName: 'Test',
+      IamRoleArn: {
+        'Fn::GetAtt': [
+          'MySlackChannelConfigurationRole1D3F23AE',
+          'Arn',
+        ],
+      },
+      SlackChannelId: 'DEF456',
+      SlackWorkspaceId: 'ABC123',
+      LoggingLevel: 'ERROR',
+    }));
   });
 
-  test('new configuration with new sns topic', () => {
-    const stack = new cdk.Stack();
+  test('created with new sns topic', () => {
     const topic = new sns.Topic(stack, 'MyTopic');
 
     new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
@@ -63,54 +78,25 @@ describe('slack channel configuration tests', () => {
       notificationTopics: [topic],
     });
 
-    cdkAssert.expect(stack).toMatch({
-      Resources: {
-        MyTopic86869434: {
-          Type: 'AWS::SNS::Topic',
-        },
-        MySlackChannelConfigurationRole1D3F23AE: {
-          Type: 'AWS::IAM::Role',
-          Properties: {
-            AssumeRolePolicyDocument: {
-              Statement: [
-                {
-                  Action: 'sts:AssumeRole',
-                  Effect: 'Allow',
-                  Principal: {
-                    Service: 'chatbot.amazonaws.com',
-                  },
-                },
-              ],
-              Version: '2012-10-17',
-            },
-          },
-        },
-        MySlackChannelA8E0B56C: {
-          Type: 'AWS::Chatbot::SlackChannelConfiguration',
-          Properties: {
-            ConfigurationName: 'Test',
-            IamRoleArn: {
-              'Fn::GetAtt': [
-                'MySlackChannelConfigurationRole1D3F23AE',
-                'Arn',
-              ],
-            },
-            SlackChannelId: 'DEF456',
-            SlackWorkspaceId: 'ABC123',
-            LoggingLevel: 'NONE',
-            SnsTopicArns: [
-              {
-                Ref: 'MyTopic86869434',
-              },
-            ],
-          },
-        },
+    cdkAssert.expect(stack).to(cdkAssert.haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
+      ConfigurationName: 'Test',
+      IamRoleArn: {
+        'Fn::GetAtt': [
+          'MySlackChannelConfigurationRole1D3F23AE',
+          'Arn',
+        ],
       },
-    });
+      SlackChannelId: 'DEF456',
+      SlackWorkspaceId: 'ABC123',
+      SnsTopicArns: [
+        {
+          Ref: 'MyTopic86869434',
+        },
+      ],
+    }));
   });
 
-  test('new configuration with existing role', () => {
-    const stack = new cdk.Stack();
+  test('created with existing role', () => {
     const role = iam.Role.fromRoleArn(stack, 'Role', 'arn:aws:iam:::role/test-role');
 
     new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
@@ -123,9 +109,7 @@ describe('slack channel configuration tests', () => {
     cdkAssert.expect(stack).to(cdkAssert.countResources('AWS::IAM::Role', 0));
   });
 
-  test('new configuration with new role and add notification permissions', () => {
-    const stack = new cdk.Stack();
-
+  test('created with new role and add notification permissions', () => {
     const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
       slackWorkspaceId: 'ABC123',
       slackChannelId: 'DEF456',
@@ -155,9 +139,7 @@ describe('slack channel configuration tests', () => {
     }));
   });
 
-  test('new configuration with new role and add read-only command permissions', () => {
-    const stack = new cdk.Stack();
-
+  test('created with new role and add read-only command permissions', () => {
     const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
       slackWorkspaceId: 'ABC123',
       slackChannelId: 'DEF456',
@@ -232,9 +214,7 @@ describe('slack channel configuration tests', () => {
     }));
   });
 
-  test('new configuration with new role and add lambda invoke command permissions', () => {
-    const stack = new cdk.Stack();
-
+  test('created with new role and add lambda invoke command permissions', () => {
     const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
       slackWorkspaceId: 'ABC123',
       slackChannelId: 'DEF456',
@@ -263,9 +243,7 @@ describe('slack channel configuration tests', () => {
     }));
   });
 
-  test('new configuration with new role and add support command permissions', () => {
-    const stack = new cdk.Stack();
-
+  test('created with new role and add support command permissions', () => {
     const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
       slackWorkspaceId: 'ABC123',
       slackChannelId: 'DEF456',
@@ -304,9 +282,7 @@ describe('slack channel configuration tests', () => {
     }));
   });
 
-  test('new configuration with new role and extra iam policies', () => {
-    const stack = new cdk.Stack();
-
+  test('created with new role and extra iam policies', () => {
     const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
       slackWorkspaceId: 'ABC123',
       slackChannelId: 'DEF456',
@@ -336,7 +312,6 @@ describe('slack channel configuration tests', () => {
   });
 
   test('added a iam policy to a from slack channel configuration ARN will nothing to do', () => {
-    const stack = new cdk.Stack();
     const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
 
     (imported as chatbot.SlackChannelConfiguration).addToPrincipalPolicy(new iam.PolicyStatement({
@@ -352,7 +327,6 @@ describe('slack channel configuration tests', () => {
   });
 
   test('from slack channel configuration ARN', () => {
-    const stack = new cdk.Stack();
     const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
 
     expect(imported.configurationName).toEqual('my-slack');

From 07860173a3b4619eb3dc86a8164da6bd9a6411a3 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Sun, 16 Aug 2020 00:45:47 +0800
Subject: [PATCH 07/13] Adjust unit tests 1. remove all prefix of
 @aws-cdk/assert 2. test case `import` move to
 import-slack-channel-configuration.test

---
 ...import-slack-channel-configuration.test.ts | 15 ++++++++
 .../test/slack-channel-configuration.test.ts  | 35 ++++++++-----------
 2 files changed, 29 insertions(+), 21 deletions(-)
 create mode 100644 packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts

diff --git a/packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts
new file mode 100644
index 0000000000000..5d41a006f8032
--- /dev/null
+++ b/packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts
@@ -0,0 +1,15 @@
+import * as cdk from '@aws-cdk/core';
+import * as chatbot from '../lib';
+
+let stack: cdk.Stack;
+
+beforeEach(() => {
+  stack = new cdk.Stack();
+});
+
+test('from slack channel configuration ARN', () => {
+  const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+
+  expect(imported.configurationName).toEqual('my-slack');
+  expect(imported.slackChannelConfigurationArn).toEqual('arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+});
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
index 81e39ca452162..3747366f660d7 100644
--- a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
@@ -1,4 +1,4 @@
-import * as cdkAssert from '@aws-cdk/assert';
+import { expect, haveResourceLike, countResources } from '@aws-cdk/assert';
 import * as iam from '@aws-cdk/aws-iam';
 import * as sns from '@aws-cdk/aws-sns';
 import * as cdk from '@aws-cdk/core';
@@ -18,7 +18,7 @@ describe('slack channel configuration tests', () => {
       slackChannelConfigurationName: 'Test',
     });
 
-    cdkAssert.expect(stack).to(cdkAssert.haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
+    expect(stack).to(haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
       ConfigurationName: 'Test',
       IamRoleArn: {
         'Fn::GetAtt': [
@@ -30,7 +30,7 @@ describe('slack channel configuration tests', () => {
       SlackWorkspaceId: 'ABC123',
     }));
 
-    cdkAssert.expect(stack).to(cdkAssert.haveResourceLike('AWS::IAM::Role', {
+    expect(stack).to(haveResourceLike('AWS::IAM::Role', {
       AssumeRolePolicyDocument: {
         Statement: [
           {
@@ -54,7 +54,7 @@ describe('slack channel configuration tests', () => {
       loggingLevel: chatbot.LoggingLevel.ERROR,
     });
 
-    cdkAssert.expect(stack).to(cdkAssert.haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
+    expect(stack).to(haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
       ConfigurationName: 'Test',
       IamRoleArn: {
         'Fn::GetAtt': [
@@ -78,7 +78,7 @@ describe('slack channel configuration tests', () => {
       notificationTopics: [topic],
     });
 
-    cdkAssert.expect(stack).to(cdkAssert.haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
+    expect(stack).to(haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
       ConfigurationName: 'Test',
       IamRoleArn: {
         'Fn::GetAtt': [
@@ -106,7 +106,7 @@ describe('slack channel configuration tests', () => {
       role: role,
     });
 
-    cdkAssert.expect(stack).to(cdkAssert.countResources('AWS::IAM::Role', 0));
+    expect(stack).to(countResources('AWS::IAM::Role', 0));
   });
 
   test('created with new role and add notification permissions', () => {
@@ -118,7 +118,7 @@ describe('slack channel configuration tests', () => {
 
     slackChannel.addNotificationPermissions();
 
-    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::ManagedPolicy', {
+    expect(stack).to(haveResourceLike('AWS::IAM::ManagedPolicy', {
       PolicyDocument: {
         Statement: [
           {
@@ -148,7 +148,7 @@ describe('slack channel configuration tests', () => {
 
     slackChannel.addReadOnlyCommandPermissions();
 
-    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::Role', {
+    expect(stack).to(haveResourceLike('AWS::IAM::Role', {
       AssumeRolePolicyDocument: {
         Statement: [
           {
@@ -180,7 +180,7 @@ describe('slack channel configuration tests', () => {
       ],
     }));
 
-    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::ManagedPolicy', {
+    expect(stack).to(haveResourceLike('AWS::IAM::ManagedPolicy', {
       PolicyDocument: {
         Statement: [
           {
@@ -223,7 +223,7 @@ describe('slack channel configuration tests', () => {
 
     slackChannel.addLambdaInvokeCommandPermissions();
 
-    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::ManagedPolicy', {
+    expect(stack).to(haveResourceLike('AWS::IAM::ManagedPolicy', {
       PolicyDocument: {
         Statement: [
           {
@@ -252,7 +252,7 @@ describe('slack channel configuration tests', () => {
 
     slackChannel.addSupportCommandPermissions();
 
-    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::Role', {
+    expect(stack).to(haveResourceLike('AWS::IAM::Role', {
       AssumeRolePolicyDocument: {
         Statement: [
           {
@@ -297,7 +297,7 @@ describe('slack channel configuration tests', () => {
       resources: ['arn:aws:s3:::abc/xyz/123.txt'],
     }));
 
-    cdkAssert.expect(stack).to(cdkAssert.haveResource('AWS::IAM::Policy', {
+    expect(stack).to(haveResourceLike('AWS::IAM::Policy', {
       PolicyDocument: {
         Statement: [
           {
@@ -322,14 +322,7 @@ describe('slack channel configuration tests', () => {
       resources: ['arn:aws:s3:::abc/xyz/123.txt'],
     }));
 
-    cdkAssert.expect(stack).to(cdkAssert.countResources('AWS::IAM::Role', 0));
-    cdkAssert.expect(stack).to(cdkAssert.countResources('AWS::IAM::Policy', 0));
-  });
-
-  test('from slack channel configuration ARN', () => {
-    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
-
-    expect(imported.configurationName).toEqual('my-slack');
-    expect(imported.slackChannelConfigurationArn).toEqual('arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+    expect(stack).to(countResources('AWS::IAM::Role', 0));
+    expect(stack).to(countResources('AWS::IAM::Policy', 0));
   });
 });
\ No newline at end of file

From e7a2d16b819058547e591d2fde672596bfa8da07 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Sun, 16 Aug 2020 01:58:27 +0800
Subject: [PATCH 08/13] Enhanced to better 1. rename configurationName to
 slackChannelConfigurationName for keep it consistent with
 slackChannelConfigurationArn. 2. adjust method chain for better way. 3. use
 `props.notificationTopics?.map(topic => topic.topicArn)` is better. 4. add a
 test case.

---
 .../lib/slack-channel-configuration.ts        | 31 +++++++++----------
 ...import-slack-channel-configuration.test.ts | 24 +++++++++-----
 .../test/slack-channel-configuration.test.ts  |  2 +-
 3 files changed, 31 insertions(+), 26 deletions(-)

diff --git a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
index 0cadbfcbebaa9..d7e46e956c226 100644
--- a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
+++ b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
@@ -89,7 +89,7 @@ export interface ISlackChannelConfiguration extends cdk.IResource {
    * The name of Slack channel configuration
    * @attribute
    */
-  readonly configurationName: string;
+  readonly slackChannelConfigurationName: string;
 
   /**
    * The permission role of Slack channel configuration
@@ -106,7 +106,7 @@ export interface ISlackChannelConfiguration extends cdk.IResource {
 abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISlackChannelConfiguration {
   abstract readonly slackChannelConfigurationArn: string;
 
-  abstract readonly configurationName: string;
+  abstract readonly slackChannelConfigurationName: string;
 
   abstract readonly role?: iam.IRole;
 
@@ -234,14 +234,15 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
        * The ArnComponents API will return `slack-channel/my-slack`
        * So i need to handle that to gets a correct name.`my-slack`
        */
-      readonly configurationName = (() => {
-        const resourceName = cdk
-          .Stack
-          .of(scope)
-          .parseArn(slackChannelConfigurationArn)
-          .resourceName as string;
-
-        return resourceName.replace(/^slack-channel\//, '');
+      readonly slackChannelConfigurationName = (() => {
+        const re = /^slack-channel\//;
+        const resourceName = cdk.Stack.of(scope).parseArn(slackChannelConfigurationArn).resourceName as string;
+
+        if (!re.test(resourceName)) {
+          throw new Error('The ARN of a Slack integration must be in the form: arn:aws:chatbot:accountId:chat-configuration/slack-channel/slackChannelName');
+        }
+
+        return resourceName.substring('slack-channel/'.length);
       })();
     }
 
@@ -250,7 +251,7 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
 
   readonly slackChannelConfigurationArn: string;
 
-  readonly configurationName: string;
+  readonly slackChannelConfigurationName: string;
 
   readonly role?: iam.IRole;
 
@@ -263,21 +264,17 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
       assumedBy: new iam.ServicePrincipal('chatbot.amazonaws.com'),
     });
 
-    const topicArns = props.notificationTopics
-      ? props.notificationTopics.map((topic) => topic.topicArn)
-      : undefined;
-
     const configuration = new CfnSlackChannelConfiguration(this, 'Resource', {
       configurationName: props.slackChannelConfigurationName,
       iamRoleArn: this.role.roleArn,
       slackWorkspaceId: props.slackWorkspaceId,
       slackChannelId: props.slackChannelId,
-      snsTopicArns: topicArns,
+      snsTopicArns: props.notificationTopics?.map(topic => topic.topicArn),
       loggingLevel: props.loggingLevel?.toString(),
     });
 
     this.slackChannelConfigurationArn = configuration.ref;
-    this.configurationName = props.slackChannelConfigurationName;
+    this.slackChannelConfigurationName = props.slackChannelConfigurationName;
   }
 }
 
diff --git a/packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts
index 5d41a006f8032..dbe39b466217f 100644
--- a/packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts
@@ -1,15 +1,23 @@
 import * as cdk from '@aws-cdk/core';
 import * as chatbot from '../lib';
 
-let stack: cdk.Stack;
+describe('import slack channel configuration tests', () => {
+  let stack: cdk.Stack;
 
-beforeEach(() => {
-  stack = new cdk.Stack();
-});
+  beforeEach(() => {
+    stack = new cdk.Stack();
+  });
 
-test('from slack channel configuration ARN', () => {
-  const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+  test('should throw if ARN invalid', () => {
+    expect(() => chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/my-slack')).toThrow(
+      /The ARN of a Slack integration must be in the form: arn:aws:chatbot:accountId:chat-configuration\/slack-channel\/slackChannelName/,
+    );
+  });
 
-  expect(imported.configurationName).toEqual('my-slack');
-  expect(imported.slackChannelConfigurationArn).toEqual('arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+  test('from slack channel configuration ARN', () => {
+    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+
+    expect(imported.slackChannelConfigurationName).toEqual('my-slack');
+    expect(imported.slackChannelConfigurationArn).toEqual('arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+  });
 });
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
index 3747366f660d7..ec4ed3d03ecab 100644
--- a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
@@ -4,7 +4,7 @@ import * as sns from '@aws-cdk/aws-sns';
 import * as cdk from '@aws-cdk/core';
 import * as chatbot from '../lib';
 
-describe('slack channel configuration tests', () => {
+describe('created slack channel configuration tests', () => {
   let stack: cdk.Stack;
 
   beforeEach(() => {

From d424153106092b63284af80f92451596a5e31fa0 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Mon, 17 Aug 2020 22:56:19 +0800
Subject: [PATCH 09/13] Enhanced for consistency and iam role handling 1.
 change addToPrincipalPolicy of method name to addToRolePolicy for consistency
 2. addXPermissions() change to use this.addToRolePolicy() for avoiding throw
 error. 3. ISlackChannelConfiguration extend iam.IGrantable and implementing.
 4. add test case for addXPermissions() methods of imported slack channel
 configuration.

---
 .../lib/slack-channel-configuration.ts        | 121 +++++++++---------
 ...import-slack-channel-configuration.test.ts |  23 ----
 .../test/integ.chatbot.expected.json          | 104 ++++-----------
 .../aws-chatbot/test/integ.chatbot.ts         |   2 +-
 .../test/slack-channel-configuration.test.ts  | 111 ++++++++++------
 5 files changed, 165 insertions(+), 196 deletions(-)
 delete mode 100644 packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts

diff --git a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
index d7e46e956c226..42c6f8e5d0ab8 100644
--- a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
+++ b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
@@ -77,7 +77,7 @@ export enum LoggingLevel {
 /**
  * Represents a Slack channel configuration
  */
-export interface ISlackChannelConfiguration extends cdk.IResource {
+export interface ISlackChannelConfiguration extends cdk.IResource, iam.IGrantable {
 
   /**
    * The ARN of the Slack channel configuration
@@ -98,6 +98,11 @@ export interface ISlackChannelConfiguration extends cdk.IResource {
    * @default - A role will be created.
    */
   readonly role?: iam.IRole;
+
+  /**
+   * Adds a statement to the IAM role.
+   */
+  addToRolePolicy(statement: iam.PolicyStatement): void;
 }
 
 /**
@@ -108,38 +113,34 @@ abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISl
 
   abstract readonly slackChannelConfigurationName: string;
 
+  abstract readonly grantPrincipal: iam.IPrincipal;
+
   abstract readonly role?: iam.IRole;
 
   /**
    * Adds extra permission to iam-role of Slack channel configuration
    * @param statement
    */
-  public addToPrincipalPolicy(statement: iam.PolicyStatement): void {
+  public addToRolePolicy(statement: iam.PolicyStatement): void {
     if (!this.role) {
       return;
     }
 
-    this.role!.addToPrincipalPolicy(statement);
+    this.role.addToPrincipalPolicy(statement);
   }
 
   /**
    * Allows AWS chatbot to retrieve metric graphs from AWS CloudWatch.
    */
   public addNotificationPermissions(): void {
-    this.role!.addManagedPolicy(new iam.ManagedPolicy(this, 'NotificationsOnlyPolicy', {
-      managedPolicyName: 'AWS-Chatbot-NotificationsOnly-Policy',
-      description: 'NotificationsOnly policy for AWS Chatbot',
-      statements: [
-        new iam.PolicyStatement({
-          effect: iam.Effect.ALLOW,
-          actions: [
-            'cloudwatch:Describe*',
-            'cloudwatch:Get*',
-            'cloudwatch:List*',
-          ],
-          resources: ['*'],
-        }),
+    this.addToRolePolicy(new iam.PolicyStatement({
+      effect: iam.Effect.ALLOW,
+      actions: [
+        'cloudwatch:Describe*',
+        'cloudwatch:Get*',
+        'cloudwatch:List*',
       ],
+      resources: ['*'],
     }));
   }
 
@@ -147,35 +148,33 @@ abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISl
    * Allows read-only commands in supported clients.
    */
   public addReadOnlyCommandPermissions(): void {
-    this.role!.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess'));
-
-    this.role!.addManagedPolicy(new iam.ManagedPolicy(this, 'ReadonlyCommandsPolicy', {
-      managedPolicyName: 'AWS-Chatbot-ReadonlyCommands',
-      description: 'ReadonlyCommands policy for AWS Chatbot',
-      statements: [
-        new iam.PolicyStatement({
-          effect: iam.Effect.DENY,
-          actions: [
-            'iam:*',
-            's3:GetBucketPolicy',
-            'ssm:*',
-            'sts:*',
-            'kms:*',
-            'cognito-idp:GetSigningCertificate',
-            'ec2:GetPasswordData',
-            'ecr:GetAuthorizationToken',
-            'gamelift:RequestUploadCredentials',
-            'gamelift:GetInstanceAccess',
-            'lightsail:DownloadDefaultKeyPair',
-            'lightsail:GetInstanceAccessDetails',
-            'lightsail:GetKeyPair',
-            'lightsail:GetKeyPairs',
-            'redshift:GetClusterCredentials',
-            'storagegateway:DescribeChapCredentials',
-          ],
-          resources: ['*'],
-        }),
+    if (!this.role) {
+      return;
+    }
+
+    this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess'));
+
+    this.addToRolePolicy(new iam.PolicyStatement({
+      effect: iam.Effect.DENY,
+      actions: [
+        'iam:*',
+        's3:GetBucketPolicy',
+        'ssm:*',
+        'sts:*',
+        'kms:*',
+        'cognito-idp:GetSigningCertificate',
+        'ec2:GetPasswordData',
+        'ecr:GetAuthorizationToken',
+        'gamelift:RequestUploadCredentials',
+        'gamelift:GetInstanceAccess',
+        'lightsail:DownloadDefaultKeyPair',
+        'lightsail:GetInstanceAccessDetails',
+        'lightsail:GetKeyPair',
+        'lightsail:GetKeyPairs',
+        'redshift:GetClusterCredentials',
+        'storagegateway:DescribeChapCredentials',
       ],
+      resources: ['*'],
     }));
   }
 
@@ -183,19 +182,13 @@ abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISl
    * Allows Lambda-invoke commands in supported clients.
    */
   public addLambdaInvokeCommandPermissions(): void {
-    this.role!.addManagedPolicy(new iam.ManagedPolicy(this, 'LambdaInvokePolicy', {
-      managedPolicyName: 'AWS-Chatbot-LambdaInvoke-Policy',
-      description: 'LambdaInvoke policy for AWS Chatbot',
-      statements: [
-        new iam.PolicyStatement({
-          effect: iam.Effect.ALLOW,
-          actions: [
-            'lambda:invokeAsync',
-            'lambda:invokeFunction',
-          ],
-          resources: ['*'],
-        }),
+    this.addToRolePolicy(new iam.PolicyStatement({
+      effect: iam.Effect.ALLOW,
+      actions: [
+        'lambda:invokeAsync',
+        'lambda:invokeFunction',
       ],
+      resources: ['*'],
     }));
   }
 
@@ -203,7 +196,11 @@ abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISl
    * Allows calling AWS Support APIs in supported clients.
    */
   public addSupportCommandPermissions(): void {
-    this.role!.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AWSSupportAccess'));
+    if (!this.role) {
+      return;
+    }
+
+    this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AWSSupportAccess'));
   }
 }
 
@@ -228,6 +225,7 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
        */
       readonly slackChannelConfigurationArn = slackChannelConfigurationArn;
       readonly role?: iam.IRole = undefined;
+      readonly grantPrincipal: iam.IPrincipal;
 
       /**
        * For example: arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack
@@ -244,6 +242,11 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
 
         return resourceName.substring('slack-channel/'.length);
       })();
+
+      constructor(s: cdk.Construct, i: string) {
+        super(s, i);
+        this.grantPrincipal = new iam.UnknownPrincipal({ resource: this });
+      }
     }
 
     return new Import(scope, id);
@@ -255,6 +258,8 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
 
   readonly role?: iam.IRole;
 
+  readonly grantPrincipal: iam.IPrincipal;
+
   constructor(scope: cdk.Construct, id: string, props: SlackChannelConfigurationProps) {
     super(scope, id, {
       physicalName: props.slackChannelConfigurationName,
@@ -264,6 +269,8 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
       assumedBy: new iam.ServicePrincipal('chatbot.amazonaws.com'),
     });
 
+    this.grantPrincipal = this.role;
+
     const configuration = new CfnSlackChannelConfiguration(this, 'Resource', {
       configurationName: props.slackChannelConfigurationName,
       iamRoleArn: this.role.roleArn,
diff --git a/packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts
deleted file mode 100644
index dbe39b466217f..0000000000000
--- a/packages/@aws-cdk/aws-chatbot/test/import-slack-channel-configuration.test.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import * as cdk from '@aws-cdk/core';
-import * as chatbot from '../lib';
-
-describe('import slack channel configuration tests', () => {
-  let stack: cdk.Stack;
-
-  beforeEach(() => {
-    stack = new cdk.Stack();
-  });
-
-  test('should throw if ARN invalid', () => {
-    expect(() => chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/my-slack')).toThrow(
-      /The ARN of a Slack integration must be in the form: arn:aws:chatbot:accountId:chat-configuration\/slack-channel\/slackChannelName/,
-    );
-  });
-
-  test('from slack channel configuration ARN', () => {
-    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
-
-    expect(imported.slackChannelConfigurationName).toEqual('my-slack');
-    expect(imported.slackChannelConfigurationArn).toEqual('arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
-  });
-});
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json
index 67297d57e4f3f..02398d5f91051 100644
--- a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json
+++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json
@@ -16,12 +16,6 @@
           "Version": "2012-10-17"
         },
         "ManagedPolicyArns": [
-          {
-            "Ref": "MySlackChannelLambdaInvokePolicy276508B8"
-          },
-          {
-            "Ref": "MySlackChannelNotificationsOnlyPolicy54A7013D"
-          },
           {
             "Fn::Join": [
               "",
@@ -45,51 +39,12 @@
                 ":iam::aws:policy/ReadOnlyAccess"
               ]
             ]
-          },
-          {
-            "Ref": "MySlackChannelReadonlyCommandsPolicyD69F9CE1"
           }
         ]
       }
     },
     "MySlackChannelConfigurationRoleDefaultPolicyE4C1FA62": {
       "Type": "AWS::IAM::Policy",
-      "Properties": {
-        "PolicyDocument": {
-          "Statement": [
-            {
-              "Action": "s3:GetObject",
-              "Effect": "Allow",
-              "Resource": "arn:aws:s3:::abc/xyz/123.txt"
-            }
-          ],
-          "Version": "2012-10-17"
-        },
-        "PolicyName": "MySlackChannelConfigurationRoleDefaultPolicyE4C1FA62",
-        "Roles": [
-          {
-            "Ref": "MySlackChannelConfigurationRole1D3F23AE"
-          }
-        ]
-      }
-    },
-    "MySlackChannelA8E0B56C": {
-      "Type": "AWS::Chatbot::SlackChannelConfiguration",
-      "Properties": {
-        "ConfigurationName": "test-channel",
-        "IamRoleArn": {
-          "Fn::GetAtt": [
-            "MySlackChannelConfigurationRole1D3F23AE",
-            "Arn"
-          ]
-        },
-        "SlackChannelId": "C0187JABUE9",
-        "SlackWorkspaceId": "T49239U4W",
-        "LoggingLevel": "NONE"
-      }
-    },
-    "MySlackChannelLambdaInvokePolicy276508B8": {
-      "Type": "AWS::IAM::ManagedPolicy",
       "Properties": {
         "PolicyDocument": {
           "Statement": [
@@ -100,20 +55,7 @@
               ],
               "Effect": "Allow",
               "Resource": "*"
-            }
-          ],
-          "Version": "2012-10-17"
-        },
-        "Description": "LambdaInvoke policy for AWS Chatbot",
-        "ManagedPolicyName": "AWS-Chatbot-LambdaInvoke-Policy",
-        "Path": "/"
-      }
-    },
-    "MySlackChannelNotificationsOnlyPolicy54A7013D": {
-      "Type": "AWS::IAM::ManagedPolicy",
-      "Properties": {
-        "PolicyDocument": {
-          "Statement": [
+            },
             {
               "Action": [
                 "cloudwatch:Describe*",
@@ -122,20 +64,7 @@
               ],
               "Effect": "Allow",
               "Resource": "*"
-            }
-          ],
-          "Version": "2012-10-17"
-        },
-        "Description": "NotificationsOnly policy for AWS Chatbot",
-        "ManagedPolicyName": "AWS-Chatbot-NotificationsOnly-Policy",
-        "Path": "/"
-      }
-    },
-    "MySlackChannelReadonlyCommandsPolicyD69F9CE1": {
-      "Type": "AWS::IAM::ManagedPolicy",
-      "Properties": {
-        "PolicyDocument": {
-          "Statement": [
+            },
             {
               "Action": [
                 "iam:*",
@@ -157,13 +86,36 @@
               ],
               "Effect": "Deny",
               "Resource": "*"
+            },
+            {
+              "Action": "s3:GetObject",
+              "Effect": "Allow",
+              "Resource": "arn:aws:s3:::abc/xyz/123.txt"
             }
           ],
           "Version": "2012-10-17"
         },
-        "Description": "ReadonlyCommands policy for AWS Chatbot",
-        "ManagedPolicyName": "AWS-Chatbot-ReadonlyCommands",
-        "Path": "/"
+        "PolicyName": "MySlackChannelConfigurationRoleDefaultPolicyE4C1FA62",
+        "Roles": [
+          {
+            "Ref": "MySlackChannelConfigurationRole1D3F23AE"
+          }
+        ]
+      }
+    },
+    "MySlackChannelA8E0B56C": {
+      "Type": "AWS::Chatbot::SlackChannelConfiguration",
+      "Properties": {
+        "ConfigurationName": "test-channel",
+        "IamRoleArn": {
+          "Fn::GetAtt": [
+            "MySlackChannelConfigurationRole1D3F23AE",
+            "Arn"
+          ]
+        },
+        "SlackChannelId": "C0187JABUE9",
+        "SlackWorkspaceId": "T49239U4W",
+        "LoggingLevel": "NONE"
       }
     }
   }
diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
index 324a591068569..73344fa3b01b5 100644
--- a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
@@ -18,7 +18,7 @@ class ChatbotInteg extends cdk.Stack {
     slackChannel.addSupportCommandPermissions();
     slackChannel.addReadOnlyCommandPermissions();
 
-    slackChannel.addToPrincipalPolicy(new iam.PolicyStatement({
+    slackChannel.addToRolePolicy(new iam.PolicyStatement({
       effect: iam.Effect.ALLOW,
       actions: [
         's3:GetObject',
diff --git a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
index ec4ed3d03ecab..ccd150554ab45 100644
--- a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
@@ -1,10 +1,10 @@
-import { expect, haveResourceLike, countResources } from '@aws-cdk/assert';
+import '@aws-cdk/assert/jest';
 import * as iam from '@aws-cdk/aws-iam';
 import * as sns from '@aws-cdk/aws-sns';
 import * as cdk from '@aws-cdk/core';
 import * as chatbot from '../lib';
 
-describe('created slack channel configuration tests', () => {
+describe('SlackChannelConfiguration', () => {
   let stack: cdk.Stack;
 
   beforeEach(() => {
@@ -18,7 +18,7 @@ describe('created slack channel configuration tests', () => {
       slackChannelConfigurationName: 'Test',
     });
 
-    expect(stack).to(haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
+    expect(stack).toHaveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
       ConfigurationName: 'Test',
       IamRoleArn: {
         'Fn::GetAtt': [
@@ -28,9 +28,9 @@ describe('created slack channel configuration tests', () => {
       },
       SlackChannelId: 'DEF456',
       SlackWorkspaceId: 'ABC123',
-    }));
+    });
 
-    expect(stack).to(haveResourceLike('AWS::IAM::Role', {
+    expect(stack).toHaveResourceLike('AWS::IAM::Role', {
       AssumeRolePolicyDocument: {
         Statement: [
           {
@@ -43,7 +43,7 @@ describe('created slack channel configuration tests', () => {
         ],
         Version: '2012-10-17',
       },
-    }));
+    });
   });
 
   test('created and pass loggingLevel parameter [LoggingLevel.ERROR], it should be set [ERROR] logging level in Cloudformation', () => {
@@ -54,7 +54,7 @@ describe('created slack channel configuration tests', () => {
       loggingLevel: chatbot.LoggingLevel.ERROR,
     });
 
-    expect(stack).to(haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
+    expect(stack).toHaveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
       ConfigurationName: 'Test',
       IamRoleArn: {
         'Fn::GetAtt': [
@@ -65,7 +65,7 @@ describe('created slack channel configuration tests', () => {
       SlackChannelId: 'DEF456',
       SlackWorkspaceId: 'ABC123',
       LoggingLevel: 'ERROR',
-    }));
+    });
   });
 
   test('created with new sns topic', () => {
@@ -78,7 +78,7 @@ describe('created slack channel configuration tests', () => {
       notificationTopics: [topic],
     });
 
-    expect(stack).to(haveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
+    expect(stack).toHaveResourceLike('AWS::Chatbot::SlackChannelConfiguration', {
       ConfigurationName: 'Test',
       IamRoleArn: {
         'Fn::GetAtt': [
@@ -93,7 +93,7 @@ describe('created slack channel configuration tests', () => {
           Ref: 'MyTopic86869434',
         },
       ],
-    }));
+    });
   });
 
   test('created with existing role', () => {
@@ -106,7 +106,7 @@ describe('created slack channel configuration tests', () => {
       role: role,
     });
 
-    expect(stack).to(countResources('AWS::IAM::Role', 0));
+    expect(stack).toCountResources('AWS::IAM::Role', 0);
   });
 
   test('created with new role and add notification permissions', () => {
@@ -118,7 +118,7 @@ describe('created slack channel configuration tests', () => {
 
     slackChannel.addNotificationPermissions();
 
-    expect(stack).to(haveResourceLike('AWS::IAM::ManagedPolicy', {
+    expect(stack).toHaveResourceLike('AWS::IAM::Policy', {
       PolicyDocument: {
         Statement: [
           {
@@ -133,10 +133,7 @@ describe('created slack channel configuration tests', () => {
         ],
         Version: '2012-10-17',
       },
-      Description: 'NotificationsOnly policy for AWS Chatbot',
-      ManagedPolicyName: 'AWS-Chatbot-NotificationsOnly-Policy',
-      Path: '/',
-    }));
+    });
   });
 
   test('created with new role and add read-only command permissions', () => {
@@ -148,7 +145,7 @@ describe('created slack channel configuration tests', () => {
 
     slackChannel.addReadOnlyCommandPermissions();
 
-    expect(stack).to(haveResourceLike('AWS::IAM::Role', {
+    expect(stack).toHaveResourceLike('AWS::IAM::Role', {
       AssumeRolePolicyDocument: {
         Statement: [
           {
@@ -174,13 +171,10 @@ describe('created slack channel configuration tests', () => {
             ],
           ],
         },
-        {
-          Ref: 'MySlackChannelReadonlyCommandsPolicyD69F9CE1',
-        },
       ],
-    }));
+    });
 
-    expect(stack).to(haveResourceLike('AWS::IAM::ManagedPolicy', {
+    expect(stack).toHaveResourceLike('AWS::IAM::Policy', {
       PolicyDocument: {
         Statement: [
           {
@@ -208,10 +202,7 @@ describe('created slack channel configuration tests', () => {
         ],
         Version: '2012-10-17',
       },
-      Description: 'ReadonlyCommands policy for AWS Chatbot',
-      ManagedPolicyName: 'AWS-Chatbot-ReadonlyCommands',
-      Path: '/',
-    }));
+    });
   });
 
   test('created with new role and add lambda invoke command permissions', () => {
@@ -223,7 +214,7 @@ describe('created slack channel configuration tests', () => {
 
     slackChannel.addLambdaInvokeCommandPermissions();
 
-    expect(stack).to(haveResourceLike('AWS::IAM::ManagedPolicy', {
+    expect(stack).toHaveResourceLike('AWS::IAM::Policy', {
       PolicyDocument: {
         Statement: [
           {
@@ -237,10 +228,7 @@ describe('created slack channel configuration tests', () => {
         ],
         Version: '2012-10-17',
       },
-      Description: 'LambdaInvoke policy for AWS Chatbot',
-      ManagedPolicyName: 'AWS-Chatbot-LambdaInvoke-Policy',
-      Path: '/',
-    }));
+    });
   });
 
   test('created with new role and add support command permissions', () => {
@@ -252,7 +240,7 @@ describe('created slack channel configuration tests', () => {
 
     slackChannel.addSupportCommandPermissions();
 
-    expect(stack).to(haveResourceLike('AWS::IAM::Role', {
+    expect(stack).toHaveResourceLike('AWS::IAM::Role', {
       AssumeRolePolicyDocument: {
         Statement: [
           {
@@ -279,7 +267,7 @@ describe('created slack channel configuration tests', () => {
           ],
         },
       ],
-    }));
+    });
   });
 
   test('created with new role and extra iam policies', () => {
@@ -289,7 +277,7 @@ describe('created slack channel configuration tests', () => {
       slackChannelConfigurationName: 'Test',
     });
 
-    slackChannel.addToPrincipalPolicy(new iam.PolicyStatement({
+    slackChannel.addToRolePolicy(new iam.PolicyStatement({
       effect: iam.Effect.ALLOW,
       actions: [
         's3:GetObject',
@@ -297,7 +285,7 @@ describe('created slack channel configuration tests', () => {
       resources: ['arn:aws:s3:::abc/xyz/123.txt'],
     }));
 
-    expect(stack).to(haveResourceLike('AWS::IAM::Policy', {
+    expect(stack).toHaveResourceLike('AWS::IAM::Policy', {
       PolicyDocument: {
         Statement: [
           {
@@ -308,13 +296,13 @@ describe('created slack channel configuration tests', () => {
         ],
         Version: '2012-10-17',
       },
-    }));
+    });
   });
 
   test('added a iam policy to a from slack channel configuration ARN will nothing to do', () => {
     const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
 
-    (imported as chatbot.SlackChannelConfiguration).addToPrincipalPolicy(new iam.PolicyStatement({
+    (imported as chatbot.SlackChannelConfiguration).addToRolePolicy(new iam.PolicyStatement({
       effect: iam.Effect.ALLOW,
       actions: [
         's3:GetObject',
@@ -322,7 +310,52 @@ describe('created slack channel configuration tests', () => {
       resources: ['arn:aws:s3:::abc/xyz/123.txt'],
     }));
 
-    expect(stack).to(countResources('AWS::IAM::Role', 0));
-    expect(stack).to(countResources('AWS::IAM::Policy', 0));
+    expect(stack).toCountResources('AWS::IAM::Role', 0);
+    expect(stack).toCountResources('AWS::IAM::Policy', 0);
+  });
+
+  test('call addNotificationPermissions() method with imported slack channel configuration will nothing todo', () => {
+    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+    (imported as chatbot.SlackChannelConfiguration).addNotificationPermissions();
+
+    expect(stack).toCountResources('AWS::IAM::Role', 0);
+    expect(stack).toCountResources('AWS::IAM::Policy', 0);
+  });
+
+  test('call addReadOnlyCommandPermissions() method with imported slack channel configuration will nothing todo', () => {
+    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+    (imported as chatbot.SlackChannelConfiguration).addReadOnlyCommandPermissions();
+
+    expect(stack).toCountResources('AWS::IAM::Role', 0);
+    expect(stack).toCountResources('AWS::IAM::Policy', 0);
+  });
+
+  test('call addLambdaInvokeCommandPermissions() method with imported slack channel configuration will nothing todo', () => {
+    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+    (imported as chatbot.SlackChannelConfiguration).addLambdaInvokeCommandPermissions();
+
+    expect(stack).toCountResources('AWS::IAM::Role', 0);
+    expect(stack).toCountResources('AWS::IAM::Policy', 0);
+  });
+
+  test('call addSupportCommandPermissions() method with imported slack channel configuration will nothing todo', () => {
+    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+    (imported as chatbot.SlackChannelConfiguration).addSupportCommandPermissions();
+
+    expect(stack).toCountResources('AWS::IAM::Role', 0);
+    expect(stack).toCountResources('AWS::IAM::Policy', 0);
+  });
+
+  test('should throw error if ARN invalid', () => {
+    expect(() => chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/my-slack')).toThrow(
+      /The ARN of a Slack integration must be in the form: arn:aws:chatbot:accountId:chat-configuration\/slack-channel\/slackChannelName/,
+    );
+  });
+
+  test('from slack channel configuration ARN', () => {
+    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
+
+    expect(imported.slackChannelConfigurationName).toEqual('my-slack');
+    expect(imported.slackChannelConfigurationArn).toEqual('arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
   });
 });
\ No newline at end of file

From 733e6ae5fc785b7e2b7024a7f611dc9485dfd4d1 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Tue, 18 Aug 2020 02:30:01 +0800
Subject: [PATCH 10/13] remove addXPermissions() methods

---
 .../lib/slack-channel-configuration.ts        |  74 -------
 .../test/integ.chatbot.expected.json          |  67 +-----
 .../aws-chatbot/test/integ.chatbot.ts         |   5 -
 .../test/slack-channel-configuration.test.ts  | 193 ------------------
 4 files changed, 1 insertion(+), 338 deletions(-)

diff --git a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
index 42c6f8e5d0ab8..d90cfd363064f 100644
--- a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
+++ b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
@@ -128,80 +128,6 @@ abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISl
 
     this.role.addToPrincipalPolicy(statement);
   }
-
-  /**
-   * Allows AWS chatbot to retrieve metric graphs from AWS CloudWatch.
-   */
-  public addNotificationPermissions(): void {
-    this.addToRolePolicy(new iam.PolicyStatement({
-      effect: iam.Effect.ALLOW,
-      actions: [
-        'cloudwatch:Describe*',
-        'cloudwatch:Get*',
-        'cloudwatch:List*',
-      ],
-      resources: ['*'],
-    }));
-  }
-
-  /**
-   * Allows read-only commands in supported clients.
-   */
-  public addReadOnlyCommandPermissions(): void {
-    if (!this.role) {
-      return;
-    }
-
-    this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess'));
-
-    this.addToRolePolicy(new iam.PolicyStatement({
-      effect: iam.Effect.DENY,
-      actions: [
-        'iam:*',
-        's3:GetBucketPolicy',
-        'ssm:*',
-        'sts:*',
-        'kms:*',
-        'cognito-idp:GetSigningCertificate',
-        'ec2:GetPasswordData',
-        'ecr:GetAuthorizationToken',
-        'gamelift:RequestUploadCredentials',
-        'gamelift:GetInstanceAccess',
-        'lightsail:DownloadDefaultKeyPair',
-        'lightsail:GetInstanceAccessDetails',
-        'lightsail:GetKeyPair',
-        'lightsail:GetKeyPairs',
-        'redshift:GetClusterCredentials',
-        'storagegateway:DescribeChapCredentials',
-      ],
-      resources: ['*'],
-    }));
-  }
-
-  /**
-   * Allows Lambda-invoke commands in supported clients.
-   */
-  public addLambdaInvokeCommandPermissions(): void {
-    this.addToRolePolicy(new iam.PolicyStatement({
-      effect: iam.Effect.ALLOW,
-      actions: [
-        'lambda:invokeAsync',
-        'lambda:invokeFunction',
-      ],
-      resources: ['*'],
-    }));
-  }
-
-  /**
-   * Allows calling AWS Support APIs in supported clients.
-   */
-  public addSupportCommandPermissions(): void {
-    if (!this.role) {
-      return;
-    }
-
-    this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AWSSupportAccess'));
-  }
 }
 
 /**
diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json
index 02398d5f91051..1cf6a7afbdd1f 100644
--- a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json
+++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.expected.json
@@ -14,33 +14,7 @@
             }
           ],
           "Version": "2012-10-17"
-        },
-        "ManagedPolicyArns": [
-          {
-            "Fn::Join": [
-              "",
-              [
-                "arn:",
-                {
-                  "Ref": "AWS::Partition"
-                },
-                ":iam::aws:policy/AWSSupportAccess"
-              ]
-            ]
-          },
-          {
-            "Fn::Join": [
-              "",
-              [
-                "arn:",
-                {
-                  "Ref": "AWS::Partition"
-                },
-                ":iam::aws:policy/ReadOnlyAccess"
-              ]
-            ]
-          }
-        ]
+        }
       }
     },
     "MySlackChannelConfigurationRoleDefaultPolicyE4C1FA62": {
@@ -48,45 +22,6 @@
       "Properties": {
         "PolicyDocument": {
           "Statement": [
-            {
-              "Action": [
-                "lambda:invokeAsync",
-                "lambda:invokeFunction"
-              ],
-              "Effect": "Allow",
-              "Resource": "*"
-            },
-            {
-              "Action": [
-                "cloudwatch:Describe*",
-                "cloudwatch:Get*",
-                "cloudwatch:List*"
-              ],
-              "Effect": "Allow",
-              "Resource": "*"
-            },
-            {
-              "Action": [
-                "iam:*",
-                "s3:GetBucketPolicy",
-                "ssm:*",
-                "sts:*",
-                "kms:*",
-                "cognito-idp:GetSigningCertificate",
-                "ec2:GetPasswordData",
-                "ecr:GetAuthorizationToken",
-                "gamelift:RequestUploadCredentials",
-                "gamelift:GetInstanceAccess",
-                "lightsail:DownloadDefaultKeyPair",
-                "lightsail:GetInstanceAccessDetails",
-                "lightsail:GetKeyPair",
-                "lightsail:GetKeyPairs",
-                "redshift:GetClusterCredentials",
-                "storagegateway:DescribeChapCredentials"
-              ],
-              "Effect": "Deny",
-              "Resource": "*"
-            },
             {
               "Action": "s3:GetObject",
               "Effect": "Allow",
diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
index 73344fa3b01b5..bb7fd54fa96f8 100644
--- a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
@@ -13,11 +13,6 @@ class ChatbotInteg extends cdk.Stack {
       loggingLevel: chatbot.LoggingLevel.NONE,
     });
 
-    slackChannel.addLambdaInvokeCommandPermissions();
-    slackChannel.addNotificationPermissions();
-    slackChannel.addSupportCommandPermissions();
-    slackChannel.addReadOnlyCommandPermissions();
-
     slackChannel.addToRolePolicy(new iam.PolicyStatement({
       effect: iam.Effect.ALLOW,
       actions: [
diff --git a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
index ccd150554ab45..4cca685da37ab 100644
--- a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
@@ -109,167 +109,6 @@ describe('SlackChannelConfiguration', () => {
     expect(stack).toCountResources('AWS::IAM::Role', 0);
   });
 
-  test('created with new role and add notification permissions', () => {
-    const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
-      slackWorkspaceId: 'ABC123',
-      slackChannelId: 'DEF456',
-      slackChannelConfigurationName: 'Test',
-    });
-
-    slackChannel.addNotificationPermissions();
-
-    expect(stack).toHaveResourceLike('AWS::IAM::Policy', {
-      PolicyDocument: {
-        Statement: [
-          {
-            Action: [
-              'cloudwatch:Describe*',
-              'cloudwatch:Get*',
-              'cloudwatch:List*',
-            ],
-            Effect: 'Allow',
-            Resource: '*',
-          },
-        ],
-        Version: '2012-10-17',
-      },
-    });
-  });
-
-  test('created with new role and add read-only command permissions', () => {
-    const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
-      slackWorkspaceId: 'ABC123',
-      slackChannelId: 'DEF456',
-      slackChannelConfigurationName: 'Test',
-    });
-
-    slackChannel.addReadOnlyCommandPermissions();
-
-    expect(stack).toHaveResourceLike('AWS::IAM::Role', {
-      AssumeRolePolicyDocument: {
-        Statement: [
-          {
-            Action: 'sts:AssumeRole',
-            Effect: 'Allow',
-            Principal: {
-              Service: 'chatbot.amazonaws.com',
-            },
-          },
-        ],
-        Version: '2012-10-17',
-      },
-      ManagedPolicyArns: [
-        {
-          'Fn::Join': [
-            '',
-            [
-              'arn:',
-              {
-                Ref: 'AWS::Partition',
-              },
-              ':iam::aws:policy/ReadOnlyAccess',
-            ],
-          ],
-        },
-      ],
-    });
-
-    expect(stack).toHaveResourceLike('AWS::IAM::Policy', {
-      PolicyDocument: {
-        Statement: [
-          {
-            Action: [
-              'iam:*',
-              's3:GetBucketPolicy',
-              'ssm:*',
-              'sts:*',
-              'kms:*',
-              'cognito-idp:GetSigningCertificate',
-              'ec2:GetPasswordData',
-              'ecr:GetAuthorizationToken',
-              'gamelift:RequestUploadCredentials',
-              'gamelift:GetInstanceAccess',
-              'lightsail:DownloadDefaultKeyPair',
-              'lightsail:GetInstanceAccessDetails',
-              'lightsail:GetKeyPair',
-              'lightsail:GetKeyPairs',
-              'redshift:GetClusterCredentials',
-              'storagegateway:DescribeChapCredentials',
-            ],
-            Effect: 'Deny',
-            Resource: '*',
-          },
-        ],
-        Version: '2012-10-17',
-      },
-    });
-  });
-
-  test('created with new role and add lambda invoke command permissions', () => {
-    const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
-      slackWorkspaceId: 'ABC123',
-      slackChannelId: 'DEF456',
-      slackChannelConfigurationName: 'Test',
-    });
-
-    slackChannel.addLambdaInvokeCommandPermissions();
-
-    expect(stack).toHaveResourceLike('AWS::IAM::Policy', {
-      PolicyDocument: {
-        Statement: [
-          {
-            Action: [
-              'lambda:invokeAsync',
-              'lambda:invokeFunction',
-            ],
-            Effect: 'Allow',
-            Resource: '*',
-          },
-        ],
-        Version: '2012-10-17',
-      },
-    });
-  });
-
-  test('created with new role and add support command permissions', () => {
-    const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
-      slackWorkspaceId: 'ABC123',
-      slackChannelId: 'DEF456',
-      slackChannelConfigurationName: 'Test',
-    });
-
-    slackChannel.addSupportCommandPermissions();
-
-    expect(stack).toHaveResourceLike('AWS::IAM::Role', {
-      AssumeRolePolicyDocument: {
-        Statement: [
-          {
-            Action: 'sts:AssumeRole',
-            Effect: 'Allow',
-            Principal: {
-              Service: 'chatbot.amazonaws.com',
-            },
-          },
-        ],
-        Version: '2012-10-17',
-      },
-      ManagedPolicyArns: [
-        {
-          'Fn::Join': [
-            '',
-            [
-              'arn:',
-              {
-                Ref: 'AWS::Partition',
-              },
-              ':iam::aws:policy/AWSSupportAccess',
-            ],
-          ],
-        },
-      ],
-    });
-  });
-
   test('created with new role and extra iam policies', () => {
     const slackChannel = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', {
       slackWorkspaceId: 'ABC123',
@@ -314,38 +153,6 @@ describe('SlackChannelConfiguration', () => {
     expect(stack).toCountResources('AWS::IAM::Policy', 0);
   });
 
-  test('call addNotificationPermissions() method with imported slack channel configuration will nothing todo', () => {
-    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
-    (imported as chatbot.SlackChannelConfiguration).addNotificationPermissions();
-
-    expect(stack).toCountResources('AWS::IAM::Role', 0);
-    expect(stack).toCountResources('AWS::IAM::Policy', 0);
-  });
-
-  test('call addReadOnlyCommandPermissions() method with imported slack channel configuration will nothing todo', () => {
-    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
-    (imported as chatbot.SlackChannelConfiguration).addReadOnlyCommandPermissions();
-
-    expect(stack).toCountResources('AWS::IAM::Role', 0);
-    expect(stack).toCountResources('AWS::IAM::Policy', 0);
-  });
-
-  test('call addLambdaInvokeCommandPermissions() method with imported slack channel configuration will nothing todo', () => {
-    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
-    (imported as chatbot.SlackChannelConfiguration).addLambdaInvokeCommandPermissions();
-
-    expect(stack).toCountResources('AWS::IAM::Role', 0);
-    expect(stack).toCountResources('AWS::IAM::Policy', 0);
-  });
-
-  test('call addSupportCommandPermissions() method with imported slack channel configuration will nothing todo', () => {
-    const imported = chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack');
-    (imported as chatbot.SlackChannelConfiguration).addSupportCommandPermissions();
-
-    expect(stack).toCountResources('AWS::IAM::Role', 0);
-    expect(stack).toCountResources('AWS::IAM::Policy', 0);
-  });
-
   test('should throw error if ARN invalid', () => {
     expect(() => chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/my-slack')).toThrow(
       /The ARN of a Slack integration must be in the form: arn:aws:chatbot:accountId:chat-configuration\/slack-channel\/slackChannelName/,

From 26c7a273f394fa3f5530f9cf0757b1ed640a9eb2 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Thu, 20 Aug 2020 12:49:19 +0800
Subject: [PATCH 11/13] fix linter problem

---
 packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
index bb7fd54fa96f8..c005dfa873abe 100644
--- a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot.ts
@@ -9,7 +9,7 @@ class ChatbotInteg extends cdk.Stack {
     const slackChannel = new chatbot.SlackChannelConfiguration(this, 'MySlackChannel', {
       slackChannelConfigurationName: 'test-channel',
       slackWorkspaceId: 'T49239U4W', // modify to your slack workspace id
-      slackChannelId: 'C0187JABUE9',   // modify to your slack channel id
+      slackChannelId: 'C0187JABUE9', // modify to your slack channel id
       loggingLevel: chatbot.LoggingLevel.NONE,
     });
 

From cc1c6e2afe37d66464ff63a17a4f612cd64fd4f1 Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Tue, 1 Sep 2020 12:52:52 +0800
Subject: [PATCH 12/13] Enhanced `fromSlackChannelConfigurationArn()` static
 method 1. adjust error description. 2. remove the IIFE way.

---
 .../lib/slack-channel-configuration.ts        | 24 ++++++++++---------
 .../test/slack-channel-configuration.test.ts  |  2 +-
 2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
index d90cfd363064f..d7746c6b8d679 100644
--- a/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
+++ b/packages/@aws-cdk/aws-chatbot/lib/slack-channel-configuration.ts
@@ -81,6 +81,7 @@ export interface ISlackChannelConfiguration extends cdk.IResource, iam.IGrantabl
 
   /**
    * The ARN of the Slack channel configuration
+   * In the form of arn:aws:chatbot:{region}:{account}:chat-configuration/slack-channel/{slackChannelName}
    * @attribute
    */
   readonly slackChannelConfigurationArn: string;
@@ -144,6 +145,13 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
    * @returns a reference to the existing Slack channel configuration
    */
   public static fromSlackChannelConfigurationArn(scope: cdk.Construct, id: string, slackChannelConfigurationArn: string): ISlackChannelConfiguration {
+    const re = /^slack-channel\//;
+    const resourceName = cdk.Stack.of(scope).parseArn(slackChannelConfigurationArn).resourceName as string;
+
+    if (!re.test(resourceName)) {
+      throw new Error('The ARN of a Slack integration must be in the form: arn:aws:chatbot:{region}:{account}:chat-configuration/slack-channel/{slackChannelName}');
+    }
+
     class Import extends SlackChannelConfigurationBase {
 
       /**
@@ -154,20 +162,14 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
       readonly grantPrincipal: iam.IPrincipal;
 
       /**
+       * Returns a name of Slack channel configuration
+       *
+       * NOTE:
        * For example: arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack
        * The ArnComponents API will return `slack-channel/my-slack`
-       * So i need to handle that to gets a correct name.`my-slack`
+       * It need to handle that to gets a correct name.`my-slack`
        */
-      readonly slackChannelConfigurationName = (() => {
-        const re = /^slack-channel\//;
-        const resourceName = cdk.Stack.of(scope).parseArn(slackChannelConfigurationArn).resourceName as string;
-
-        if (!re.test(resourceName)) {
-          throw new Error('The ARN of a Slack integration must be in the form: arn:aws:chatbot:accountId:chat-configuration/slack-channel/slackChannelName');
-        }
-
-        return resourceName.substring('slack-channel/'.length);
-      })();
+      readonly slackChannelConfigurationName = resourceName.substring('slack-channel/'.length);
 
       constructor(s: cdk.Construct, i: string) {
         super(s, i);
diff --git a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
index 4cca685da37ab..3cf1189d9fee2 100644
--- a/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
+++ b/packages/@aws-cdk/aws-chatbot/test/slack-channel-configuration.test.ts
@@ -155,7 +155,7 @@ describe('SlackChannelConfiguration', () => {
 
   test('should throw error if ARN invalid', () => {
     expect(() => chatbot.SlackChannelConfiguration.fromSlackChannelConfigurationArn(stack, 'MySlackChannel', 'arn:aws:chatbot::1234567890:chat-configuration/my-slack')).toThrow(
-      /The ARN of a Slack integration must be in the form: arn:aws:chatbot:accountId:chat-configuration\/slack-channel\/slackChannelName/,
+      /The ARN of a Slack integration must be in the form: arn:aws:chatbot:{region}:{account}:chat-configuration\/slack-channel\/{slackChannelName}/,
     );
   });
 

From b7526409b141ed6709745b31c1dc552d33945adf Mon Sep 17 00:00:00 2001
From: joelzhong <luckily.zhong@gmail.com>
Date: Tue, 1 Sep 2020 15:19:23 +0800
Subject: [PATCH 13/13] upgrade constructs to ^3.0.4

---
 packages/@aws-cdk/aws-chatbot/package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/@aws-cdk/aws-chatbot/package.json b/packages/@aws-cdk/aws-chatbot/package.json
index 10c73bbd2bd2b..2a2acb7013526 100644
--- a/packages/@aws-cdk/aws-chatbot/package.json
+++ b/packages/@aws-cdk/aws-chatbot/package.json
@@ -76,13 +76,13 @@
     "@aws-cdk/aws-iam": "0.0.0",
     "@aws-cdk/aws-sns": "0.0.0",
     "@aws-cdk/core": "0.0.0",
-    "constructs": "^3.0.2"
+    "constructs": "^3.0.4"
   },
   "peerDependencies": {
     "@aws-cdk/aws-iam": "0.0.0",
     "@aws-cdk/aws-sns": "0.0.0",
     "@aws-cdk/core": "0.0.0",
-    "constructs": "^3.0.2"
+    "constructs": "^3.0.4"
   },
   "engines": {
     "node": ">= 10.13.0 <13 || >=13.7.0"