Slack Integration

You can use the SNS topic (optional) for any purpose you like. Here are an example and a CloudFormation template to set up lambda to integrate with slack.

Step 1 - Create a slack bot

Create a slack bot and install it in your workspace. Be sure to add chat:write scope, you can find slack documents here and here. Get the bot token after the bot is created.

Step 2 - Create secert in AWS Secrets Manager

Create a secret with the name slack-token in AWS Secrets Manager, see AWS documentation here. You don’t need to use key-value pair for the secret, since the only content stored is the slack bot token.

Step 3 - Create a lambda function using CloudFormation

You can use below CloudFormation template to create a lambda function, subscribe to your SNS topic, and use the secret you created above. Change the channel name below before creating the stack. When creating the stack with the below template, it will ask you for the SNS topic ARN and secret ARN.

{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
    "slackTokenSecretArn": {
    "Type": "String"
    },
    "snsTopicArn": {
    "Type": "String"
    }
},
"Resources": {
    "SnsSubscription": {
    "Type" : "AWS::SNS::Subscription",
    "Properties" : {
        "Endpoint" : {
            "Fn::GetAtt": [
            "SlackLambda",
            "Arn"
            ]
        },
        "Protocol" : "lambda",
        "TopicArn" : {
            "Ref": "snsTopicArn"
        }
        }
    },
    "LambdaResourcePolicy": {
    "Type": "AWS::Lambda::Permission",
    "Properties": {
        "FunctionName": {
        "Fn::GetAtt": [
            "SlackLambda",
            "Arn"
        ]
        },
        "Principal": "sns.amazonaws.com",
        "Action": "lambda:InvokeFunction",
        "SourceArn": {
        "Ref": "snsTopicArn"
        }
    }
    },
    "LambdaExecutionRole": {
    "Type": "AWS::IAM::Role",
    "Properties": {
        "AssumeRolePolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                "lambda.amazonaws.com"
                ]
            },
            "Action": [
                "sts:AssumeRole"
            ]
            }
        ]
        },
        "Path": "/",
        "Policies": [
        {
            "PolicyName": "root",
            "PolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                "Effect": "Allow",
                "Action": [
                    "logs:*"
                ],
                "Resource": "arn:aws:logs:*:*:*"
                }
            ]
            }
        },
        {
            "PolicyName": "secret",
            "PolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                "Effect": "Allow",
                "Action": [
                    "secretsmanager:GetSecretValue"
                ],
                "Resource": {
                    "Ref": "slackTokenSecretArn"
                }
                }
            ]
            }
        }
        ]
    }
    },
    "SlackLambda": {
    "Type": "AWS::Lambda::Function",
    "Properties": {
        "FunctionName": "SlackLambda",
        "Handler": "index.handler",
        "MemorySize": 1024,
        "Role": {
        "Fn::GetAtt": [
            "LambdaExecutionRole",
            "Arn"
        ]
        },
        "Runtime": "nodejs12.x",
        "Timeout": 15,
        "Code": {
        "ZipFile": {
            "Fn::Join": [
            "\n",
            [
                "const AWS = require(\"aws-sdk\"),",
                "    qs = require(\"querystring\"),",
                "    https = require(\"https\");",
                "",
                "exports.handler = async (event) => {",
                "",
                "    console.log(\"Event:\", JSON.stringify(event));",
                "",
                "    try {",
                "",
                "",
                "        if (typeof (event) !== \"undefined\") {",
                "",
                "            var message = {",
                "                channel: '#put_your_channel_name_here',",
                "                attachments: [",
                "                    {",
                "                        fallback: `DataRow.io: ${event.Title}`,",
                "                        pretext: event.Records[0].Sns.Subject,",
                "                        title: event.Records[0].Sns.Subject,",
                "                        text: event.Records[0].Sns.Message,",
                "                        color: \"danger\"",
                "                    }",
                "                ]",
                "            };",
                "",
                "            var postBody = JSON.stringify(message);",
                "",
                "            var client = new AWS.SecretsManager({region: process.env.AWS_REGION});",
                "            const data = await client.getSecretValue({SecretId: \"slack-token\"}).promise();",
                "            var options = {",
                "                hostname: \"slack.com\",",
                "                port: 443,",
                "                path: \"/api/chat.postMessage\",",
                "                method: \"POST\",",
                "                headers: {",
                "                    \"Authorization\": `Bearer ${data.SecretString}`,",
                "                    \"Content-Type\": \"application/json; charset=utf-8\",",
                "                    \"Content-Length\": postBody.length",
                "                }",
                "            };",
                "",
                "            var res = await new Promise((resolve, reject) => {",
                "                var postreq = https.request(options, (res) => {",
                "                    console.log(\"statusCode:\", res.statusCode);",
                "                    console.log(\"statusCode:\", res.statusMessage);",
                "                    res.on('data', (d) => {",
                "                                console.log(d.toString('utf8'))  ",
                "                     });  ",
                "                    resolve(\"Success\");",
                "                });",
                "                postreq.on(\"error\", (e) => {",
                "                    console.error(e);",
                "                    reject(e.message);",
                "                });",
                "                postreq.write(postBody);",
                "                postreq.end();",
                "            });",
                "",
                "            return res;",
                "        } else {",
                "            console.log(\"Slack event is missing: \", event);",
                "            return \"Slack event is missing.\";",
                "        }",
                "    }",
                "    catch (err) {",
                "        console.log(err);",
                "        return err;",
                "    }",
                "};"
            ]
            ]
        }
        }
    }
    }
}
}