[アップデート] SNS サブスクリプションフィルターのスコープとしてメッセージ属性に加えメッセージ本文がサポートされました

メッセージ属性だけでなくメッセージ本文でもフィルタリングができるようになったので、いろんな構成をシンプルにできそうです。

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

コンバンハ、千葉(幸)です。

Amazon SNS でペイロード(メッセージ本文)ベースでのメッセージフィルタリングがサポートされました。

これまではメッセージ属性でだけフィルタリングが可能でしたが、メッセージ本文でもできるようになりました。


ネタが被りました

いそいそとアップデートブログを書いていたら 若槻 が同じネタでブログを書いていました。

若干アプローチが違うので、あわせて読むとより理解が捗るかと思います!!!


何が変わったのか

イメージは以下です。

SNS トピックはイベントソースからイベントを受け取り、サブスクライブしたエンドポイントにメッセージを発行します。

イベントソースとなる AWS サービスは多々あり、その一覧は以下にまとまっています。

例えば Amazon RDS イベントサブスクリプションをイベントソースとするイベントを例にとると、その内訳は以下の構造になっています。

{
  "Records": [
    {
      "EventSource": "aws:sns",
      "EventVersion": "1.0",
      "EventSubscriptionArn": "arn:aws:sns:ap-northeast-1:012345678910:Publish-to-Lambda:b54a1670-6021-4da7-9a58-86ba7ff436e1",
      "Sns": {
        "Type": "Notification",
        "MessageId": "862f377d-4fd4-5a82-8156-6c9246b1629a",
        "TopicArn": "arn:aws:sns:ap-northeast-1:012345678910:Publish-to-Lambda",
        "Subject": "RDS Notification Message",
        "Message": "{\"Event Source\":\"db-cluster\",\"Event Time\":\"2022-11-16 15:52:04.443\",\"Identifier Link\":\"https://console.aws.amazon.com/rds/home?region=ap-northeast-1#dbclusters:id=database-1\",\"Source ID\":\"database-1\",\"Source ARN\":\"arn:aws:rds:ap-northeast-1:012345678910:cluster:database-1\",\"Event ID\":\"http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html#RDS-EVENT-0150\",\"Event Message\":\"DB cluster stopped\"}",
        "Timestamp": "2022-11-16T15:52:05.043Z",
        "SignatureVersion": "1",
        "Signature": "qo30sQiOPS8r1WBVHFCvyrAnX6+q7FYGZYKga(略)vP1Mnv/D6KYrovs13E3YqjrXPw==",
        "SigningCertUrl": "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-56e(略)385.pem",
        "UnsubscribeUrl": "https://sns.ap-northeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-northeast-1:012345678910:Publish-to-Lambda:b54a1(略)ff436e1",
        "MessageAttributes": {
          "Resource": {
            "Type": "String",
            "Value": "arn:aws:rds:ap-northeast-1:012345678910:cluster:database-1"
          },
          "EventID": {
            "Type": "String",
            "Value": "RDS-EVENT-0150"
          }
        }
      }
    }
  ]
}

12 行目がメッセージ本文(ペイロード)、18-27 行目がメッセージ属性です。

(メッセージ本文を整形すると以下のようになります。)

{
    "Event Source": "db-cluster",
    "Event Time": "2022-11-16 15:52:04.443",
    "Identifier Link": "https://console.aws.amazon.com/rds/home?region=ap-northeast-1#dbclusters:id=database-1",
    "Source ID": "database-1",
    "Source ARN": "arn:aws:rds:ap-northeast-1:012345678910:cluster:database-1",
    "Event ID": "http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html#RDS-EVENT-0150",
    "Event Message": "DB cluster stopped"
}

なお、今回のイベント例ではメッセージ属性が含まれていましたが、すべてのイベントに含まれているわけではありません。

SNS トピックでは宛先エンドポイントごとにサブスクリプションを作成します。デフォルトではすべてのメッセージをサブスクリプションしたエンドポイントに発行しますが、オプションでフィルタリングポリシーを設定することでフィルタリングが可能です。

従来はフィルタリングポリシーのスコープとなるのはメッセージ属性のみでしたが、今回のアップデートによりメッセージ本文もスコープにできるようになりました。

設定画面でもこのようにスコープの選択ができますね。

SNS_payload_Simple_Notification_Service

メッセージの内容に応じて後続のリソースに振り分ける、というのを、間に何か挟んだりトピックを分けたりすることなくお手軽に実現できるようになりました。

詳細は以下ドキュメントを詳細してください。

やってみた

今回は以下ブログで取り上げられている構成を試してみます。

