AWS serverless Microservice to send SMS (API Gateway + Usage plan + SNS)

Lorenzo Panzeri
6 min readJan 28, 2023

--

In this article I’ll explain how to build a serverless application that sends SMS.

Project goals:

  • Use as much as possible directly AWS’s services integration: connect AWS Api Gateway right with AWS SNS with no AWS Lambda needed in between.
  • Protect the service endpoint with an API Key regulated with an Usage Plan.

Github link: https://github.com/l4op/micro-apigw-sns-sms

What you need:

To build this microservice I’ll use AWS Serverless Application Model (SAM) that combines lots of documentation with easy development and fast time-to-market.

How to build:

  1. Create a new project in your workspace
  2. Open the project folder with VSC and in a new terminal type:
sam init

3. In the template.yaml file, create one by one the resource we need:

  • Initialization of SAM template:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: API to send SMS using SNS
  • API
Resources:
RestApi:
Type: AWS::Serverless::Api
Properties:
StageName: dev
Auth:
ApiKeyRequired: true
DefinitionBody: # OpenApi standard
'Fn::Transform':
Name: 'AWS::Include'
Parameters:
Location: './api.yaml'
OpenApiVersion: 3.0.3
EndpointConfiguration:
Type: REGIONAL

Auth.ApiKeyRequired enables the Api Key + Usage Plan configuration.

We will import an OpenApi template to directly integrate with SNS (explained later).

  • IAM ROLE to enable API GW access to SNS:
 RoleForSNS:
Type: 'AWS::IAM::Role'
Properties:
RoleName: API-SNSSMS
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
Policies:
- PolicyName: SNS_SMS
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: [ "sns:Publish"]
Resource: "*"
- Effect: Deny
Action: ["sns:Publish"]
Resource: "arn:aws:sns:*:*"
  • The Usage Plan to limit API access:
  UsagePlan:
Type: AWS::ApiGateway::UsagePlan
Properties:
ApiStages:
- ApiId: !Ref RestApi
Stage: !Ref RestApi.Stage
Description: Usage plan for this API
Quota:
Limit: 100
Period: MONTH
Throttle:
BurstLimit: 10
RateLimit: 10
Tags:
- Key: usage-plan
Value: micro-apigw-sns-sms

Set up as you want the Quota and the Throttle

  • The API Key:
 ApiKey:
Type: AWS::ApiGateway::ApiKey
Properties:
Enabled: True
  • Link the API Key to the Usage Plan:
UsagePlanKey:
Type: AWS::ApiGateway::UsagePlanKey
Properties:
KeyId: !Ref ApiKey
KeyType: API_KEY
UsagePlanId: !Ref UsagePlan
  • An API GW’s deployment:
  Deployment:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId: !Ref RestApi

For creating the API and leverage all API Gateway’s configuration it’s common to use the OpenApi standard and write your own API:

openapi: "3.0.1"
info:
title: "AWS API-GW Integration with SNS to send SMS"
paths:
/:
post:
parameters:
- name: "Content-Type"
in: "header"
schema:
type: "string"
responses:
"200":
description: "200 response"
content:
application/json:
schema:
$ref: "#/components/schemas/Empty"
x-amazon-apigateway-integration:
credentials: arn:aws:iam::${AWS::AccountId}:role/API-SNSSMS
httpMethod: "POST"
uri: arn:aws:apigateway:${AWS::Region}:sns:path//
responses:
default:
statusCode: "200"
requestParameters:
integration.request.header.Content-Type: "'application/x-www-form-urlencoded'"
requestTemplates:
application/json: "#set($inputRoot = $input.path('$'))\nAction=Publish&PhoneNumber=$util.urlEncode($util.parseJson($input.json('$.PhoneNumber')))&Message=$util.parseJson($input.json('$.Message'))&AWS.SNS.SMS.SMSType={'DataType:'String','StringValue': 'Transactional'}"
passthroughBehavior: "when_no_templates"
type: "aws"
components:
schemas:
Empty:
title: "Schema"
type: "object"

You can see how to manage the direct SNS integration in requeuestTemplates.

SNS can manage two type of SMS:

  • Promotional: less important message on which AWS is allowed to manage the message delivery to optimize costs.
  • Transactional: High priority messages, sent by AWS as soon as possible and without cost optimization.

Now, to deploy with AWS SAM directly on our AWS Account (I assume you already have programmatically access configured in .aws/credentials and .aws/config) write in the terminal:

sam build
sam deploy --guided

This command will create a samconfig.toml template in your project. Here is mine:

version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "micro-apigw-sns-sms"
s3_prefix = "micro-apigw-sns-sms"
region = "eu-west-1"
confirm_changeset = true
capabilities = "CAPABILITY_IAM CAPABILITY_NAMED_IAM"
disable_rollback = true
image_repositories = []
profile = "aws-lab"
s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-abcdefghilmno123"

Pay attention to “capabilities”: is needed to ensure that SAM could interact with IAM

The “- -guided” parameter will guide you step to step to create this file. For next deploys you can type only “sam deploy” and these config will be automatically retrieved.

Now let’s check out what happened in the AWS Console:

  • In API Gateway there is our new API:
  • The API is protected by an Api Key:
  • The API has an endpoint:
  • The Api Key is linked to an Usage Plan:
  • We can retrieve our Api Key (click on Show) to provide to the caller application. The correct place for this Api Key is in the x-api-key header in the POST request.

Now let’s check out SNS Dashboard:

  • AWS keeps Sms messaging in a “Sandbox” to avoid sms spamming. You can ask to exit the Sandbox simply by clicking on “Exit SMS Sandbox” and filling in some details and a brief overview of why you need to send SMSs.
  • In the Sandbox, to allow SMS sending, you have to register your mobile number by clicking on “Add phone number” and after the validation you can receive SMS from AWS SNS.
  • SMS sending also in Sandbox is OVER the Free Tier so be aware that you will pay a few cents also for testing it out.

You can edit the Sender info from this box on the bottom of the SNS Console:

To test, open Postman:

Create a new Collection and setup a Request:

  • Use the service endpoint from API Gateway’s console and choose the POST method.
  • In Authorization select API Key and put in Key “x-api-key” and in Value your API Key retrieved before.
  • Put in the body of the call a json like this (the receiver phone number need the international prefix with plus symbol):
{
"Message": "Test",
"PhoneNumber": "+39123456789"
}

(If you are still in the Sandbox, use only the number registered before on SNS’s console or you won’t receive any SMS)

  • Hit “Send” and check your phone:

Whoa! Sms sent and received!

If you want to cleanup all the resources, type in the terminal:

sam delete –stack-name <your_stack_name>

Now we are able to create and deploy a serverless microservice in our environment to manage SMS sending. We can create different Api Keys and Usage Plans for every application that needs to send Text Messages in order to have a single “gateway” for SMS messaging that is easy to integrate and ready to use.

Hope this will help 🙂

Ciao,

Lorenzo

Thanks to Siddhi Jha and Albert Blaya for their awesome work:

--

--

Lorenzo Panzeri

Passionate Developer - Compulsive learner - Messy maker