The cloud, it seems, is everywhere nowadays. One way I find it useful to classify the offerings is the following crude categorisation:
- applications, such as Google Docs or Gmail;
- infrastructure, such as the AWS (Amazon Web Services) S3 storage service or the EC2 compute service (virtual servers and containers);
- services, such as the AWS Simple Queue Service (SQS) or Lambda functions
Other ways of categorising offerings are available too; for example, AWS divvy up their offerings as follows:
Having recently just signed back into the AWS world, I thought I’d start to try out some of the first year free tier offerings. So for this first bit of toe dipping into the AWS ocean, I thought I’d see if I could make use of Amazon Lambda functions – “serverless” computational functions executed by AWS – to implement something akin to the Slack slash command handler I described in the previous post.
In that previous post, I described how I used a hook.io Slack /slash pattern that takes an HTTP POST request from a Slack slash extension to call out to a microservice on hook.io; that service responds to an incoming callback extension on Slack. The microservice itself also makes a query request to a third party search API. The architecture looks something like this (though I wonder if I could have simplified it by just responding to the slash command request, rather than returning the response via the Slack incoming extension?):
Amazon Lambda functions work in a similar way to the way hook.io handles the compute function definition and its execution, but the invocation needs to come either from an event triggered by another AWS source or over HTTPS using an event raised by the Amazon API Gateway (AWS Lambda Function and Event Sources). That is, we need a pattern that looks more like this (though I haven’t tried the call out to the UK Parliament API yet):
A recent post on the AWS blog – New – Slack Integration Blueprints for AWS Lambda – described a simple blueprint for implementing a simple “echo” slash command handler running on AWS. Excellent – it took me less than half an hour to hack together the hook.io thing, so I was hoping for the same with AWS.
That was this morning, well before coffee, and now it’s after lunch. Having got it working, it’s a simple five minute job. but it took me a couple of hours to find the 5 minute route. (Trying to follow notes on the web is one reason I blog the way I do, and why I have such high regard (honestly!) for the majority of OU materials. Recalling the times when I used to work through through maths texts, too many tutorials have a “hence” or “just” step that may be obvious to an expert, but is a huge blocker to a novice…)
So here’s the five minute version (maybe fifteen!;-), containing pictures with boxes and arrows and a paragraph associated with each one to describe what’s going on…
Step the first
You need an Amazon AWS account – you means handing over your credit card. That said, when you sign up you get access to to the free tier for a year. You may even get additional credit if you sign up via the Github Student Developer Pack.
Step the second
Go to the AWS Lambda console (you may want to change region – I’m going via Ireland) and get started…
Step the third
We can make use of the simple template for the slack echo command using Python.
Step the fourth
In this step, we start naming things. Names are important, because we’ll be calling things by name to invoke them; you need to keep track of what’s called what and where so that you can make sure you’re calling it properly.
The first thing you need to do is give your Lambda function a name, I’m calling mine simpletest. This is effectively a filename – for the python function I’m creating, we can think of this setting as saving the filename of the local/inline copy of the function code to simpletest.py.
The second thing you need to check is the name of the function in the code you want to invoke when the lambda function is called. In the example code, this is the function lambda_handler().
The third thing you need to check is the name of the handler that will be executed when the Lambda function is triggered. This is the function-in-the-file we want to run in the form FILENAME.FUNCTION. In this example, simpletest.lambda_handler.
Step the fifth
Define the Lambda function role.The suggested role is a “Basic execution role”. On first run you won’t have one of these, so you’ll need to create one (your browser will possibly need pop-ups enabling).
Step the Sixth
If you now look at the guidance given in the example Lambda function code, it starts off with the following:
Follow these steps to configure the slash command in Slack:
1. Navigate to https://<your-team-domain>.slack.com/services/new
2. Search for and select "Slash Commands".
3. Enter a name for your command and click "Add Slash Command Integration".
4. Copy the token string from the integration settings and use it in the next section.
5. After you complete this blueprint, enter the provided API endpoint URL in the URL field.
This is all good advice. Except for the use it in the next section bit, because we’re going to ignore that for now.
Step the Seventh – just don’t…
In the guidance, steps are described for encrypting the token you got from the Slack slash definition page. This is Good Practice, but a real pain if you’re just trying to get started and what to check things are working in the first place because you’ll quite possibly end up going down various ratholes. (I’ll describe what you need to do to follow those steps in another post.)
So for the instructions that begin:
Follow these steps to encrypt your Slack token for use in this function:
just ignore them. Instead, edit the code, comment out the encrypted token handler bits, and paste in a plaintext version of the token you got from Slack. (We’re just trying stuff out, remember… we can reset the token and move to an encrypted one once we know the other bits are working).
#ENCRYPTED_EXPECTED_TOKEN = "<kmsEncryptedToken>" # Enter the base-64 encoded, encrypted Slack command token (CiphertextBlob)
#kms = boto3.client('kms')
#expected_token = kms.decrypt(CiphertextBlob = b64decode(ENCRYPTED_EXPECTED_TOKEN))['Plaintext']
Step the Eighth
The next step of guidance (the bit beginning Follow these steps to complete the configuration of your command API endpoint) refer to what happens on the next step – which I’ll walk through…
Click on Next from the function definition page, and start to configure the API endpoint, specifically setting the Method to POST and the Security to Open. (You might also want to change the name of the API to something more appropriate, perhaps away from LambdMicroservice and towards something more personally recognisable, such as slacktestservice.) Leave the deployment stage set to prod.
Step the Ninth
Move on to the next step, and you can create your Lambda function:
But…. we’re still not there yet….
Step the Tenth
…there’s still stuff to do with the API definition. From the API Endpoints tab, you need to go into the prod deployment stage settings:
This will allow us to tweak the way that the API handles requests made to it.
Step the Eleventh
From the API Gateway console, select the service we associated with the Lambda function, which by default was called LambdMicroservice; (if you renamed the service, for example to slacktestservice, click on that service.
Step the Twelfth
Select the simpletest function, and click on the POST method. This shows the steps associated with the call handler. Click on the Integration Request setting.
We now need to set the API service up so it can handle the Slack POSTed content.
Step the Thirteenth
The Integration Request needs customising to handle the JSON data sent from Slack. To do this we need to create Mapping Template for the JSON content.
So create one…
Step the Fifteenth
The mapping we need to make is from the accepted application/x-www-form-urlencoded type. (Note, the official guidance currently (incorrectly) sets this as x-www-form-urlencoded).
Step the Sixteenth
Select the Mapping template, and define the template as follows:
Accept the template setting.
Step the Seventeenth
Having defined the mapping template, deploy the API.
Make sure you deploy to the correct place (recall, we were using prod)!
Step the Eighteenth
From the Lambda function control panel, you should be able to see the URL for your API endpoint. Grab a copy of this URL.
Step the Nineteenth
Paste the API endpoint URL – making sure it points to the correct function handler (in my case, simpletest).
Make sure you save/update the settings!
Step the Twentieth
Finally, you should be able to try out your Slack slash command…
Phew… got there eventually, albeit insecurely… In a later post, I’ll describe how to do the token encryption bit, because for an AWS n00b it again takes multiple, and not necessarily obvious, steps… I’ll also describe how to set up a simple test case for testing out the function.
PS If I’ve missed anything out in this tutorial, please let me know. I’d only intended to spend half and hour or so tinkering and half and hour blogging this, and it’s now getting on for six hours after I started, though a fair chunk of that time was also spent putting this post together … So if I can spare anyone else the pain…!;-)