Tutorial: Building a community on-boarding app with Serverless, StepFunctions, and StackStorm
Are you a serverless developer using the Serverless framework who wants to supercharge your functions with pre-built integrations? Or maybe a StackStorm user who wants to bring the power of StackStorm Exchange to your AWS environment?
If you‘ve got a couple hours to spare and want a guided project that‘s a bit more in-depth than yet another "hello world" example, you‘re in the right place. Over the course of this multi-part tutorial, we‘ll build a real-world serverless application that automates the process of on-boarding new users to a community Slack team.
Here‘s an overview of what the final app will do:
- Present a registration form to collect info from prospective new members
- Pass the form data to a StepFunction workflow for processing
- Use a StackStorm Slack integration to invite the user to our team
- Create a contact record in a CRM system
- Store the new member info in a DynamoDB table
We‘ll be implementing this with the Serverless framework, AWS Lambda and StepFunctions, and ready-made integrations from StackStorm Exchange.
Sound interesting? Great, let‘s get started! We‘ll take it step-by-step over the course of a few posts. Here‘s what we‘ll cover in part one:
Prerequisites
Before we dive in, let‘s make sure you have the tools you‘ll need.
- An AWS account and some basic familiarity with AWS services like Lambda and API Gateway. You don‘t need to be an expert but some experience will help.
- Node.js – version 8.4.0 or higher. Installation instructions here.
- The Serverless framework. Run
npm install -g serverless
to install it globally after installing Node.js. - Docker. The StackStorm plugin uses Docker to provide a consistent local testing environment. Guides for OS X, Windows, and Linux.
- A Slack workspace to test with. It only takes a moment to create a new one if you don‘t have a sandbox space already. You‘ll need admin privileges.
Once you have your Slack workspace set up, you‘ll need to grab an API token. Go to the Legacy Token page and generate a token.
Pro Tip: If you prefer, you can grab your own user token instead. It‘s quicker for development purposes but not recommended for real apps.
With that, we should have everything we need. On to the code!
Creating a Serverless Project
Create a new directory for the project and initialize it with npm init
:
$ mkdir community-signup
$ cd community-signup
$ npm init
Answer the prompts (or just hit enter to accept defaults) to generate a package.json
file.
Next, let‘s install the serverless-plugin-stackstorm plugin. This is our bridge between the Serverless framework and StackStorm Exchange:
$ npm install --save-dev serverless-plugin-stackstorm
With the plugin installed, create a serverless.yml
file in the project root to tell Serverless to use it:
# serverless.yml
plugins:
- serverless-plugin-stackstorm
We now have a (very minimal) working Serverless project. To make sure everything is wired up correctly, try running:
$ sls stackstorm
You should see output showing a list of available commands that the StackStorm plugin adds to the serverless CLI. If you get an error, double check the steps above and consult the plugin docs for troubleshooting tips.
Adding our First Action
Now for the fun part – pulling in an action from StackStorm Exchange! The Slack pack provides a bunch of actions for working with Slack. The one we want is slack.users_admin_invite
to invite new members to our team.
Before we integrate it, let‘s take a look at the action definition:
$ sls stackstorm info --action slack.users_admin_invite
slack.users_admin_invite
Send an invitation to a slack instance.
Parameters
email [string] (required) ... Email of user
channels [string] .......... Channels to add user to
first_name [string] ........ First name
last_name [string] ......... Last name
resend [bool] .............. Resend invitation if already invited
Config
admin_token [string] ....... Admin OAuth token
We can see this action takes a few parameters, but only email
is required. Since we‘ll be receiving data from a signup form, let‘s use email
and first_name
.
The config specifies an admin_token
, which is the API token we generated earlier. We‘ll need to provide that, and there are two ways to do it:
- Pass it as a parameter when invoking the action
- Specify it in the
config
section of ourserverless.yml
file
The second approach keeps sensitive tokens out of our code, so let‘s go with that. While we‘re at it, we can move the Slack-specific configuration to its own file to keep things tidy.
Create an env.yml
file to hold the config:
# env.yml
slack:
admin_token: YOUR_TOKEN_HERE
Make sure to replace YOUR_TOKEN_HERE
with your actual token.
Important: The
env.yml
file will contain sensitive info like API keys. Be sure to add it to your.gitignore
file so it doesn‘t get committed to source control!
Now update the serverless.yml
file to use the config:
# serverless.yml
service: community-signup
plugins:
- serverless-plugin-stackstorm
provider:
name: aws
runtime: python3.6
stage: dev
region: us-west-2
custom:
slack_config: ${file(env.yml):slack}
functions:
invite:
handler: slack.users_admin_invite
stackstorm:
action: slack.users_admin_invite
input:
email: "{{ input.body.email }}"
first_name: "{{ input.body.first_name }}"
output:
body: "{{ output }}"
events:
- http:
path: invite
method: post
Let‘s break this down:
-
The
provider
section configures default options for our functions, like the runtime and AWS region to use. -
custom
lets us define variables that can be referenced elsewhere in the file. Here we load the Slack config fromenv.yml
. -
Under
functions
, we define our first Lambda function, namedinvite
. The real work happens in thestackstorm
section:action
specifies which StackStorm action to useinput
maps the parameters our action expects to the incoming HTTP requestoutput
defines what the function will return. To keep things simple, we just echo back the raw result from the action.
-
The
events
section sets up an API Gateway trigger so we can invoke our function via HTTP
With the configuration in place, we‘re ready to test it out!
Invoking our Action
Let‘s start by doing a quick local test using the sls stackstorm docker
command:
$ sls stackstorm docker run --function invite --data ‘{"body":{"email":"[email protected]","first_name":"Alice"}}‘
This will spin up a local Docker container to closely replicate the AWS Lambda environment and execute our function with the sample payload. If everything is set up correctly, you should see a successful response.
Pro tip: Use the
--passthrough
flag to skip the actual StackStorm action invocation. This is handy for debugging the input/output mapping without making repeated calls to the live API and dealing with things like rate limiting.
With the local test passing, we‘re ready to deploy to AWS and try it live!
$ sls deploy
The first deployment takes a few minutes as it sets everything up. Subsequent deploys will be much faster.
After the deploy finishes, you‘ll see the URL for the API Gateway endpoint that was automatically created. We can use this to test our live function.
Using curl:
$ curl -X POST -H "Content-Type: application/json" -d ‘{"email":"[email protected]","first_name":"Alice"}‘ PASTE_YOUR_URL_HERE/invite
Or, if you have HTTPie installed:
$ http POST PASTE_YOUR_URL_HERE/invite [email protected] first_name=Alice
You should get back a successful response containing the email address of the invited user. And if you check Slack, you‘ll see the invitation was actually sent! We‘ve got a working serverless function that integrates with Slack, not bad for a few minutes effort.
Wrapping Up
In this first part of our tutorial, we got a basic Serverless project set up and integrated a Slack action from StackStorm Exchange to invite users to a team. We‘ve already seen how Serverless and StackStorm can help quickly build useful functions without having to write a bunch of boilerplate code.
In the next part, we‘ll add more actions to continue building out the community on-boarding workflow. Then we‘ll chain them together into an automated flow with AWS StepFunctions.
I hope you found this guide informative and practical. Please let me know if you have any questions or feedback! You can reach me on Twitter at @yourtwitterhandle.