diff --git a/packages/@aws-cdk/aws-chatbot/README.md b/packages/@aws-cdk/aws-chatbot/README.md index 6f868ff02e17d..b4b92719c1583 100644 --- a/packages/@aws-cdk/aws-chatbot/README.md +++ b/packages/@aws-cdk/aws-chatbot/README.md @@ -52,3 +52,8 @@ allows you to customize the maximum number of retries and base backoff duration. resource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cfn-customresource.html) is added to the stack that pre-creates the log group as part of the stack deployment, if it already doesn't exist, and sets the correct log retention period (never expire, by default). + +## Guardrails + +By default slack channel will use `AdministratorAccess` managed policy as guardrail policy. +The `guardrailPolicies` property can be used to set a different set of managed policies. 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 3960c3675f389..ccba363517688 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,12 @@ export interface SlackChannelConfigurationProps { * @default - Default AWS SDK retry options. */ readonly logRetentionRetryOptions?: logs.LogRetentionRetryOptions; + + /** + * A list of IAM managed policies that are applied as channel guardrails. + * @default - The AWS managed 'AdministratorAccess' policy is applied as a default if this is not set. + */ + readonly guardrailPolicies?: iam.IManagedPolicy[]; } /** @@ -293,6 +299,7 @@ export class SlackChannelConfiguration extends SlackChannelConfigurationBase { slackChannelId: props.slackChannelId, snsTopicArns: cdk.Lazy.list({ produce: () => this.notificationTopics.map(topic => topic.topicArn) }, { omitEmpty: true } ), loggingLevel: props.loggingLevel?.toString(), + guardrailPolicies: cdk.Lazy.list({ produce: () => props.guardrailPolicies?.map(policy => policy.managedPolicyArn) }, { omitEmpty: true } ), }); // Log retention diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/ChatbotGuardrailsInteg.assets.json b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/ChatbotGuardrailsInteg.assets.json new file mode 100644 index 0000000000000..5b39c0206c29b --- /dev/null +++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/ChatbotGuardrailsInteg.assets.json @@ -0,0 +1,19 @@ +{ + "version": "29.0.0", + "files": { + "6c1ea5243130449f4b215f7eac487f2dd07715c6751857440bbbfeb883e736ce": { + "source": { + "path": "ChatbotGuardrailsInteg.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "6c1ea5243130449f4b215f7eac487f2dd07715c6751857440bbbfeb883e736ce.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/ChatbotGuardrailsInteg.template.json b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/ChatbotGuardrailsInteg.template.json new file mode 100644 index 0000000000000..d0215614625fd --- /dev/null +++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/ChatbotGuardrailsInteg.template.json @@ -0,0 +1,83 @@ +{ + "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-channel", + "IamRoleArn": { + "Fn::GetAtt": [ + "MySlackChannelConfigurationRole1D3F23AE", + "Arn" + ] + }, + "SlackChannelId": "C0187JABUE9", + "SlackWorkspaceId": "T49239U4W", + "GuardrailPolicies": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/CloudWatchReadOnlyAccess" + ] + ] + } + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/cdk.out b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/cdk.out new file mode 100644 index 0000000000000..d8b441d447f8a --- /dev/null +++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"29.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/manifest.json b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/manifest.json new file mode 100644 index 0000000000000..7fcd256d8f3ce --- /dev/null +++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/manifest.json @@ -0,0 +1,70 @@ +{ + "version": "29.0.0", + "artifacts": { + "ChatbotGuardrailsInteg.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "ChatbotGuardrailsInteg.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "ChatbotGuardrailsInteg": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "ChatbotGuardrailsInteg.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/6c1ea5243130449f4b215f7eac487f2dd07715c6751857440bbbfeb883e736ce.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "ChatbotGuardrailsInteg.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "ChatbotGuardrailsInteg.assets" + ], + "metadata": { + "/ChatbotGuardrailsInteg/MySlackChannel/ConfigurationRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MySlackChannelConfigurationRole1D3F23AE" + } + ], + "/ChatbotGuardrailsInteg/MySlackChannel/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MySlackChannelA8E0B56C" + } + ], + "/ChatbotGuardrailsInteg/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/ChatbotGuardrailsInteg/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "ChatbotGuardrailsInteg" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/tree.json b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/tree.json new file mode 100644 index 0000000000000..bc5b7e2328655 --- /dev/null +++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.js.snapshot/tree.json @@ -0,0 +1,152 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "ChatbotGuardrailsInteg": { + "id": "ChatbotGuardrailsInteg", + "path": "ChatbotGuardrailsInteg", + "children": { + "MyPolicy": { + "id": "MyPolicy", + "path": "ChatbotGuardrailsInteg/MyPolicy", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "MySlackChannel": { + "id": "MySlackChannel", + "path": "ChatbotGuardrailsInteg/MySlackChannel", + "children": { + "ConfigurationRole": { + "id": "ConfigurationRole", + "path": "ChatbotGuardrailsInteg/MySlackChannel/ConfigurationRole", + "children": { + "ImportConfigurationRole": { + "id": "ImportConfigurationRole", + "path": "ChatbotGuardrailsInteg/MySlackChannel/ConfigurationRole/ImportConfigurationRole", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "ChatbotGuardrailsInteg/MySlackChannel/ConfigurationRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "chatbot.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "ChatbotGuardrailsInteg/MySlackChannel/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Chatbot::SlackChannelConfiguration", + "aws:cdk:cloudformation:props": { + "configurationName": "test-channel", + "iamRoleArn": { + "Fn::GetAtt": [ + "MySlackChannelConfigurationRole1D3F23AE", + "Arn" + ] + }, + "slackChannelId": "C0187JABUE9", + "slackWorkspaceId": "T49239U4W", + "guardrailPolicies": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":Policy/CloudWatchReadOnlyAccess" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-chatbot.CfnSlackChannelConfiguration", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-chatbot.SlackChannelConfiguration", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "ChatbotGuardrailsInteg/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "ChatbotGuardrailsInteg/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.237" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.ts b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.ts new file mode 100644 index 0000000000000..ab3ce8d845445 --- /dev/null +++ b/packages/@aws-cdk/aws-chatbot/test/integ.chatbot-guardrails.ts @@ -0,0 +1,24 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as cdk from '@aws-cdk/core'; +import * as chatbot from '../lib'; + +class ChatbotGuardrailsInteg extends cdk.Stack { + constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const guardrailPolicy = iam.ManagedPolicy.fromAwsManagedPolicyName('CloudWatchReadOnlyAccess'); + + new chatbot.SlackChannelConfiguration(this, 'MySlackChannel', { + slackChannelConfigurationName: 'test-channel', + slackWorkspaceId: 'T49239U4W', // modify to your slack workspace id + slackChannelId: 'C0187JABUE9', // modify to your slack channel id + guardrailPolicies: [guardrailPolicy], + }); + } +} + +const app = new cdk.App(); + +new ChatbotGuardrailsInteg(app, 'ChatbotGuardrailsInteg'); + +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 index 9918dba29a1a1..49314e8572f3e 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 @@ -282,4 +282,19 @@ describe('SlackChannelConfiguration', () => { Ref: 'ARN', }); }); + + test('guardrail policy should be configured by ARN when specified', () => { + new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', { + slackWorkspaceId: 'ABC123', + slackChannelId: 'DEF456', + slackChannelConfigurationName: 'ConfigurationName', + guardrailPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('CloudWatchReadOnlyAccess')], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Chatbot::SlackChannelConfiguration', { + GuardrailPolicies: [ + { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::aws:policy/CloudWatchReadOnlyAccess']] }, + ], + }); + }); }); \ No newline at end of file