AWS Serverless — SNS Notifications to Slack without Lambda via API Gateway

So, while searching online to see how other folks are sending SNS notifications to Slack I noticed that all guides and tutorials I read required some mandatory element of compute power — that is, being able to execute code (whether it be via Lambda or EC2).

The purpose of this guide is to show developers how to efficiently send SNS notifications to Slack via a True Serverless model without requiring any compute power (Lambda/EC2) or code via an HTTP proxy through API Gateway.

Before we start, this guide assumes you have already completed the process of creating a Slack webhook and associating it to a Slack channel. If you have not done so yet, then please complete Steps 1, 2, & 3 via this link to the official Slack guide. Please keep the Slack webhook URL handy, as we will need it for this guide.

This guide also assumes you have already created an SNS Topic, so… if you have not done so yet, please take a few seconds to follow the steps required to create an SNS Topic via this link to the official AWS documentation.


So, let’s jump right in…

1. Create a New API Gateway

Note: If you already have an existing API Gateway that you have created in the past and wish to use, you may skip this step.

While logged into the AWS Console, please go to the API Gateway service.

If you have never worked with API Gateway before, you’ll be prompted with a screen that looks like this:

Please click on the Blue Get Started button, and create a new API like so…


2. Create a New API Resource

Within your newly created API Gateway, follow the 5 steps outlined in the picture below. For the resource name, you can call it anything you want.


3. Create a New API Method

After you created your new API Resource, you should now see it as a resource in the middle column of the page (under the Actions dropdown button). Please click on the new resource your made, and just like you did in the previous step, click the Actions dropdown, but this time select Create Method.

You should a see a new empty grey dropdown box appear under you resource. Click on this empty grey dropdown box, select POST, and then click on the checkmark located immediately to the right of the grey dropdown box.

4. Configure the API Method

Upon clicking the checkmark when creating the new method, you should now see options on the right most column to configure this API Method.

Please follow the instructions in the screenshot below to properly configure the API Method:

If you’re wondering why we didn’t use the “Use HTTP Proxy Integration”, it’s because we technically don’t want to directly proxy the requests to the Slack webhook URL — this wouldn’t work because we need to manipulate the incoming SNS message (the POST request payload/data) a bit in order for Slack to accept it. This will be explained in the next step.


5. Create an Integration Request Mapping Template

Once you’ve successfully created and configured the new API POST method from the previous step, you should now see some new options on your screen, please click on Integration Request as shown in the image below:


Once you’ve clicked on Integration Request, scroll all the way to the bottom, and follow the steps outlined in the image below:

Creating a new Mapping Template allows us to manipulate the data of all incoming POST requests that have a Content-Type header set to text/plain.
In this case of SNS, notifications will be sent via a POST request with indeed a Content-Type set text/plain (regardless if the message you publish to the SNS topic is JSON or not).


Once you’ve clicked the checkmark for the new text/plain mapping template, if you scroll down, you should now see an empty grey text box. Please copy & paste the following code into that box:

I’ll explain what this code does at the end of this guide (it is written using the Apache Velocity Template Language). For now, once you’ve pasted in this code, click on the save button…

NOTE: Once you click on the Save button, you will not see any confirmation that it was actually saved, nor will the page change in any way. This is normal, but feel free to click on the Save button a few times just to be safe :-p


6. Deploy your New API

All the hard work is over now, thankfully, and we are just a few moments away from being able to receive SNS Notifications via Slack!

On the same screen you are on, click on the Actions dropdown and select Deploy API

You should see a new popup dialogue appear. In this popup, select to create a new Deployment Stage and name it anything you want. Then finally, click on Deploy.

Once you click on Deploy, your API is now deployed and able to accept HTTP POST request from the Internet (aka — your API can now accept SNS Notifications).

After deploying your API, you should be taken to a new screen. Follow the steps in this screenshot to grab the URL for the resource we’ve created (we will need it for SNS):

7. Create a New SNS Subscription

Now that we have the URL for the API Resource we’ve created, it’s time to create an SNS Subscription with it.

Please go to the SNS service page within the AWS Console, take note of the ARN for the SNS Topic we want to subscribe to (the SNS topic that we want to receive Slack Notifications from), and click on the Subscriptions tab on the left column. From there, create & configure a new subscription as shown in the image below:

As soon as you click on Create Subscription, if you followed these steps correctly, you should have now actually received a new Slack message in the channel that you associated with the Slack webhook.

