はじめに
Node.js で作成したアプリ(Lambda 関数)から、AWS SNS を利用して SMS を送る方法を紹介します。
- Lambda 関数作成
- Node.js のアプリ作成(ポイント)
- SNS トピック・サブスクリプションの作成
をおこないます。 Node.js アプリについては簡単なソースコードを記載しております。
この記事を読むとわかること
- AWS SNS による SMS 送信を Node.js で行う方法がわかります。
- AWS SDK の利用方法がわかります。
【参考】この記事で学んだことを使って、アプリケーションを作ってみました! →
SNS とは?
Amazon SNS (Amazon Simple Notification Service) とは、イベント駆動の非同期処理を実現する「イベントルータ」の一種です。
何かイベントが起こったことを検知して、イベント起こったよ!と各システムに伝えることができます。イベントを発行する側のシステムは「Publisher」と呼ばれます。イベントを受け取る側は「Subscriber」と呼ばれます。
SNS の Subscriber として、AWS のサービスを利用することができます。
- SQS
- Lambda
- Kinesis Data Firehose
など。
メジャーなのは SQS らしいです。SQS は、「イベントストア」と呼ばれ、SNS から受けたイベントをポーリングして受信者側からのアクションを待ちます。SNS+SQS の組み合わせを使ったアーキテクチャは、ファンアウトと呼ばれます。
ポイント: SNS は、このようなシステム間(A2A:アプリケーション対アプリケーション)でのやりとりだけでなく、人との間(A2P:アプリケーション対個人)のやりとりにも利用できます。
今回使いたいのは A2P で、
SNS から個人スマホに SMS を送る機能です。
前提
AWS マネジメントコンソールにアクセスできること。
本記事で作成する機能
Node.js で作成した Lambda 関数から、「てすとめっせーじ」と書かれた SMS を送る。
アプリケーションでの利用イメージ
SNS が一番よくつかわれるのは、AWS で想定外の挙動があったときの E メール通知です。 CloudWatch と連動して利用していると思います。
私は、電車の遅延を駅に行く前に検知するために、関連するツイート数が増えたら SMS で通知してくれる機能を作りました。 この機能は別記事にまとめたいと思います。
- EventBridge で、15 分おきにイベント発行
- Lambda で、TwitterAPI を使った処理を実行する
- 条件に合致した場合、SMS を送る
次章より実際の手順を記載します。
SNS トピックの作成
トピックを作成します。
マネジメントコンソール > Amazon SNS > トピック から。
トピックのタイプ
トピックのタイプは2種類から選べます。
- FIFO タイプ
- 「順序性」と「単一性」が担保される
- 秒間 300 件しか実行できない
- SQS に対するイベント発行にしか使えない
- スタンダードタイプ
- 1 回の要求で 2 回実行されることがある。(最低 1 回)
- 順序はベストエフォート
- 制限はなし?
- A2P で使える
順序はともかく、単一性は処理によっては重要になりそうです。1 回の要求で 2 回処理されることがあるとなると、課金処理とかだったら大変。
今回は、スタンダードタイプしか使えないのでスタンダードを選択。
アクセスポリシー
トピック所有者のみにしておきます。
再配信ポリシー
デフォルトのままにします。
CloudWatchLogs のロール
CloudWatchLogs に、ログを書き込むためのアクセス許可を SNS に与える必要があります。
ここでは新しいロールを作成してしまうことにします。
「許可」をおします。
サブスクリプションの作成
トピックの配信先を「サブスクリプション」として設定します。 1 トピックにつき複数サブスクリプションを設定可能です。
SMS サンドボックス
注意点として、いきなり不特定多数に SMS を送るような設定はできないようです。
事前に認証した電話番号にのみ SMS を送信できるようになっています。(「AWS アカウントが SMS サンドボックス内にいる」と表現されています。)
SMS サンドボックスを終了して、事前認証した以外の電話番号に SMS を送れるようにするには、サンドボックス外へ移動する手順が必要とのことです。
今回は勉強用ですし、自分個人のスマホにしか SMS 送信しないため、サンドボックスのまま使います。
電話番号追加
サンドボックスに電話番号を追加します。
送信先は、例えば電話番号が080-1234-5678
であれば+81 80 1234 5678
と入力します。
SMS でワンタイムパスワードが送られてくるので、それをマネジメントコンソールに入力します。
サブスクリプションの追加
先ほど作成したトピック画面からサブスクリプションを作成します。
Node.js アプリ作成(ソースコードあり)
初期設定
Node.js アプリを作成します。
$ mkdir snstest
$ cd snstest/
$ npm init -y
$ touch index.js
$ npm install
(任意)GitHub にリポジトリを作成して連携しておきます。
$ git init
$ git add .
$ git commit -m "first commit"
$ git remote add origin git@github.com:ユーザ名/snstest.git
$ git branch -M main
$ git push -u origin main
パッケージインストール
Node.js から AWS リソースを動かすためには、AWS SDK のモジュールが必要なため、インストールします。
$ npm install @aws-sdk/client-sns
AWS SDK とは、各プログラム言語ごとに用意された、AWS の開発をサポートするツールです。AWS 用モジュールだと思っても大丈夫だと思います。
- python で AWS リソースを動かす:boto3
- Node.js で AWS リソースを動かす:AWS SDK for JavaScript for Node.js
SNS 接続部品
SNS 接続部品を作ります。
const REGION = "ap-northeast-1" //e.g. "us-east-1"
const { SNSClient, PublishCommand } = require("@aws-sdk/client-sns")
module.exports.sns = async (msg) => {
const client = new SNSClient({ region: REGION })
const params = {
Message: msg /* required */,
TopicArn: "arn:aws:sns:ap-northeast-1:xxxxx{作成したトピックのARNを記載}",
}
const command = new PublishCommand(params)
try {
const data = await client.send(command) // process data.
console.log(data)
} catch (error) {
// error handling.
console.log(`error: ${error}`)
} finally {
// finally.
}
}
@aws-sdk/client-sns
の公式ドキュメントはこちら。
ざっくりと理解しておきます。
SNSClient
というクラスは、SMS 送信を実行してくれるものです。send
というメソッドを持っています。PublishCommand
は、send
に渡すコマンドを作成するクラスです。
PublishCommand のインプット定義は公式を読んでおいても損は無いです。上記コードでいうparams
オブジェクトに入れることができる設定がわかります。
公式ドキュメント
PublishCommand のインプット定義: https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-sns/interfaces/publishcommandinput.html
SNSClient: https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-sns/classes/snsclient.html
PublishCommand: https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-sns/classes/publishcommand.html
メイン関数作成
メイン関数はこれだけです。
const { sns } = require("./utils"); // ロール設定忘れずに。
const index = async () => {
const msg="てすとめっせーじ"
await sns(msg);
};
module.exports.handler = index;
Lambda 用なのでhandler
としてエクスポートすることを忘れないようにします。
Lambda 関数作成とソースアップロード
先ほど作成したアプリを Lambda 関数にアップロードします。 Lambda 関数の作成・アップロード方法はこちらを参照。
Lambda 関数の設定は
- 一から作成
- Node.js 16.x
- 実行ロールは「基本的な Lambda アクセス権限で新しいロールを作成
とします。
アップロード対象の資材は以下。
- index.js
- node_modules/
- package-lock.json
- package.json
- utils.js
IAM ロールの設定
作成した Lambda 関数のマネジメントコンソールから、設定 > 実行ロール と進んで IAM ロールを確認します。
IAM のコンソールから「ポリシーをアタッチ」を選択します。
「AmazonSNSFullAccess」を付けておきます。
実行確認
Lambda 関数のコンソールから、テストを実行してみます。
スマホに SMS が届きました!
つまづいたところ
権限問題
Lambda 関数に適切なロール(主に SNS を実行するロール)が無いと権限エラーになります。
START RequestId: xxxxxxxxxxxxxxxxxxxxxxxVersion: $LATEST
2022-06-05T05:08:42.084Z xxxxxxxxxxxxxxxxxxxxxxx INFO error: AuthorizationError: User: arn:aws:sts::XXXXXXXXXXXX:assumed-role/snstest-role-f3wh4aq2/snstest is not authorized to perform: SNS:Publish on resource: arn:aws:sns:ap-northeast-1:XXXXXXXXXXX:topic-test2 because no identity-based policy allows the SNS:Publish action
END RequestId: xxxxxxxxxxxxxxxxxxxxxxx
REPORT RequestId: xxxxxxxxxxxxxxxxxxxxxxx Duration: 991.31 ms Billed Duration: 992 ms Memory Size: 128 MB Max Memory Used: 77 MB Init Duration: 371.37 ms
タイムアウト問題
Lambda 側でタイムアウトエラーが発生する場合があります。 デフォルトでは Lambda タイムアウトは 3 秒になっています。
その場合は、Lambda の 設定 > 一般設定 から、タイムアウト値を適当に伸ばします。
ES6 記法使えない問題
本当は、Node.js の ES6 以降記法で記載したいのですが、上手くいきませんでした。
AWS 公式ページ:SDK Version3 のデベロッパーガイドでも、Node.js の 13 以降であれば ES6 基本は可能と書かれていますが。。
本当はこんな形で書きたい。
import { SNSClient, PublishCommand } = from "@aws-sdk/client-sns"
export const sns = async (msg) => {
// 省略
}
NG な理由がよくわからないので、とりあえず諦め、前に示したような記法で書いています。
使用上限を超えた問題
Lambd の実行自体は成功するのに、一向に SMS が届かなくなりました。
CloudWatch ログを見るとNo quota left for accountのログが出ています。
{
"notification": {
"messageId": "a81c56f5-97d3-59f1-b6bb-0de3df441de8",
"timestamp": "2022-06-05 05:59:51.371"
},
"delivery": {
"destination": "+81xxxxxxxxxx",
"smsType": "Promotional",
"providerResponse": "No quota left for account",
"dwellTimeMs": 79 },
"status": "FAILURE"
}
対処法 Step1 上限申請
こちらにしたがって上限申請しました。
問い合わせに必要な内容は以下になるようです。
- 月あたりの上限額をいくらまで引き上げるか
- どんなアプリから SMS を発信するか
- 合意したユーザにだけ SMS を発信することをどのように担保しているのか
- 1 日何通送るか
- 何というメッセージを送るか
【参考】AWS に送付した内容
I will write the details that follow.
- The dollar limit requested: $ 50
- Link or name of site or app that will be sending SMS:
arn:aws:lambda:{Lambda 関数の ARN}
This is my personal app. - Details of your opt-in process and any other ways in which you ensure you are only sending to people who have requested your messages:
This account is in the sandbox, so I can only send SMS to authenticated phone numbers. - A list of countries in which the recipients of your messages are located: Japan
- The maximum number of messages you expect to send per day: 20
- Message templates you plan to send:
ツイート数が増えました。今日のカウント数:XX。昨日のカウント数:XX。 - Any additional information that would help us better understand your use case:
This account is for project members to learn AWS.I'm learning AWS. I'm trying some services.
1 日たたずに返信が来ました。
アジアパシフィック (東京) リージョンにおいて、新しい SMS の月額使用限度額$1000USD が適用されました。また、お客様のアカウントを Amazon SNS サンドボックスから移動いたしました。
頼んでいませんが、SNS サンドボックスから移動もされたようです。
対処法 Step2
SNS のマネジメントコンソール > テキストメッセージング(SMS) > テキストメッセージングの優先設定
上限を 1 ドルから 50 ドルに変更しました。
まとめ
Amazon SNSを利用して、Lambda関数からメールを送ることができました。
もう少し非同期処理に近いところを学んでみようと思います。
関連記事
Lambda 関数の作り方・ソースコードのアップロード方法