SNSトピックのunsubscribeの無効化設定


<h3>はじめに</h3>
今回業務の中でSNSのサブスクリプションを誤って解除してしまうのを無効化したいという要件があったため、調べた中で作り直しが必要と記載のある記事が多く、作り直さなくても設定できたので、メモとして残そうと思います。

<h3>unsubscribeとは</h3>
一言でいうと、SNSトピックに設定しているサブスクリプションを解除することです。
SNSトピックにサブスクリプションを設定しているとインスタンスが起動したなど必要なメッセージを特定のメールアドレスに送信することができます。
そのメッセージの中のリンクをクリックしてしまうとunsubscribeされて、SNSトピックからサブスクリプションが削除されてしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/1ffccfed-7453-29df-56b1-57d006087482.png)

サブスクリプションの通知先としてメーリングリストを設定していると誰かしらが誤ってリンクを触ってしまった場合に必要な通知が受け取れなくなってしまうので、メールのリンクからのunsubscribeを無効化しておけば、メールのリンクからサブスクリプションの解除ができなくなります。

<h3>事前準備</h3>
まずはSNSトピックを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/99263204-5be0-6d3f-be74-dbd79701ea5c.png)

作成できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/2061062d-ba21-2061-41e2-3d71015bb74e.png)

<h3>通常の方法でサブスクリプションを作成した場合</h3>
テスト用のトピックTESTくんが作成できたので「サブスクリプションの作成」から作成ページを開き、必要事項を入力します。
今回はメールアドレスに通知設定をするので、プロトコルにEメール、エンドポイントに通知したいメールアドレスを入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/da415689-5103-d626-51c1-1909d15d8f26.png)

「サブスクリプションの作成」を押すと、設定したメールアドレスに以下の確認メールが送信され、「保留中の確認」のステータスでサブスクリプションが作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/943c1f8a-a94e-75e7-e132-36d3103436ef.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/9aff372b-d5d1-cbd3-0d05-2f7967615e49.png)

確認メールのリンク(Confirm subscription)を開くと、次のようなページが表示され、先ほどのサブスクリプションのステータスが「確認済み」となり、メッセージが発行されたら通知されるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/1e548273-5d11-edf3-98e5-08185a689606.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/678ed2b1-2d08-fa6a-644a-89319a5c4986.png)

この状態であれば、設定したメールアドレス宛にメッセージを通知できます。
試しにテストメッセージを発行してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/d6d2598e-c3c2-483a-f974-9581d5c7e602.png)
以下のようにメッセージが通知されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/1ffccfed-7453-29df-56b1-57d006087482.png)
「unsubscribeとは」の所で触れましたが、このメッセージのリンクをクリックすると、次の画面が表示されてサブスクリプションが解除されます。サブスクリプションが解除されている状態だとメッセージが発行されても受信できません。サブスクリプションのステータスは「確認済み」ですが、IDが「削除済み」になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/cd5e90ff-ae1a-d2d6-9abb-9588d3d9d2ad.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/ce2e46ec-1b9e-cded-29ca-5480423bd1d2.png)
サブスクリプションが解除されると設定していたメールアドレス宛に次のようなメールが送信されます。このメール内のResubscribeをクリックすると再度サブスクリプションが登録されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/7b8e1ab2-4e4e-2c2b-12e9-7e1515e8a830.png)

通常のサブスクリプションの方法で作成した時のunsubscribeの設定を確認してみましょう。この設定値はコンソールから確認することができず、CloudShellなどからAWS CLIを使用することで確認することができます。
```
[cloudshell-user@ip-XX-XX-XX-XX ~]$ aws sns get-subscription-attributes --subscription-arn (サブスクリプションARN) --region ap-northeast-1 
{
    "Attributes": {
        "Owner": "(アカウントID)",
        "RawMessageDelivery": "false",
        "TopicArn": "arn:aws:sns:ap-northeast-1:(アカウントID):TEST",
        "Endpoint": "設定したメールアドレス",
        "Protocol": "email",
        "PendingConfirmation": "false",
        "ConfirmationWasAuthenticated": "false",
        "SubscriptionArn": "arn:aws:sns:ap-northeast-1:(アカウントID):TEST:(サブスクリプションID)"
    }
}
[cloudshell-user@ip-XX-XX-XX-XX ~]$ 
```
使用したコマンドはget-subscription-attributesです。
オプションには、サブスクリプションARNが必須です。2つ目のオプションとしてリージョンを指定していますが対象サブスクリプションとコマンド入力する環境のリージョンが同一の場合は省略可能です。
メールからのサブスクリプション解除の設定はConfirmationWasAuthenticatedの値が関係しています。この値がfalseだとメールから解除可能、trueだとメールから解除不可です。今回のように特に意識せずにサブスクリプションを作成した場合は、falseで作成されます。そのためメールから解除ができてしまいます。