ここでは S3 バケットに特定の接頭辞(autoもしくはhome)を持つオブジェクトが格納された際に、それぞれに応じた SQS キューにメッセージを振り分ける仕組みが想定されています。

そしてありがたいことに一式の構成をデプロイできる SAM テンプレートが用意されています。

SNS_Payload_sample

(画像は上記ブログより引用)

SAM によるリソース一式のデプロイ

今回は AWS Cloud9 環境から実行していきます。使用した SAM CLI のバージョンは以下です。

$ sam --version
SAM CLI, version 1.57.0

以下に SAM のテンプレートが公開されています。

ローカルにテンプレートをダウンロードします。

$ curl -O https://raw.githubusercontent.com/aws-samples/aws-sns-samples/master/templates/SNS-Payload-Based-Filtering-SAM.template
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2757  100  2757    0     0   9108      0 --:--:-- --:--:-- --:--:--  9099

テンプレートを指定してビルドを行います。

$ sam build -t SNS-Payload-Based-Filtering-SAM.template 

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch
[*] Deploy: sam deploy --guided

ガイドに従いデプロイを行います。

$ sam deploy --guided

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]: sns-payload
        AWS Region [ap-northeast-1]: 
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [y/N]: y
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: y
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]: y
        Save arguments to configuration file [Y/n]: y
        SAM configuration file [samconfig.toml]:  
        SAM configuration environment [default]: 

        Looking for resources needed for deployment:
         Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-58h089x7vwj
         A different default S3 bucket can be set in samconfig.toml

        Saved arguments to config file
        Running 'sam deploy' for future deployments will use the parameters saved above.
        The above parameters can be changed by modifying samconfig.toml
        Learn more about samconfig.toml syntax at 
        https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html


        Deploying with following values
        ===============================
        Stack name                   : sns-payload
        Region                       : ap-northeast-1
        Confirm changeset            : True
        Disable rollback             : True
        Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-58h089x7vwj
        Capabilities                 : ["CAPABILITY_IAM"]
        Parameter overrides          : {}
        Signing Profiles             : {}

Initiating deployment
=====================
Uploading to sns-payload/0f37e669edf344868277b8a6a14533ae.template  3190 / 3190  (100.00%)

Waiting for changeset to be created..
CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------
Operation                     LogicalResourceId             ResourceType                  Replacement                 
---------------------------------------------------------------------------------------------------------------------
+ Add                         AutoInsuranceEventsQueue      AWS::SQS::Queue               N/A                         
+ Add                         AutoInsuranceEventsSubscrip   AWS::SNS::Subscription        N/A                         
                              tion                                                                                    
+ Add                         HomeInsuranceEventsQueue      AWS::SQS::Queue               N/A                         
+ Add                         HomeInsuranceEventsSubscrip   AWS::SNS::Subscription        N/A                         
                              tion                                                                                    
+ Add                         InsuranceEventsBucket         AWS::S3::Bucket               N/A                         
+ Add                         InsuranceEventsQueuePolicy    AWS::SQS::QueuePolicy         N/A                         
+ Add                         InsuranceEventsTopicPolicy    AWS::SNS::TopicPolicy         N/A                         
+ Add                         InsuranceEventsTopic          AWS::SNS::Topic               N/A                         
---------------------------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:012345678910:changeSet/samcli-deploy1669179940/80e18d2f-c484-4b7d-ab2e-e26c22d0f9f5


Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y

2022-11-23 05:06:03 - Waiting for stack create/update to complete

CloudFormation events from stack operations (refresh every 0.5 seconds)
---------------------------------------------------------------------------------------------------------------------
ResourceStatus                ResourceType                  LogicalResourceId             ResourceStatusReason        
---------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS            AWS::SQS::Queue               HomeInsuranceEventsQueue      -                           
CREATE_IN_PROGRESS            AWS::SNS::Topic               InsuranceEventsTopic          -                           
CREATE_IN_PROGRESS            AWS::SQS::Queue               AutoInsuranceEventsQueue      -                           
CREATE_IN_PROGRESS            AWS::SQS::Queue               HomeInsuranceEventsQueue      Resource creation Initiated 
CREATE_IN_PROGRESS            AWS::SNS::Topic               InsuranceEventsTopic          Resource creation Initiated 
CREATE_IN_PROGRESS            AWS::SQS::Queue               AutoInsuranceEventsQueue      Resource creation Initiated 
CREATE_COMPLETE               AWS::SNS::Topic               InsuranceEventsTopic          -                           
CREATE_IN_PROGRESS            AWS::SNS::TopicPolicy         InsuranceEventsTopicPolicy    -                           
CREATE_IN_PROGRESS            AWS::SNS::TopicPolicy         InsuranceEventsTopicPolicy    Resource creation Initiated 
CREATE_COMPLETE               AWS::SNS::TopicPolicy         InsuranceEventsTopicPolicy    -                           
CREATE_IN_PROGRESS            AWS::S3::Bucket               InsuranceEventsBucket         -                           
CREATE_IN_PROGRESS            AWS::S3::Bucket               InsuranceEventsBucket         Resource creation Initiated 
CREATE_COMPLETE               AWS::S3::Bucket               InsuranceEventsBucket         -                           
CREATE_COMPLETE               AWS::SQS::Queue               HomeInsuranceEventsQueue      -                           
CREATE_COMPLETE               AWS::SQS::Queue               AutoInsuranceEventsQueue      -                           
CREATE_IN_PROGRESS            AWS::SNS::Subscription        HomeInsuranceEventsSubscrip   -                           
                                                            tion                                                      
