How to build a Slackbot in AWS

Aws Lambda makes it simple to build serverless applications. This tutorial is to build a simple Slackbot using API Gateway, DynamoDB, and Lambda using Python. Send a key to lambda. Return a value to Slack.

This is part of a larger webinar around logging a distributed system. Click here to view full webinar.

Below is the extended section on building a Slackbot.

Pre Reqs

  1. AWS Account
  2. Slack Account
  3. AWS CLI

Instructions

Video Tutorial Here

Create Slack Bot

  1. Create lambda function
  2. Create from blueprint
  3. Select “slack-echo-command-python”
  4. Name the function
  5. Create policy in new tab https://console.aws.amazon.com/iam/home?region=us-east-1#/policies > Select “JSON” > paste the following

Policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "lambda:CreateFunction",
                "lambda:TagResource",
                "lambda:GetLayerVersion",
                "logs:*",
                "lambda:PublishLayerVersion",
                "lambda:DeleteProvisionedConcurrencyConfig",
                "dynamodb:*",
                "lambda:InvokeAsync",
                "lambda:GetAccountSettings",
                "lambda:GetFunctionConfiguration",
                "lambda:CreateEventSourceMapping",
                "lambda:GetLayerVersionPolicy",
                "lambda:UntagResource",
                "lambda:PutFunctionConcurrency",
                "lambda:GetProvisionedConcurrencyConfig",
                "lambda:ListTags",
                "kms:*",
                "lambda:DeleteLayerVersion",
                "lambda:PutFunctionEventInvokeConfig",
                "lambda:DeleteFunctionEventInvokeConfig",
                "lambda:DeleteFunction",
                "lambda:GetAlias",
                "lambda:UpdateFunctionEventInvokeConfig",
                "lambda:UpdateEventSourceMapping",
                "lambda:GetEventSourceMapping",
                "lambda:InvokeFunction",
                "apigateway:*",
                "lambda:GetFunction",
                "lambda:UpdateFunctionConfiguration",
                "lambda:UpdateAlias",
                "lambda:UpdateFunctionCode",
                "cloudwatch:*",
                "lambda:GetFunctionConcurrency",
                "lambda:GetFunctionEventInvokeConfig",
                "lambda:PutProvisionedConcurrencyConfig",
                "lambda:DeleteAlias",
                "lambda:PublishVersion",
                "lambda:DeleteFunctionConcurrency",
                "lambda:DeleteEventSourceMapping",
                "lambda:GetPolicy",
                "lambda:CreateAlias"
            ],
            "Resource": "*"
        }
    ]
}
  1. Create role https://console.aws.amazon.com/iam/home?region=us-east-1#/roles$new?step=type
  2. Select Lambda under “Choose Use Case” then press “Next: Permissions”
  3. Search for the policy you just created
  4. Next: Tags > Next: Review
  5. Name it and “Create Role”
  6. Go back to your previous tab and under “Execution role” select the role you just created
  7. Under “API Gateway trigger” select “Create New”
  8. choose “REST API”
  9. under “Security” select “Open”
  10. do not check “Enable metrics and error logging” we will do that later
  11. Create new KMS service https://console.aws.amazon.com/kms/home?region=us-east-1#/kms/keys/create
  12. select Symmetric
  13. assign an admin i used my user account
  14. select the role we created above
  15. navigate to slack.com/apps
  16. Search for Slash Commands
  17. Create new give name /slackbot-name
  18. copy the token
  19. paste the token
  20. Expand Encryption configuration
  21. Enable helpers for encryption in transit
  22. Once the function is created find api gateway in the “source” section and expand it. Paste the url in slack
  23. Save.
  24. Create DDB Table aws dynamodb create-table –cli-input-json file://table.json

Table Schema table.json

{
    "AttributeDefinitions": [
    {
        "AttributeName": "Id",
        "AttributeType": "N"
    }
    ],
        "ProvisionedThroughput": {
            "WriteCapacityUnits": 5,
            "ReadCapacityUnits": 5
        },
        "TableName": "ProductCatalog",
        "KeySchema": [
        {
            "KeyType": "HASH",
            "AttributeName": "Id"
        }
    ]
}

25. Write sample date or your own data. aws dynamodb batch-write-item –request-items file://ProductCatalog.json

Product Catalog ProductCatalog.json

