Lambda Slack



This post is intended to walk you through creating a Slackbot with AWS Lambda and Lex. I am not using any of the Lambda frameworks like Serverless or Apex in this post instead demonstrates how it is possible to create a Lambda function with pure Node and deploy using HashiCorps Terraform.The simple application is a bot which listens for messages that correspond to the phrase “Which timezone is [user] in?” it then calls the Slack API and retrieve the timezone for that particular user. Lex has the capability of defining an Intent which is a request; the natural language processing means that we do not need to cater for every permutation of the question such as:

Get CloudWatch error logs notifacations on Slack when you work on AWS Lambda. This can be done easily with Sigma, the serverless IDE by SLAppForge. Blog Timbercode.pl. We use this to notify the security team on Slack by configuring a CloudWatch Event Rule on GuardDuty findings that triggers a Lambda serverless function written in Go called GuardDuty2Slack. This post will walk you through the process and code used to join member accounts to an organization and send GuardDuty findings as Slack notifications.

  • Which timezone is nic in?
  • What timezone is nic in?
  • Hey @otheruser what timezone is nic in?The Lex runtime selects the best intent based on its natural language processing. In addition to this, we can define variables or Slots which Lex extracts out of the intent and pass to the function.
  1. Meet your instructors. Start by meeting a few of the web development and Data Science instructors.
  2. Since lambda functions are just functions, we can use Mocha, Chai, Sinon and Proxyquire. Now that we have it as an object from the body mapping we can create a sample response from Slack: I've put all the Slack events into test/slackEvents/: The pongbot has one command at the moment.

You can find the source code for this post at https://github.com/nicholasjackson/slack-bot-lex-lambda:

Step 1: Creating the Lambda function

To add interactivity to Lex we can pass an intent to a Lambda function, in our example, this function is going to call the slack API and fetch the Timezone that matches the given user.

file://src/index.js

Lambda

If we take a look at src/index.js in the code repository, we see that it is a relatively simple Javascript lambda handler.

The first thing to note is line 5 because we are going to be bundling a couple of node modules with our Lambda function we need to configure the environment path to be able to pick these up. The path only needs to be set once and not on every file.We are also importing a couple of local modules slack which is our slack client and dispatcher which encapsulates the logic for processing the Lex request. These have been separated out into modules to allow individual unit testing.

line 24 is where we are calling the dispatcher to process the request, we pass to this the reference to the slack client, again separating dependency and increasing testability.

file://src/dispatcher.js

Looking at the file src/dispatcher.js we export a single function which is the one we are calling from our main handler.

The request that is sent to the function from Lex looks something like this:

Lambda slack post

As we mentioned in the first section, Lex has the capability to extract information from the request and to include it in slots passed as an object to your function.The first thing we are doing in our function is to determine if this Lambda function can handle the intent. On line 92 we are checking this; it is not necessary to perform this check however it could save some headaches if you accidentally wire the wrong function to an Intent.We then call the slack client on line 95 and pass a callback which crafts the response to send back to Lex. The main part of this code to note is line 110, in this instance, we are sending back a Close response to Lex, this sends the message that the response is final and no further interaction takes place for this session. Other valid values are ElicitSlot and ConfirmIntent, the example code has helper functions which can craft the correct responses however for the purpose of simplicity we are only using the close function in this walkthrough.

sessionAttributes are what Lex uses to pass between client and code as it does not log and store these values. A responseCard if used is passed back to slack and can be used in addition to the message to request further information in the form of lists to prompt the user for action. For example, in our application, if we do not find the user mentioned we could send back a list of possible matches, and the user could then click on this item rather than having to type the response.fulfillmentState is a string variable which contains the options of “Fulfilled” or “Failed”.Moreover, the message is an object with the syntax:

file://src/dispatcher.test.js

As an investigation into the workflow for building lambda functions with JavaScript, I have configured a couple of tests for the dispatcher which is the most complicated part of the function.

Using dependency injection on our dispatcher, we can isolate the slack client giving us the capability to unit test the dispatcher. Jest provides some nice matchers for the assertions and if needed also has a comprehensive mocking framework. These tests are by no mean comprehensive or fully covering but they do indicate that it is possible to write Lambda TDD style, and with good test coverage can make up for the inability to easily run locally. The serverless framework does provide the capability to run in dev mode, but my opinion is that by removing this crutch you are pushed to producing a high-quality test suite which carries far greater benefit with long term maintenance.

file://src/slack.js

Our slack client is fairly straight forward and makes a call to the users.list endpoint on the slack API, iterates over the list and selects the timezone for the matching user. At the time of writing, there was no way to directly retrieve a slack user using the API with only having the name of the user, not the ID.

The main thing to note in this function is that we need the slack API token which is set in the Lambda setup when we deploy the function.

Step 2: Deploying the Lambda function

To deploy the lambda function uses Terraform by HashiCorp, the config for the deployment is found in the terraform folder.For this, to work, we need valid AWS credentials and the Slack API token set to an environment variables on your machine. I recommend using direnv and a .encrc file to manage this rather than setting the variables globally. The variables which we need to set are:

  • AWS_ACCESS_KEY - Your aws access key for the account
  • AWS_SECRET_ACCESS_KEY - The secret which corresponds to your access key
  • TF_VAR_SLACK_API_KEY - A valid API token which can read the users details in slack