CREATE_IN_PROGRESS            AWS::SNS::Subscription        HomeInsuranceEventsSubscrip   Resource creation Initiated 
                                                            tion                                                      
CREATE_COMPLETE               AWS::SNS::Subscription        HomeInsuranceEventsSubscrip   -                           
                                                            tion                                                      
CREATE_IN_PROGRESS            AWS::SQS::QueuePolicy         InsuranceEventsQueuePolicy    -                           
CREATE_IN_PROGRESS            AWS::SNS::Subscription        AutoInsuranceEventsSubscrip   -                           
                                                            tion                                                      
CREATE_IN_PROGRESS            AWS::SQS::QueuePolicy         InsuranceEventsQueuePolicy    Resource creation Initiated 
CREATE_COMPLETE               AWS::SQS::QueuePolicy         InsuranceEventsQueuePolicy    -                           
CREATE_IN_PROGRESS            AWS::SNS::Subscription        AutoInsuranceEventsSubscrip   Resource creation Initiated 
                                                            tion                                                      
CREATE_COMPLETE               AWS::SNS::Subscription        AutoInsuranceEventsSubscrip   -                           
                                                            tion                                                      
CREATE_COMPLETE               AWS::CloudFormation::Stack    sns-payload                   -                           
---------------------------------------------------------------------------------------------------------------------
CloudFormation outputs from deployed stack
------------------------------------------------------------------------------------------------------------------------
Outputs                                                                                                                
------------------------------------------------------------------------------------------------------------------------
Key                 InsuranceEventsBucketName                                                                          
Description         The name of the S3 bucket to which insurance documents are uploaded, and from which events are     
triggered                                                                                                              
Value               sns-payload-insuranceeventsbucket-l93auq0ntam1                                                     
------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - sns-payload in ap-northeast-1

これにより、以下が作成されました。

  • S3 バケット(イベント含む)
  • SNS トピック(トピックポリシー含む)
  • SQS キュー *2 (キューポリシー含む)
  • SNS サブスクリプション *2 (フィルタリングポリシー含む)

メッセージ本文に応じたフィルタリングを確認する

2 つある SNS サブスクリプションのフィルタリングポリシーはそれぞれ以下のような値で作成されています。

autoのSQSキュー用

{
  "Records": {
    "s3": {
      "object": {
        "key": [{
          "prefix": "auto-"
        }]
      }
    },
    "eventName": [{
      "prefix": "ObjectCreated:"
    }]
  }
}

homeのSQSキュー用

{
  "Records": {
    "s3": {
      "object": {
        "key": [{
          "prefix": "home-"
        }]
      }
    },
    "eventName": [{
      "prefix": "ObjectCreated:"
    }]
  }
}

対応する接頭辞を持つオブジェクトの Put イベントがあれば後続の SQS キューにメッセージを発行する、という状態です。

S3 バケットにオブジェクトを Put することでイベントを発火させます。今回はautoを 4 つ、homeを 2 つ Put しました。

SNS_Payload_S3_Management_Console

S3 イベント→ SNS トピック→ SQS キューという形でイベントが流れていきます。SQS キューを確認すると、それぞれ対応したメッセージを受信していることがわかります。

SNS_Payload_Amazon_SQS

SQS キューに入ったメッセージはこのような感じ。ここにはメッセージ属性は含まれていないので、アップデート前ではフィルタリングポリシーが使えなかったことが分かります。

