Lambda(SAM)からAmazon SNSトピックへpublishを行う方法メモ


* LocalStack上で動くAmazon SNSのトピックに対して、AWS SAM CLIで動くLambdaからpublishを行う方法をメモする。

## 構成
![sam-sns.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/ede9f228-fef0-f12c-76cc-62a82f1aefe6.png)


## LocalStack準備

* `docker-compose.yml`

  ```yaml
  version: "3.8"
  networks:
    container-link:
      name: docker.internal
  services:
    localstack:
      container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
      image: localstack/localstack
      ports:
        - "127.0.0.1:53:53"                # only required for Pro
        - "127.0.0.1:53:53/udp"            # only required for Pro
        - "127.0.0.1:443:443"              # only required for Pro
        - "127.0.0.1:4510-4530:4510-4530"  # only required for Pro
        - "127.0.0.1:4566:4566"
        - "127.0.0.1:4571:4571"
      environment:
        - SERVICES=${SERVICES- }
        - DEBUG=${DEBUG- }
        - DATA_DIR=${DATA_DIR- }
        - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
        - LOCALSTACK_API_KEY=${LOCALSTACK_API_KEY- }  # only required for Pro
        - HOST_TMP_FOLDER=${TMPDIR:-/tmp/}localstack
        - DOCKER_HOST=unix:///var/run/docker.sock
      volumes:
        - "${TMPDIR:-/tmp}/localstack:/tmp/localstack"
        - "/var/run/docker.sock:/var/run/docker.sock"
      networks:
        - container-link
  ```

* 起動

  ```shell
  docker-compose up
  ```

  



## SNS準備

### トピックの作成

```shell
aws sns create-topic --name test-topic --endpoint-url http://localhost:4566 --profile localstack 
{
    "TopicArn": "arn:aws:sns:ap-northeast-1:000000000000:test-topic"
}
```



### トピックへのサブスクライブ

* `email` プロトコルと `notification-endpoint` の E メールアドレスを指定する。

```shell
aws sns subscribe --topic-arn arn:aws:sns:ap-northeast-1:000000000000:test-topic --protocol email --notification-endpoint test@example.com --endpoint-url http://localhost:4566 --profile localstack 
{
    "SubscriptionArn": "arn:aws:sns:ap-northeast-1:000000000000:test-topic:36e4e475-b360-4c7a-96f0-0690371e7566"
}
```



## Lambda準備

* ひな形作成

  ```shell
  sam init
  ```

  ※ランタイムはPython3.8を選択。

* `template.yml`

  * Hello World Exampleを利用する。

  * /topic/subscribe(サブスクライブ用)と/topic/publish(パブリッシュ用)を設定するように以下修正

    ```yaml
    Events:
            PublishTopic:
              Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
              Properties:
                Path: /topic/publish
                Method: post
    ```

    

* `requrements.txt`

  ```
  requests
  boto3
  ```

  ※SNSアクセス用にboto3追加

  

* Lambda処理(`app.py`)修正

  ```python
  import json
  from boto3.session import Session
  
  # TopicARN
  TOPIC_ARN = u'arn:aws:sns:ap-northeast-1:000000000000:test-topic'
  
  # SNS 接続設定
  session = Session(
      aws_access_key_id='dummy',
      aws_secret_access_key='dummy',
      region_name='ap-northeast-1'
  )
  client = session.client(
      service_name='sns', 
      endpoint_url='http://localstack:4566'
  )
  
  
  def lambda_handler(event, context):
      # メッセージpublish
      request = {
          'TopicArn': TOPIC_ARN,
          'Message': json.loads(event["body"])["msg"],
          'Subject': json.loads(event["body"])["subject"]
      }
      response = client.publish(**request)
  
      return {
          "statusCode": 200,
          "body": json.dumps({"MessageId":response["MessageId"]}),
      }
  
  ```



## 動作確認

* ビルド

  ```
  sam build
  ```

* 実行

  ```
  sam local start-api --docker-network docker.internal
  ```

* メッセージPublish

  * リクエスト

    ```http
    POST /topic/publish HTTP/1.1
Host: localhost:3000
Content-Type: application/json
Content-Length: 61
 {    
      "msg":"test message",    
      "subject":"test subject"
 }
    ```

  * レスポンス

    ```json
    {
        "MessageId": "e08bba69-1ceb-4eef-9b88-19f74529bcbf"
 }
    ```

    


## 参考情報

* [boto3を使ってAmazon SNSへpublishしてみた](https://dev.classmethod.jp/articles/publish_to_sns_using_boto3/)