In previous hackdays here at Blank, I wanted to get to know the Slack APIs. I ended up looking at the slash command (https://api.slack.com/slash-commands) feature, and implemented a private application for our Slack WorkSpace named ord . It’s purpose was to replicate a norwegian dictionary lookup, so one would execute it by /ord julenisse , and it would respond with the meaning of that phrase.

Merry x-mas!

As a backend, I decided to put up a simple JSON API using ASP.NET Web API (hey, this was some time back..). Being the cheap person I am, I ended up hosting that API on a free plan at https://appharbor.com/ . This was for fun anyways, so I did not want to spend a dollar on it. Here’s a small Swagger definition for it: http://ordbokapi.apphb.com . The main task for the API was to provide the Slack app with the payload Slack expects when running slash commands.

However, later on I experiened the Slack app timing out quite often. Slack has a timeout of 3 seconds, and my app usually responded around 300ms. The timeouts tend to occur when the slash command (or the API rather) had not been used for a while. I suspect this had to do with appharbor killing app pools that are idle just to avoid wasting resources, and we hit the first request penalty (warm up) of the IIS web server. This is of course fair enough, as I’m not paying for it anyways. But, I’d still like to see if it’s possible to get a better experience on another platform. So I decided to try hosting the API on another (basically) free service instead — AWS.

.NET Core 2 on AWS Lambda

I knew that AWS Lambda had launched their .NET Core support some time ago, and now was a good time to try it. In short, to get .NET Core 2 to run on AWS Lambda, all you need is a .NET Core 2.0 app referencing the AWS Lambda Nugets.

As my main dictionary lookup code was in a .NET 4.6.1 class library, I first migrated that to a .NET Standard 2 library. My transient dependencies, allthough supporting full framework only, worked fine thanks to the .NET Core 2 shim. 🙏

My next steps to deploy the lambda backed HTTP API involved:

Setting up a role with correct access rights to run AWS Lambda functions

Setting up access tokens in AWS console for lambda deployments

Setting up a AWS Gateway API, forwarding Slacks HTTP calls to Lambda

Deploying the lambda

Re-configuring my Slack app from appharbor to the AWS Gateway API

Creating a lambda role

Being a novice with the AWS console, I did have some difficulties with setting it up — but this was the how I did in the end. I created a specific role with AWS for running lambdas using the IAM tool (https://console.aws.amazon.com/iam/ ) and named it lamdbarole . I attached two lambda related execution policies to it.

Access tokens for deployment

Again using IAM, I created a deploy user, gave it the AWSCodeDeployRoleForLambda and assigned access tokens to it. The generated access tokens were copied and assigned to an environment variable, AWS_SECRET_ACCESS_KEY. The env variables is where the dotnet lambda tool looks for credentials when talking to AWS, but there are also other options out there like deployment profiles.

The HTTP Gateway API

A lambda function itself cannot be called via HTTP, so to talk to the lambda from a Slack app I had to introduce a HTTP proxy. Luckily, AWS has made this very easy. Creating an Amazon API Gateway, you are presented with the option to connect the API to a lambda. As Slack is going to do a POST, I set it up with that in mind — the end result being like this:

Allthough your C# lambda function code can take any input and provide any output, when using the AWS Gateway API as proxy we need to use the following inputs and outputs in our function : APIGatewayProxyRequest, APIGatewayProxyResponse. My small lambda function then ended up like so:

It parses the Form POST data sent by the Slack app, then does the lookup using my custom dictionary library, and returns a response the AWS Gateway API understands.

Deploying the lambda to AWS

Amazon has created a Visual Studio plugin to work with deployments, but I prefer the command line tools so I can script it. Here, this meant including the Amazon.Lambda.Tools nuget, which provides you with the dotnet lambda <args> tool.

To deploy the lambda, two parts are important. The dotnet CLI and the default AWS CLI config file; aws-lambda-tools-defaults.json. The file is picked up automatically, and the rest is read via cmdline params:

dotnet lambda cli tool

aws-lambda-tools-defaults.json

Gotcha: although there is a command line parameter to set the role executing the lambda, -frole , I never got that working. It only worked setting in the config file. Might be a bug in the AWS tool. Pretty ¯\_(ツ)_/¯ to me.

Wrapping up

After a succesful deployment, I tested the AWS Gateway API using Postman and verified that it’s response matched what my old appharbor hosted Web API returned. All left was to switch my Slack App endpoint from Appharbor to the new AWS endpoint, and voila — the API is now running on Lambda. 🎉

For reference, the full source code for the AWS lambda function can be found here:

https://github.com/johnkors/ordbok.lambda