Realize asynchronous Slack slash command with AWS serverless
Slack is one of the most popular team collaboration software nowadays. Besides instant messaging, Slack provides many apps and supports custom apps to fulfill various requirements. To invoke a custom app, one entry point is slash commands that enable users to run the custom app by simply typing a string on a channel. Explicitly, what a slash command does is to send an HTTP request with the user input to the web endpoint of the associated custom app. Slash commands empower users to accomplish a variety of tasks on Slack channels as long as the corresponding web apps are well implemented.
Service deployment as an example
In this post, I’d like to introduce how to create a slash command to deploy new versions of services on AWS ECS. Taking service deployment as an example is because it’s common for software engineers. Although deployment should be done via CI/CD tools (e.g., Gitlab CI/CD and Jenkins), using the slash command has two main benefits.
- The deployment actions and results are visible to and traceable for all the stakeholders.
- The deployment is more convenient. Developers can do deployment/rollback easily by simply typing a few words on their mobile phones. QA are able to do the deployment to verify issues with specific versions when necessary without understanding the CI/CD tools.
Before proceeding, I’d like to remind that this example is for demonstration and is probably useful in the development or staging environment. However, I don’t suggest to adopt this approach to the production environment.
To realize this feature, we need to create a slash command and a web application with permissions to run the deployment. Creating a slash command is fairly simple by following Slack’s guide. Assume we create a slash command named /ecs_deploy
with two parameters <service>
and <version>
. When a user runs/ecs_deploy <service> <version>
on a channel, the corresponding web app shall start to update <service>
with version
and return the result to the same channel.
Because the slash command is for people instead of bots, it’s ideal to implement this web app with AWS serverless services because 1) the usage frequency is low and 2) performance and latency are not a concern. AWS serverless enables developers to run the applications without managing or operating the underlying infrastructure. With AWS serverless, we don’t need a long-running ec2 instance (which is idle most of the time) and just pay for what we really use. If you haven’t heard of AWS serverless, you may take a look at the AWS doc for a better understanding of this post.
Design and implementation of the service
Considering basic requirements and the characteristics of slash commands, this web app supports the following features.
- Command authentication. To make the app secure, we must do authentication by at least verifying the
token
of the slash command. I’d suggest to limit this slash command to be issued only on one dedicated channel by checking thechannel_id
. Slack also provides other information likeuser_id
for stricter authentication if needed. - Asynchronous response. When a slash command sends an HTTP request, it expects the response to be received within 3 seconds. Otherwise, the user invoking the command will see
Timeout was reached
. Thus, this web app must support asynchronous calls by sending the response to theresponse_url
given in the request body and setting{“response_type": “in_channel", “text": message}
to the response body. - Malformed parameters handling. Slash commands store the parameters as a string in the
text
field of the request body. The parameters are separated by a whitespace which will be replaced with+
in the request body. For example, the parameters of/command para1 para2 para3
would be transformed topara1+para2+para3
and stored in the value oftext
. The behavior of using whitespaces as delimiters is often confusing and ignored by users. Thus, the app should handle this case in a user-friendly manner.
Based on the design and implementation considerations, this web app is realized with the above system architecture where each component is described below.
- API Gateway. The AWS API Gateway serves as the entrypoint for HTTP requests from our slash command
/ecs_deploy <service> <version>
. Upon receiving requests, the API Gateway invokes the request validator to do verification. - Request validator (lambda). The request validator, implemented as an AWS Lambda function, is responsible for checking whether the
token
andchannel_id
are expected as well as the parameters are valid. If so, the request validator sends the request to the request bridge and returns200 OK
to Slack. By replying to Slack immediately and passing the request event to the request bridge, we can achieve the asynchronousness and address the 3 seconds limit. - Request bridge (SNS topic). The request bridge, implemented as an AWS SNS topic, acts as a bridge to pass the event message from the request validator to the request handler.
- Request handler (lambda). The request handler, also an AWS lambda function, runs the ECS deployment task based on
<service>
and<version>
. When the deployment is completed, the request handler sends the result back to the channel via theresponse_url
. Note that the maximum execution time of a lambda function is 15 minutes that is sufficiently long for the majority of tasks like this one. However, if you’d like apply the architecture in this post for longer tasks, you shall consider to use AWS Step Functions. - CDK. To build the entire system, we use AWS CDK which enables developers to define the AWS resources in the programming language and provisions the resources through AWS CloudFormation. With CDK, you can do infrastructure as code more concisely than CloudFormation because CDK does lots of transformations behind the scene.
Build the system with AWS CDK
To build the serverless system with CDK, we create a CDK Python app named serverless_ecs_deploy
. The code snippet above is the procedure of using this CDK app to deploy the system. First, the initialization command cdk init --language python
creates the directory structure and associated files. We then write the lambda functions serverless_ecs_deploy/request_validator.py
and serverless_ecs_deploy/request_handler.py
for the request validator and the request handler, respectively. In serverless_ecs_deploy/serverless_ecs_deploy_stack.py
, we declare and associate the objects of the AWS resources in this serverless app. From the code of the serverless_ecs_deploy_stack.py
, you can see we can easily use objects to reflect the system architecture with AWS CDK. After we write the three core scripts, we can use cdk synth
to review the corresponding CloudFormation template which is actually used to provision the system. Finally, we run cdk deploy
to trigger the provisioning of the system defined in serverless_ecs_deploy_stack.py
. If everything is fine, the outputs of cdk deploy
shows the endpoint of this web app like serverless-ecs-deploy.serverlessecsdeployEndpoint2ADDF30F = https://ww21uy5vp4.execute-api.us-east-1.amazonaws.com/prod/.
This is the request url for our slash command /ecs_deploy <service> <version>
.
Summary
As more collaboration and communications happen in Slack channels, using Slack slash commands is not only convenient for addressing various tasks but also makes things visible to and traceable for channel members. With AWS serverless, we can realize slash commands at almost no cost. This post intends to demonstrate the benefits and simplicity of combining Slack slash commands and AWS serverless for certain tasks. Welcome to leave your comments if any.