<h3>サブスクリプション作成時に設定する場合</h3>
まず通常のサブスクリプションの設定と同じようにサブスクリプションの作成をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/943c1f8a-a94e-75e7-e132-36d3103436ef.png)
確認メールが届くと思いますが、ここでリンクをクリックしないでください。
メールからのunsubscribeを無効にする場合は、このリンクをクリックせずにリンクをコピーしてください。「サブスクリプションの確認」からコピーしたリンクをそのまま入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/2ab6bfb6-0e70-5e69-dcfc-4bb26446c785.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/ca6a79bd-ad05-0f92-4511-54d18c741f0d.png)
これでメールからサブスクリプションの解除ができなくなりました。
実際にコマンドを入力して確認してみます。
```
[cloudshell-user@ip-XX-XX-XX-XX ~]$ aws sns get-subscription-attributes --subscription-arn (サブスクリプションARN) --region ap-northeast-1
{
    "Attributes": {
        "Owner": "(アカウントID)",
        "RawMessageDelivery": "false",
        "TopicArn": "arn:aws:sns:ap-northeast-1:(アカウントID):TEST",
        "Endpoint": "(設定したメールアドレス)",
        "Protocol": "email",
        "PendingConfirmation": "false",
        "ConfirmationWasAuthenticated": "true",
        "SubscriptionArn": "arn:aws:sns:ap-northeast-1:(アカウントID):TEST:(サブスクリプションID)"
    }
}
[cloudshell-user@ip-XX-XX-XX-XX ~]$ 
```
ConfirmationWasAuthenticatedの値がtrueで作成されました。本当にメールから解除できないか確認してみます。確認のためにテストメッセージを発行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/ef2b8ae8-7145-0f8a-c5af-af22b5d22416.png)
発行したメッセージに含まれるリンクをクリックすると、"Subscription not removed"と表示され、解除されませんでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/b021a486-bbec-7649-63b2-9c00964a8e02.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/03eb9b91-43ba-c357-fd0b-190137e77351.png)

これで誰かが誤ってリンクを触ってしまってもサブスクリプションが解除されることはなくなりましたね！
めでたしめでたし！

と言いたいところですが、既存のサブスクリプションの場合、サブスクリプションを選択しても「サブスクリプションの確認」が押せる状態になりません。これでは既存のサブスクリプションには解除無効化設定できないのでは、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/74f13a53-9238-00b6-23ea-ad16bd20cc0c.png)


<h3>既存のサブスクリプションに設定する場合</h3>
既存のサブスクリプションについても設定していきましょう。
(修正や変更というよりも既存の設定に同名で上書きするというのが正しいかもしれません。)
AWS CLIを使用します。
実際に実行したコマンドがこちらです。

