複数プロトコルを持つ SNS トピックに別々のメッセージを送信する方法

2023.11.13

こんにちは!アノテーションの砂川です。
以下のような SNS トピックに複数のサブスクリプションプロトコルがある場合に、特定のプロトコルにだけ任意のメッセージを送りたい..!なんて時がありませんか?
今回は、AWS CLI を用いて、それを1発で書く方法があったため、ご紹介します。

結論

以下のような CLI コマンドを書くことで、特定プロトコルに任意のメッセージを送り、それ以外のプロトコルには、デフォルトのメッセージを送信することができます。
メッセージの中身は適宜変更ください。

$ aws sns publish --topic-arn <トピック ARN> --message '{"default":"default message","LAMBDA":"I am Lambda","SMS":"I am SMS"}' --message-structure json

AWS CLI Command Reference に複数プロトコルに送信する方法についての記載があります。

--message (string)

The message you want to send.

If you are publishing to a topic and you want to send the same message to all transport protocols, include the text of the message as a String value. If you want to send different messages for each transport protocol, set the value of the MessageStructure parameter to json and use a JSON object for the Message parameter.

(機械翻訳の結果)

--message (string)

送信したいメッセージ。

トピックにパブリッシュしていて、すべてのトランスポートプロトコルに同じメッセージを送信したい場合は、メッセージのテキストを String 値として含めます。トランスポートプロトコルごとに異なるメッセージを送信したい場合は、MessageStructure パラメータの値を json に設定し、Message パラメータに JSON オブジェクトを使用します。

--message-structure (string)

Set MessageStructure to json if you want to send a different message for each protocol. For example, using one publish action, you can send a short message to your SMS subscribers and a longer message to your email subscribers. If you set MessageStructure to json , the value of the Message parameter must:

  • be a syntactically valid JSON object; and
  • - contain at least a top-level JSON key of “default” with a value that is a string.

You can define other top-level keys that define the message you want to send to a specific transport protocol (e.g., “http”).

(機械翻訳の結果)

--message-structure (string)

プロトコルごとに異なるメッセージを送信したい場合は、MessageStructurejson を指定します。例えば、1つのPublishアクションを使用して、SMS購読者には短いメッセージを送信し、Eメール購読者には長いメッセージを送信することができます。MessageStructurejson に設定した場合、Message パラメータの値は必ず以下でなければなりません:

  • 構文的に有効な JSON オブジェクトであること。
  • 少なくとも、"default" というトップレベルの JSON キーがあり、その値が文字列であること。

特定のトランスポート・プロトコル(たとえば "http")に送信するメッセージを定義する、他のトップレベル・キーを定義することもできる。

要点をまとめると、プロトコル毎に異なるメッセージを送信したい場合は、--message-structure json を指定し、その際の json オブジェクトのトップレベルには default キーを含めてねというもののようです。

やってみた

SNS トピックの作成

SNS トピックを作成します。トピックには、3つのプロトコル(SMS, EMAIL, LAMBDA)のサブスクリプションを登録しました。

Publish してみる

下記コマンドを実行し、各プロトコルに異なるメッセージを送ることができるか試してみます。

$ aws sns publish --topic-arn <トピック ARN> --message '{"default":"default message","LAMBDA":"I am Lambda","SMS":"I am SMS"}' --message-structure json

なお、Lambda(ランタイム:Python 3.11) のコードは Publish されたメッセージをそのままログに出力するように下記のように記載しておきます。

def lambda_handler(event, context):
    print("message = ",event["Records"][0]["Sns"]["Message"])

結果は以下のようになりました。
各プロトコルに異なるメッセージを Publish することができていますね。プロトコルを特に指定しなかった場合は、default に指定したメッセージが送られることも確認できました。

SMS

EMAIL

LAMBDA

補足:全プロトコルに同じメッセージを送るには?

前項では、各プロトコルに個別メッセージを送る方法を試しましたが、そもそも SNS トピックに1種類のサブスクリプションしかない場合やプロトコル毎にメッセージを分ける必要がない場合は、--message-structure jsonオプションは必要ありません。

そのため、json メッセージのトップレベルに default キーを含める必要もなく、以下のように書けば OK です。

$ aws sns publish --topic-arn <トピック ARN> --message '{"test":"this is test"}'

ちなみに、文字列であれば、以下のような json メッセージ以外も送れます。

$ aws sns publish --topic-arn <トピック ARN> --message "It's a me, Mario"

よく見るエラー

最後に、Publish 時によく見かけるエラーについても備忘録としてまとめます。

json メッセージをシングルクォートで括っていない

--message-structure json オプションを付与している際の--messageで指定するメッセージは'で括って、適切な json 構文にしましょう。

NG 例

$ aws sns publish --topic-arn <トピック ARN> --message {"default":"default message"} --message-structure json

An error occurred (InvalidParameter) when calling the Publish operation: Invalid parameter: Message Structure - JSON message body failed to parse

OK 例

$ aws sns publish --topic-arn <トピック ARN> --message '{"default":"default message"}' --message-structure json
{
    "MessageId": "<メッセージ ID>"
}

json メッセージのトップレベルに default キーがない

前述しましたが、--message-structure jsonを指定するのであれば、json メッセージのトップレベルに default キーを含めましょう。

NG 例

$ aws sns publish --topic-arn <トピック ARN> --message '{"test":"This is test"}' --message-structure json

An error occurred (InvalidParameter) when calling the Publish operation: Invalid parameter: Message Structure - No default entry in JSON message body

OK 例

$ aws sns publish --topic-arn <トピック ARN> --message '{"default":"This is test"}' --message-structure json
{
    "MessageId": "<メッセージ ID>"
}

まとめ

今回は、SNS トピックに紐づく複数のプロトコルそれぞれに個別のメッセージを送る方法についてご紹介しました。

私は、json メッセージを Publish するときは--message-structure jsonが必須だと思い込んでいたので、検証していてこんな使い方ができるのかと勉強になりました。今回の書き方を知っておくことで、開発時に無駄なトピックを作らずに個別メッセージに対するテストなどができそうでいいですね。

本記事がどなたかの参考になれば幸いです。
最後までお読みいただきありがとうございました!

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。