Skip to content

Commit 05f5e62

Browse files
authored
feat(aws-chatbot): Support L2 construct for SlackChannelConfiguration of chatbot. (#9702)
I am ready for the first run. Support L2 construct for SlackChannelConfiguration of chatbot. 1. add L2 construct 2. add unit tests 3. add integration test 4. update package.json Resolves: #9679 cc @skinny85 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent ec423ef commit 05f5e62

8 files changed

+502
-8
lines changed

packages/@aws-cdk/aws-chatbot/README.md

+21
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,29 @@
99
---
1010
<!--END STABILITY BANNER-->
1111

12+
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.
13+
1214
This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project.
1315

1416
```ts
1517
import * as chatbot from '@aws-cdk/aws-chatbot';
18+
19+
const slackChannel = new chatbot.SlackChannelConfiguration(this, 'MySlackChannel', {
20+
slackChannelConfigurationName: 'YOUR_CHANNEL_NAME',
21+
slackWorkspaceId: 'YOUR_SLACK_WORKSPACE_ID',
22+
slackChannelId: 'YOUR_SLACK_CHANNEL_ID',
23+
});
24+
25+
slackChannel.addLambdaInvokeCommandPermissions();
26+
slackChannel.addNotificationPermissions();
27+
slackChannel.addSupportCommandPermissions();
28+
slackChannel.addReadOnlyCommandPermissions();
29+
30+
slackChannel.addToPrincipalPolicy(new iam.PolicyStatement({
31+
effect: iam.Effect.ALLOW,
32+
actions: [
33+
's3:GetObject',
34+
],
35+
resources: ['arn:aws:s3:::abc/xyz/123.txt'],
36+
}));
1637
```
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
// AWS::Chatbot CloudFormation Resources:
22
export * from './chatbot.generated';
3+
export * from './slack-channel-configuration';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import * as iam from '@aws-cdk/aws-iam';
2+
import * as sns from '@aws-cdk/aws-sns';
3+
import * as cdk from '@aws-cdk/core';
4+
import { CfnSlackChannelConfiguration } from './chatbot.generated';
5+
6+
/**
7+
* Properties for a new Slack channel configuration
8+
*/
9+
export interface SlackChannelConfigurationProps {
10+
11+
/**
12+
* The name of Slack channel configuration
13+
*/
14+
readonly slackChannelConfigurationName: string;
15+
16+
/**
17+
* The permission role of Slack channel configuration
18+
*
19+
* @default - A role will be created.
20+
*/
21+
readonly role?: iam.IRole;
22+
23+
/**
24+
* The ID of the Slack workspace authorized with AWS Chatbot.
25+
*
26+
* To get the workspace ID, you must perform the initial authorization flow with Slack in the AWS Chatbot console.
27+
* Then you can copy and paste the workspace ID from the console.
28+
* For more details, see steps 1-4 in Setting Up AWS Chatbot with Slack in the AWS Chatbot User Guide.
29+
* @see https://docs.aws.amazon.com/chatbot/latest/adminguide/setting-up.html#Setup_intro
30+
*/
31+
readonly slackWorkspaceId: string;
32+
33+
/**
34+
* The ID of the Slack channel.
35+
*
36+
* To get the ID, open Slack, right click on the channel name in the left pane, then choose Copy Link.
37+
* The channel ID is the 9-character string at the end of the URL. For example, ABCBBLZZZ.
38+
*/
39+
readonly slackChannelId: string;
40+
41+
/**
42+
* The SNS topics that deliver notifications to AWS Chatbot.
43+
*
44+
* @default None
45+
*/
46+
readonly notificationTopics?: sns.ITopic[];
47+
48+
/**
49+
* Specifies the logging level for this configuration.
50+
* This property affects the log entries pushed to Amazon CloudWatch Logs.
51+
*
52+
* @default LoggingLevel.NONE
53+
*/
54+
readonly loggingLevel?: LoggingLevel;
55+
}
56+
57+
/**
58+
* Logging levels include ERROR, INFO, or NONE.
59+
*/
60+
export enum LoggingLevel {
61+
/**
62+
* ERROR
63+
*/
64+
ERROR = 'ERROR',
65+
66+
/**
67+
* INFO
68+
*/
69+
INFO = 'INFO',
70+
71+
/**
72+
* NONE
73+
*/
74+
NONE = 'NONE',
75+
}
76+
77+
/**
78+
* Represents a Slack channel configuration
79+
*/
80+
export interface ISlackChannelConfiguration extends cdk.IResource, iam.IGrantable {
81+
82+
/**
83+
* The ARN of the Slack channel configuration
84+
* In the form of arn:aws:chatbot:{region}:{account}:chat-configuration/slack-channel/{slackChannelName}
85+
* @attribute
86+
*/
87+
readonly slackChannelConfigurationArn: string;
88+
89+
/**
90+
* The name of Slack channel configuration
91+
* @attribute
92+
*/
93+
readonly slackChannelConfigurationName: string;
94+
95+
/**
96+
* The permission role of Slack channel configuration
97+
* @attribute
98+
*
99+
* @default - A role will be created.
100+
*/
101+
readonly role?: iam.IRole;
102+
103+
/**
104+
* Adds a statement to the IAM role.
105+
*/
106+
addToRolePolicy(statement: iam.PolicyStatement): void;
107+
}
108+
109+
/**
110+
* Either a new or imported Slack channel configuration
111+
*/
112+
abstract class SlackChannelConfigurationBase extends cdk.Resource implements ISlackChannelConfiguration {
113+
abstract readonly slackChannelConfigurationArn: string;
114+
115+
abstract readonly slackChannelConfigurationName: string;
116+
117+
abstract readonly grantPrincipal: iam.IPrincipal;
118+
119+
abstract readonly role?: iam.IRole;
120+
121+
/**
122+
* Adds extra permission to iam-role of Slack channel configuration
123+
* @param statement
124+
*/
125+
public addToRolePolicy(statement: iam.PolicyStatement): void {
126+
if (!this.role) {
127+
return;
128+
}
129+
130+
this.role.addToPrincipalPolicy(statement);
131+
}
132+
}
133+
134+
/**
135+
* A new Slack channel configuration
136+
*/
137+
export class SlackChannelConfiguration extends SlackChannelConfigurationBase {
138+
139+
/**
140+
* Import an existing Slack channel configuration provided an ARN
141+
* @param scope The parent creating construct
142+
* @param id The construct's name
143+
* @param slackChannelConfigurationArn configuration ARN (i.e. arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack)
144+
*
145+
* @returns a reference to the existing Slack channel configuration
146+
*/
147+
public static fromSlackChannelConfigurationArn(scope: cdk.Construct, id: string, slackChannelConfigurationArn: string): ISlackChannelConfiguration {
148+
const re = /^slack-channel\//;
149+
const resourceName = cdk.Stack.of(scope).parseArn(slackChannelConfigurationArn).resourceName as string;
150+
151+
if (!re.test(resourceName)) {
152+
throw new Error('The ARN of a Slack integration must be in the form: arn:aws:chatbot:{region}:{account}:chat-configuration/slack-channel/{slackChannelName}');
153+
}
154+
155+
class Import extends SlackChannelConfigurationBase {
156+
157+
/**
158+
* @attribute
159+
*/
160+
readonly slackChannelConfigurationArn = slackChannelConfigurationArn;
161+
readonly role?: iam.IRole = undefined;
162+
readonly grantPrincipal: iam.IPrincipal;
163+
164+
/**
165+
* Returns a name of Slack channel configuration
166+
*
167+
* NOTE:
168+
* For example: arn:aws:chatbot::1234567890:chat-configuration/slack-channel/my-slack
169+
* The ArnComponents API will return `slack-channel/my-slack`
170+
* It need to handle that to gets a correct name.`my-slack`
171+
*/
172+
readonly slackChannelConfigurationName = resourceName.substring('slack-channel/'.length);
173+
174+
constructor(s: cdk.Construct, i: string) {
175+
super(s, i);
176+
this.grantPrincipal = new iam.UnknownPrincipal({ resource: this });
177+
}
178+
}
179+
180+
return new Import(scope, id);
181+
}
182+
183+
readonly slackChannelConfigurationArn: string;
184+
185+
readonly slackChannelConfigurationName: string;
186+
187+
readonly role?: iam.IRole;
188+
189+
readonly grantPrincipal: iam.IPrincipal;
190+
191+
constructor(scope: cdk.Construct, id: string, props: SlackChannelConfigurationProps) {
192+
super(scope, id, {
193+
physicalName: props.slackChannelConfigurationName,
194+
});
195+
196+
this.role = props.role || new iam.Role(this, 'ConfigurationRole', {
197+
assumedBy: new iam.ServicePrincipal('chatbot.amazonaws.com'),
198+
});
199+
200+
this.grantPrincipal = this.role;
201+
202+
const configuration = new CfnSlackChannelConfiguration(this, 'Resource', {
203+
configurationName: props.slackChannelConfigurationName,
204+
iamRoleArn: this.role.roleArn,
205+
slackWorkspaceId: props.slackWorkspaceId,
206+
slackChannelId: props.slackChannelId,
207+
snsTopicArns: props.notificationTopics?.map(topic => topic.topicArn),
208+
loggingLevel: props.loggingLevel?.toString(),
209+
});
210+
211+
this.slackChannelConfigurationArn = configuration.ref;
212+
this.slackChannelConfigurationName = props.slackChannelConfigurationName;
213+
}
214+
}
215+

packages/@aws-cdk/aws-chatbot/package.json

+9-2
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,21 @@
6868
"devDependencies": {
6969
"@aws-cdk/assert": "0.0.0",
7070
"cdk-build-tools": "0.0.0",
71+
"cdk-integ-tools": "0.0.0",
7172
"cfn2ts": "0.0.0",
7273
"pkglint": "0.0.0"
7374
},
7475
"dependencies": {
75-
"@aws-cdk/core": "0.0.0"
76+
"@aws-cdk/aws-iam": "0.0.0",
77+
"@aws-cdk/aws-sns": "0.0.0",
78+
"@aws-cdk/core": "0.0.0",
79+
"constructs": "^3.0.4"
7680
},
7781
"peerDependencies": {
78-
"@aws-cdk/core": "0.0.0"
82+
"@aws-cdk/aws-iam": "0.0.0",
83+
"@aws-cdk/aws-sns": "0.0.0",
84+
"@aws-cdk/core": "0.0.0",
85+
"constructs": "^3.0.4"
7986
},
8087
"engines": {
8188
"node": ">= 10.13.0 <13 || >=13.7.0"

packages/@aws-cdk/aws-chatbot/test/chatbot.test.ts

-6
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"Resources": {
3+
"MySlackChannelConfigurationRole1D3F23AE": {
4+
"Type": "AWS::IAM::Role",
5+
"Properties": {
6+
"AssumeRolePolicyDocument": {
7+
"Statement": [
8+
{
9+
"Action": "sts:AssumeRole",
10+
"Effect": "Allow",
11+
"Principal": {
12+
"Service": "chatbot.amazonaws.com"
13+
}
14+
}
15+
],
16+
"Version": "2012-10-17"
17+
}
18+
}
19+
},
20+
"MySlackChannelConfigurationRoleDefaultPolicyE4C1FA62": {
21+
"Type": "AWS::IAM::Policy",
22+
"Properties": {
23+
"PolicyDocument": {
24+
"Statement": [
25+
{
26+
"Action": "s3:GetObject",
27+
"Effect": "Allow",
28+
"Resource": "arn:aws:s3:::abc/xyz/123.txt"
29+
}
30+
],
31+
"Version": "2012-10-17"
32+
},
33+
"PolicyName": "MySlackChannelConfigurationRoleDefaultPolicyE4C1FA62",
34+
"Roles": [
35+
{
36+
"Ref": "MySlackChannelConfigurationRole1D3F23AE"
37+
}
38+
]
39+
}
40+
},
41+
"MySlackChannelA8E0B56C": {
42+
"Type": "AWS::Chatbot::SlackChannelConfiguration",
43+
"Properties": {
44+
"ConfigurationName": "test-channel",
45+
"IamRoleArn": {
46+
"Fn::GetAtt": [
47+
"MySlackChannelConfigurationRole1D3F23AE",
48+
"Arn"
49+
]
50+
},
51+
"SlackChannelId": "C0187JABUE9",
52+
"SlackWorkspaceId": "T49239U4W",
53+
"LoggingLevel": "NONE"
54+
}
55+
}
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as iam from '@aws-cdk/aws-iam';
2+
import * as cdk from '@aws-cdk/core';
3+
import * as chatbot from '../lib';
4+
5+
class ChatbotInteg extends cdk.Stack {
6+
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
7+
super(scope, id, props);
8+
9+
const slackChannel = new chatbot.SlackChannelConfiguration(this, 'MySlackChannel', {
10+
slackChannelConfigurationName: 'test-channel',
11+
slackWorkspaceId: 'T49239U4W', // modify to your slack workspace id
12+
slackChannelId: 'C0187JABUE9', // modify to your slack channel id
13+
loggingLevel: chatbot.LoggingLevel.NONE,
14+
});
15+
16+
slackChannel.addToRolePolicy(new iam.PolicyStatement({
17+
effect: iam.Effect.ALLOW,
18+
actions: [
19+
's3:GetObject',
20+
],
21+
resources: ['arn:aws:s3:::abc/xyz/123.txt'],
22+
}));
23+
}
24+
}
25+
26+
const app = new cdk.App();
27+
28+
new ChatbotInteg(app, 'ChatbotInteg');
29+
30+
app.synth();
31+

0 commit comments

Comments
 (0)