{
    "ProductCatalog": [
        {
            "PutRequest": {
                "Item": {
                    "Id": {
                        "N": "101"
                    },
                    "Title": {
                        "S": "Book 101 Title"
                    },
                    "ISBN": {
                        "S": "111-1111111111"
                    },
                    "Authors": {
                        "L": [
                            {
                                "S": "Author1"
                            }
                        ]
                    },
                    "Price": {
                        "N": "2"
                    },
                    "Dimensions": {
                        "S": "8.5 x 11.0 x 0.5"
                    },
                    "PageCount": {
                        "N": "500"
                    },
                    "InPublication": {
                        "BOOL": true
                    },
                    "ProductCategory": {
                        "S": "Book"
                    }
                }
            }
        },
        {
            "PutRequest": {
                "Item": {
                    "Id": {
                        "N": "102"
                    },
                    "Title": {
                        "S": "Book 102 Title"
                    },
                    "ISBN": {
                        "S": "222-2222222222"
                    },
                    "Authors": {
                        "L": [
                            {
                                "S": "Author1"
                            },
                            {
                                "S": "Author2"
                            }
                        ]
                    },
                    "Price": {
                        "N": "20"
                    },
                    "Dimensions": {
                        "S": "8.5 x 11.0 x 0.8"
                    },
                    "PageCount": {
                        "N": "600"
                    },
                    "InPublication": {
                        "BOOL": true
                    },
                    "ProductCategory": {
                        "S": "Book"
                    }
                }
            }
        },
        {
            "PutRequest": {
                "Item": {
                    "Id": {
                        "N": "103"
                    },
                    "Title": {
                        "S": "Book 103 Title"
                    },
                    "ISBN": {
                        "S": "333-3333333333"
                    },
                    "Authors": {
                        "L": [
                            {
                                "S": "Author1"
                            },
                            {
                                "S": "Author2"
                            }
                        ]
                    },
                    "Price": {
                        "N": "2000"
                    },
                    "Dimensions": {
                        "S": "8.5 x 11.0 x 1.5"
                    },
                    "PageCount": {
                        "N": "600"
                    },
                    "InPublication": {
                        "BOOL": false
                    },
                    "ProductCategory": {
                        "S": "Book"
                    }
                }
            }
        },
        {
            "PutRequest": {
                "Item": {
                    "Id": {
                        "N": "201"
                    },
                    "Title": {
                        "S": "18-Bike-201"
                    },
                    "Description": {
                        "S": "201 Description"
                    },
                    "BicycleType": {
                        "S": "Road"
                    },
                    "Brand": {
                        "S": "Mountain A"
                    },
                    "Price": {
                        "N": "100"
                    },
                    "Color": {
                        "L": [
                            {
                                "S": "Red"
                            },
                            {
                                "S": "Black"
                            }
                        ]
                    },
                    "ProductCategory": {
                        "S": "Bicycle"
                    }
                }
            }
        },
        {
            "PutRequest": {
                "Item": {
                    "Id": {
                        "N": "202"
                    },
                    "Title": {
                        "S": "21-Bike-202"
                    },
                    "Description": {
                        "S": "202 Description"
                    },
                    "BicycleType": {
                        "S": "Road"
                    },
                    "Brand": {
                        "S": "Brand-Company A"
                    },
                    "Price": {
                        "N": "200"
                    },
                    "Color": {
                        "L": [
                            {
                                "S": "Green"
                            },
                            {
                                "S": "Black"
                            }
                        ]
                    },
                    "ProductCategory": {
                        "S": "Bicycle"
                    }
                }
            }
        },
        {
            "PutRequest": {
                "Item": {
                    "Id": {
                        "N": "203"
                    },
                    "Title": {
                        "S": "19-Bike-203"
                    },
                    "Description": {
                        "S": "203 Description"
                    },
                    "BicycleType": {
                        "S": "Road"
                    },
                    "Brand": {
                        "S": "Brand-Company B"
                    },
                    "Price": {
                        "N": "300"
                    },
                    "Color": {
                        "L": [
                            {
                                "S": "Red"
                            },
                            {
                                "S": "Green"
                            },
                            {
                                "S": "Black"
                            }
                        ]
                    },
                    "ProductCategory": {
                        "S": "Bicycle"
                    }
                }
            }
        },
        {
            "PutRequest": {
                "Item": {
                    "Id": {
                        "N": "204"
                    },
                    "Title": {
                        "S": "18-Bike-204"
                    },
                    "Description": {
                        "S": "204 Description"
                    },
                    "BicycleType": {
                        "S": "Mountain"
                    },
                    "Brand": {
                        "S": "Brand-Company B"
                    },
                    "Price": {
                        "N": "400"
                    },
                    "Color": {
                        "L": [
                            {
                                "S": "Red"
                            }
                        ]
                    },
                    "ProductCategory": {
                        "S": "Bicycle"
                    }
                }
            }
        },
        {
            "PutRequest": {
                "Item": {
                    "Id": {
                        "N": "205"
                    },
                    "Title": {
                        "S": "18-Bike-204"
                    },
                    "Description": {
                        "S": "205 Description"
                    },
                    "BicycleType": {
                        "S": "Hybrid"
                    },
                    "Brand": {
                        "S": "Brand-Company C"
                    },
                    "Price": {
                        "N": "500"
                    },
                    "Color": {
                        "L": [
                            {
                                "S": "Red"
                            },
                            {
                                "S": "Black"
                            }
                        ]
                    },
                    "ProductCategory": {
                        "S": "Bicycle"
                    }
                }
            }
        }
    ]
}

26. Replace lambda python code.

import boto3
import json
import logging
import os

from base64 import b64decode
from urlparse import parse_qs

#added from webinar
from boto3 import resource
from boto3.dynamodb.conditions import Key




ENCRYPTED_EXPECTED_TOKEN = os.environ['kmsEncryptedToken']

kms = boto3.client('kms')
expected_token = kms.decrypt(CiphertextBlob=b64decode(ENCRYPTED_EXPECTED_TOKEN))['Plaintext']

logger = logging.getLogger()
logger.setLevel(logging.INFO)



def ddb(key):
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('ProductCatalog')
    response = table.get_item(
        Key={
            'Id': key,
        }
        )

    return response['Item']
    
    
def respond(err, res=None):
    return {
        'statusCode': '400' if err else '200',
        'body': err.message if err else json.dumps(res),
        'headers': {
            'Content-Type': 'application/json',
        },
    }


def lambda_handler(event, context):
    print(json.dumps(event))
    params = parse_qs(event['body'])
    token = params['token'][0]
    if token != expected_token:
        logger.error("Request token (%s) does not match expected", token)
        return respond(Exception('Invalid request token'))

    user = params['user_name'][0]
    command = params['command'][0]
    channel = params['channel_name'][0]
    if 'text' in params:
        command_text = params['text'][0]
        command_value = ddb(int(command_text))
    else:
        command_text = ''
        command_value = 'val'

    response = respond(None, "%s invoked %s in %s searching for the following key %s and returned the following item %s" % (user, command, channel, command_text, command_value))
    logger.info(response)
    return response

27. Navigate to slack and test it out.

/slackbot-name