{
  "Type" : "Notification",
  "MessageId" : "d485d7de-dae5-51a2-bd1e-8c2968933974",
  "TopicArn" : "arn:aws:sns:ap-northeast-1:012345678910:insurance-events-topic",
  "Subject" : "Amazon S3 Notification",
  "Message" : "{\"Records\":[{\"eventVersion\":\"2.1\",\"eventSource\":\"aws:s3\",\"awsRegion\":\"ap-northeast-1\",\"eventTime\":\"2022-11-23T05:31:33.233Z\",\"eventName\":\"ObjectCreated:Put\",\"userIdentity\":{\"principalId\":\"AWS:AROAQ3BIIH732QEGJXBGU:cm-chiba.yukihiro\"},\"requestParameters\":{\"sourceIPAddress\":\"xx.xx.25.83\"},\"responseElements\":{\"x-amz-request-id\":\"YSR0M0C13618E6QH\",\"x-amz-id-2\":\"02QUPfFMnDzo1enRtf2Q12KYVcFEs7A1XHgD6REOUMfqZJiWDwAZzhMMMD8LblWJjCBrYqcGSTUg3FtyUVcV8rkuZEU6FMfoUg++XJnMK8A=\"},\"s3\":{\"s3SchemaVersion\":\"1.0\",\"configurationId\":\"46cc3514-078b-4fd3-a1dd-39fc5ee5f826\",\"bucket\":{\"name\":\"sns-payload-insuranceeventsbucket-l93auq0ntam1\",\"ownerIdentity\":{\"principalId\":\"A21653DPHJ2PYW\"},\"arn\":\"arn:aws:s3:::sns-payload-insuranceeventsbucket-l93auq0ntam1\"},\"object\":{\"key\":\"auto-sample.txt\",\"size\":4,\"eTag\":\"13d6a250f529e31f9dfda14bcba250ee\",\"sequencer\":\"00637DB03532F77F43\"}}}]}",
  "Timestamp" : "2022-11-23T05:31:34.414Z",
  "SignatureVersion" : "1",
  "Signature" : "R0Y7dqKTNb0EJbAEz9eZ1KN(略)gjGnQvU5utNzGHorQn3xqyvrgiUSA==",
  "SigningCertURL" : "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-56(略)85.pem",
  "UnsubscribeURL" : "https://sns.ap-northeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-northeast-1:012345678910:insurance-events-topic:b(略)1c6"
}

(ちなみにメッセージ本文を整形するとこのような形になっています。)

{
    "Records": [
        {
            "eventVersion": "2.1",
            "eventSource": "aws:s3",
            "awsRegion": "ap-northeast-1",
            "eventTime": "2022-11-23T05:31:33.233Z",
            "eventName": "ObjectCreated:Put",
            "userIdentity": {
                "principalId": "AWS:AROAQ3BIIH732QEGJXBGU:cm-chiba.yukihiro"
            },
            "requestParameters": {
                "sourceIPAddress": "xx.xx.25.83"
            },
            "responseElements": {
                "x-amz-request-id": "YSR0M0C13618E6QH",
                "x-amz-id-2": "02QUPfFMn(略)nMK8A="
            },
            "s3": {
                "s3SchemaVersion": "1.0",
                "configurationId": "46cc3514-078b-4fd3-a1dd-39fc5ee5f826",
                "bucket": {
                    "name": "sns-payload-insuranceeventsbucket-l93auq0ntam1",
                    "ownerIdentity": {
                        "principalId": "A21653DPHJ2PYW"
                    },
                    "arn": "arn:aws:s3:::sns-payload-insuranceeventsbucket-l93auq0ntam1"
                },
                "object": {
                    "key": "auto-sample.txt",
                    "size": 4,
                    "eTag": "13d6a250f529e31f9dfda14bcba250ee",
                    "sequencer": "00637DB03532F77F43"
                }
            }
        }
    ]
}

CloudWatch メトリクスではNumberOfNotificationsFilteredOut-MessageBodyというメトリクスで「メッセージ本文でフィルタリングが行われたイベント」の数が確認できます。

SNS_Topic_payload_CloudWatch_Management_Console

SNS サブスクリプションでメッセージ本文を用いたフィルタリングができることを確認しました。

終わりに

Amazon SNS でペイロード(メッセージ本文)ベースでのメッセージフィルタリングがサポートされた、というアップデートでした。

例えば今回試したような「S3 バケットに Put されたオブジェクトのプレフィックスに応じてメッセージを振り分ける」ということをやろうとした場合、アップデート前であれば以下のような構成を採る必要がありました。

  • S3 イベント通知、それを受ける SNS トピックを 2 セット準備するパターン
  • S3 イベントを Lambda 関数が受け、接頭辞を判断して SQS に振り分けるパターン
  • SNS トピックのメッセージを Lambda 関数が受け、接頭辞を判断して SQS に振り分けるパターン

今回のアップデートによりシンプルな構成で振り分けを実現できるようになったことが分かります。

SNS のイベントソースとなり得る AWS サービスは多数ありますので、今回のアップデートを活かせる構成が無いか考えてみると楽しそうです。

以上、 チバユキ (@batchicchi) がお送りしました。