Congratulations! You are now one click away from receiving SNS Notifications via Slack.

Please click on the link that was sent to your Slack channel, as this is a confirmation link to finalize and confirm the subscription.

Once you’ve clicked on the confirmation link in Slack, if you go back to the SNS page in AWS, and click on the refresh symbol, you should see an ARN associated with the Slack Subscription — we are actually all set now! From now on, all messages published to the associated SNS topic will be sent to Slack.

Let’s send a few messages to test it!

8. Testing the Subscription

NOTE: Whenever you are publishing messages to the SNS topic, you should always set the Message Format to Raw (even if you are sending JSON payloads) — this is also the format all AWS services use to publish messages to SNS even when sending JSON.

Here’s an example of what it looks like to receive a message from SNS without any data structure (JSON, XML, etc).

Here’s an example of receiving an SNS notification with a JSON payload…

Finally, here’s a real-world scenario example of receiving an SNS JSON notification from another AWS Service.

In this case, I have SES (email) set up to send notifications to SNS for outbound email notifications such as Successful Deliveries or Failed Bounces.

I redacted my email address from that Slack messages for obvious reasons. In that example, all I did was send an email from SES, and the “Delivery” notification was automatically published to my Slack SNS topic and delivered to my Slack Channel.

Now, in an actual real world example, I probably wouldn’t need all that raw JSON sent to me in Slack. Ideally I’d like to just receive a Slack message that informed of basic information such as: the delivery status, the recipient, the bounce reason (if applicable), and timestamps, etc.

This is actually all possible via Integration Request Mapping Templates. I could, for example, create a template that parses the raw JSON payload from SES/SNS and returns to me just the items I want.

I will perhaps publish another article explaining exactly how to do that, but for now, I’ll give a basic explanation of the Mapping Template we used early in this guide.

9. Integration Request Mapping Template Explained

For reference again, here is the original Mapping Template that we used earlier:

As we can see, there are really two main components to this… the if and the else. The Mapping Template is a script that uses the Apache Velocity Template Language and is applied to the payload using JSONPath Expressions.

For further reading on Mapping Templates, the Apache Velocity Template Language, and JSONPath, check out these resources:

  1. https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
  2. http://velocity.apache.org/engine/devel/user-guide.html
  3. http://goessner.net/articles/JsonPath/

For now, let me break down and explain the template we created earlier…


The if conditional is checking to see if the x-amz-sns-message-type is equal to SubscriptionConfirmation. For a closer look, here is an example of what that raw SNS SubscriptionConfirmation payload looks like:

If the header matches SubscriptionConfirmation then the template script parses the SubscribeURL from the JSON payload (as seen above) and sends a new payload to the Slack Webhook that looks like this:

{
"text": "the subscription url here"
}

The second component is the else conditional, which is going to handle all SNS notifications once we’ve confirmed the subscription (these notifications are sent with the message-type header value as “Notification”)

There’s really just a few pieces here to understand:

*$input.path('$.Subject')*\n

This part is simply parsing the Subject property from the SNS JSON payload. The asterisk * indicates that I want Slack to bold the text, and the newline \n indicates to Slack that I want the text after this to be on the next line.

``` $util.escapeJavaScript($input.path('$.Message')).replaceAll('"','\"')```

Firstly, the 3 backticks (`) is telling Slack that I want the contents of this text to go into a grey text box.

The code itself is parsing the Message property from the SNS JSON payload, and then replacing all quotations mark (“) with escaped quotations (\”) — this is critical so that if the message contains any quotations mark (which it will if it is JSON), that those quotations be escaped so that its not used to indicate an end to our JSON payload that we are constructing.

For example, imagine we are constructing a JSON payload with a message that has quotations, it would look like this:

{
"text": "{ "key": "value"}"
}

Do you see now how this is an issue?

10. Wrapping Up

I hope you found this guide useful, and I hope it begins to demonstrate the true power of Serverless. There’s a lot of amateurs posting tutorials on Medium that are just plain silly or inefficient. Unfortunately, these amateur written guides are appearing as top search results, and not driving innovation.

As a follow up, I may eventually get around to publishing a tutorial on how I handle SES to SNS to Slack notifications.

In the meantime, feel free to play around with API Gateway and Mapping Templates — it’s extremely powerful and can enable you to create services that require ZERO computation.

Welcome to True Serverless.

Love & Peace,

#sudo soul