file://terraform/lambda.tf

Lambda Slacker

If we take a look at the terraform/lambda.tf we will see that we are defining a IAM role and policy for lambda which allows Lambda to write to the CloudWatch logs. We are also creating an aws_lambda_function resource.

Lambda Slack

s3_bucket is the s3 bucket where our application code is stored, and the value of this is a reference to another bit of Terraform config.
runtime is specifying that we would like to use the nodejs4.3 runtime for lambda.
function_name this is the name we would like to call our lambda function which is set in the file terraform/terraform.tfvars also, has a value of slackbot.
role this is the IAM role that we created earlier in the config.
s3_key this is the id of the zip file that we have uploaded to s3 which contains our source code.source_code_hash this is the hash code of our uploaded zip file, should we change the contents of the source code then this hash will be different and triggers a deployment of a new version.
environment for our application to function we need a slack API key, and for simplicity, we are adding this to the environment variables. It is important to note that it is possible to encrypt this using KMS however we are storing this plain text.

file://terraform/s3.tf

This file contains the terraform config to create an s3 bucket where our source code lives and also creates the archive and ensures that upon deploying, the archive is added to the bucket.

Lambda Slack

We can use a data element archive_file which will create a zip archive for us when terraform runs. We are setting the paths for the source_dir which is the folder created when we run make build, and the output is a file called lambda.zip in our source code root.
Terraform also has the capability to create an s3 bucket for us, if you already have a bucket that you would like to use then you can skip this resource element and directly add the arn to the bucket attribute of the aws_s3_bucket stanza. If already have an S3 bucket you would like to use for your source code remember that it must exist in the us-east-1 zone until Lex is rolled out across the other zones in AWS.

Deploying the function

Lambda Slack Nodejs

To deploy the function we can run:

Lambda Stack Spyder

These commands install the yarn dependencies for testing and then run the test, build, and deploy steps, the deploy step simply runs terraform plan && terraform apply . The packaging step only needs to be run once or when you would like to update jest.

You will see some output on the command line:

If you take a look at your AWS Lambda dashboard (N.Virginia) you should now see a new function and we can begin to configure Lex and Slack.

Step 3 Configuring AWS Lex and Slack

  1. Sign in to the AWS Management Console and open the Amazon Lex console at https://console.aws.amazon.com/lex/.
  2. Click the Create button and choose Custom Bot fill in the details and then press Create.
  3. Click Create intent and the Create new Intent
  4. Enter the title of the intent “WhatTimezone” and press Create
  5. Add the utterances “What timezone is {User} in” and the slots User which has the type AMAZON.Person. Select our lambda function in the Fulfillment section and then save the intent.
  6. To test our bot we need to build it so press the Build button in the top right-hand corner, the test window will then pop up and you can test your bot is working before connecting it to slack. If you have called your Intent something different to “WhatTimezone” then you need to update the code in src/dispatcher.js accordingly.
  7. We can now publish the function by pressing the Publish button in the top right-hand corner and proceed to configure Slack.
  8. The default instructions from AWS are just fine for configuring slack, so rather than reproduce them verbatim I encourage you to follow steps 3 through 6 in the following guide. http://docs.aws.amazon.com/lex/latest/dg/slack-bot-association.html. One important thing to note is that when you are setting up your channel you will be asked for a Verification Token, Slack only shows the Verification token on the Basic Information screen once you have added an Event Subscription however until you add the verification token AWS will not give you the URL. To get round this click on Event Subscriptions enter any URL and once you have a verification failed message again click on Basic Information.
Slack

Step 4 testing in Slack

Once everything has been configured, you can then go to your slack team invite the slackbot you have just created to the channel and then when you write a message like “What timezone is nicjackson in?”, the bot should reply with the correct answer.

Summary

This example is only a simple based on some investigation I did at the weekend into Lex; however, it has been quite fun, and I will certainly continue to work on this and add more advanced functionality. All the resources and links for this talk are listed below, and if you need any assistance, I can be found on Twitter.

Source Code:
https://github.com/nicholasjackson/slack-bot-lex-lambda
Lex Developer Guide:
http://docs.aws.amazon.com/lex/latest/dg/what-is.html
Lex Input and Response Format:
http://docs.aws.amazon.com/lex/latest/dg/lambda-input-response-format.html
Terraform:
https://www.terraform.io
Jest:
https://facebook.github.io/jest/

You may have already found yourself in front of a bad surprise at the end of the month, when you realize your cloud bill was much more than you expected. Perhaps someone in your team started some services and forgot to terminate them, uselessly running up the bill throughout the month. This is easily avoidable by setting up billing alerts which will protect you from the end-of-the-month bad surprise.

Cloudaxis can help you integrate Amazon CloudWatch Billing Alerts allowing you to be notified by Email or SMS when your monthly bill crosses a threshold that you indicate. We can also use AWS Lambda to forward SNS to Slack to spread different CloudWatch alerts amongst your team members. And, we could also use marbot to manage CloudWatch alerts on Slack.

Just follow these five steps to forward CloudWatch Alarms to marbot. Slack will distribute alerts among your team members. One engineer, not the whole team, receives an alert. Marbot escalates unnoticed incidents up to the next engineer or the team.