```
[cloudshell-user@ip-XX-XX-XX-XX ~]$ aws sns subscribe --topic-arn (トピックARN) --protocol email --notification-endpoint (既存の設定のメールアドレス) --region ap-northeast-1
{
    "SubscriptionArn": "pending confirmation"
}
[cloudshell-user@ip-XX-XX-XX-XX ~]$
[cloudshell-user@ip-XX-XX-XX-XX ~]$ aws sns get-subscription-attributes --subscription-arn (サブスクリプションARN) --region ap-northeast-1
{
    "Attributes": {
        "Owner": "(アカウントID)",
        "RawMessageDelivery": "false",
        "TopicArn": "arn:aws:sns:ap-northeast-1:(アカウントID):TEST",
        "Endpoint": "設定したメールアドレス",
        "Protocol": "email",
        "PendingConfirmation": "false",
        "ConfirmationWasAuthenticated": "false",
        "SubscriptionArn": "arn:aws:sns:ap-northeast-1:(アカウントID):TEST:(サブスクリプションID)"
    }
}
[cloudshell-user@ip-XX-XX-XX-XX ~]$
[cloudshell-user@ip-XX-XX-XX-XX ~]$ aws sns confirm-subscription --topic-arn (トピックARN) --authenticate-on-unsubscribe true --region ap-northeast-1 --token (トークン)
[1] 2317
[cloudshell-user@ip-XX-XX-XX-XX ~]$
[cloudshell-user@ip-XX-XX-XX-XX ~]$ aws sns get-subscription-attributes --subscription-arn (サブスクリプションARN) --region ap-northeast-1
{
    "Attributes": {
        "Owner": "(アカウントID)",
        "RawMessageDelivery": "false",
        "TopicArn": "arn:aws:sns:ap-northeast-1:(アカウントID):TEST",
        "Endpoint": "(設定したメールアドレス)",
        "Protocol": "email",
        "PendingConfirmation": "false",
        "ConfirmationWasAuthenticated": "true",
        "SubscriptionArn": "arn:aws:sns:ap-northeast-1:(アカウントID):TEST:(サブスクリプションID)"
    }
}
[cloudshell-user@ip-XX-XX-XX-XX ~]$ 
```
今回4つのコマンドを実行しました。
1つ目がサブスクリプションの確認メールの送信コマンドです。オプションはトピックARN、プロトコル、エンドポイント、リージョンの4つを指定しています。今回はプロトコルにemail、エンドポイントに既存のメールアドレスを指定しています。未登録のメールアドレスを指定すれば新規サブスクリプションとして設定できます。このコマンドもリージョンは省略可能です。

このコマンドは先ほどのようにコンソールのSNSトピック「サブスクリプションの作成」から既存のメールアドレスを指定して作成しても問題ありません。
※既存のサブスクリプションのメールアドレスを指定して作成した場合、重複してサブスクリプションが作成されることは無く、通知メールだけ発行されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/e91578c2-f941-85bf-b300-603d45c7717f.png)
2つ目と4つ目は先ほどから使用しているサブスクリプションの確認コマンドです。3つ目のコマンドを実行する前と実行した後にサブスクリプションの設定がどのように変化があったかを確認するために確認コマンドを実行しています。

3つ目のコマンドがサブスクリプション解除の無効化設定のコマンドです。オプションはトピックARN、ConfirmationWasAuthenticatedの設定を変更するオプション、リージョン、トークンを指定しています。サブスクリプションを指定するオプションが無いのはトークンの中にどのサブスクリプションに対してのコマンドなのかが含まれているからだと思います。では、そのトークンはどこにあるのかというと、先ほど新規作成の時に「サブスクリプションの確認」を押して入力したURLの中に含まれています。先ほどメールのリンク(Confirm subscription)です。このリンクを一度メモなどに貼り付けて、トークンの所だけコピーしてコマンドに含めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/943c1f8a-a94e-75e7-e132-36d3103436ef.png)
```
↓実際にリンクをコピーして貼り付けたもの
https://sns.ap-northeast-1.amazonaws.com/confirmation.html?TopicArn=arn:aws:sns:ap-northeast-1:(アカウントID):TEST&Token=(ランダム文字列)&Endpoint=(通知先メールアドレス)
※トークンはToken=より後ろ全てになります。
↓実行するコマンド
aws sns confirm-subscription --topic-arn (トピックARN) --authenticate-on-unsubscribe true --region ap-northeast-1 --token (ランダム文字列)&Endpoint=(通知先メールアドレス)
```
これで改めて既存のサブスクリプションも解除ができなくなりました👏
<h3>最後に</h3>
SNSトピックの通知先としてメーリングリストをサブスクリプションに登録している場合も多いと思いますので、特にメールからサブスクリプション解除したいような場合を除き、サブスクリプション解除の無効化設定を検討してみてはいかがでしょうか。
最後まで読んでいただきありがとうございました。