SNS トピックを削除してもそこに関連づけられた SNS サブスクリプションは削除されない

SNS トピックを消すときに SNS サブスクリプションもまとめて消してくれればいいのに……と思います。

SNS サブスクリプションごと SNS トピックを削除したいが……

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

SNS トピックと SNS サブスクリプションに関して、以下の仕様があります。

  • SNS トピックには 0 個以上の SNS サブスクリプションを関連づけられる
  • SNS サブスクリプションはひとつの SNS トピックにのみ関連づけられる
  • SNS トピックに関連づかない形で SNS サブスクリプションを作成できない

ここで、SNS サブスクリプションが関連づいた SNS トピックを削除するケースを考えます。

sns topic delete sns subscriptions

SNS トピックと共に SNS サブスクリプションも削除されることを期待したくなりますが、そうはなりません。削除済みの SNS トピックに関連づけられた SNS サブスクリプションが誕生します。

実際に試してみた結果と、「削除済みの SNS トピックに関連づけられた SNS サブスクリプション」を一括で削除する方法をまとめます。

SNS トピックを削除しても SNS サブスクリプションが消えない

冒頭の繰り返しになりますが、SNS トピックを削除してもそこに関連づく SNS サブスクリプションは削除されません。

AWS ドキュメントを見ると以下の記述があります。

Once an Amazon SNS topic is deleted, all subscriptions associated with that topic are also deleted. This means that any confirmed subscription that was previously established for the deleted topic will no longer exist.

(日本語版)

Amazon SNS トピックを削除すると、そのトピックに関連するすべてのサブスクリプションも削除されます。つまり、削除されたトピックに対して以前に確立された、確認済みのサブスクリプションは存在しなくなります。

これをそのまま捉えると SNS トピックの削除とともに SNS サブスクリプションが削除されるように読めますが、実際にはそのような挙動になりません。 *1

マネジメントコンソールから確認する

今回はたまたま環境にあった SNS サブスクリプションを例に確認していきます。

「Publish-to-Lambda」という名称の SNS トピック、に関連付ける形で作成した SNS サブスクリプションが 2 つあります。Publish-to-Lambda トピックはすでに削除済みの状態です。(削除してから14日程度経過済み)

コンソール上では以下のように確認できます。

Amazon_SNS_subscrioptions

↑の画面から、Publish-to-Lambda トピックへのリンクを押下してみます。存在しない、というエラーが発生します。

トピックの属性を取得できませんでした。
エラーコード: NotFound - エラーメッセージ: Topic does not exist

SNS_topic_does_not_exsits

もちろん SNS トピック一覧にも Publish-to-Lambdaは存在しません。

Amazon_SNS_topic_exists

AWS CLI から確認する

以下のコマンドで確認していきます。

aws sns list-topics

SNS トピックの一覧を出力するコマンドです。

% aws sns list-topics --output text
TOPICS  arn:aws:sns:ap-northeast-1:000000000000:insightwatch-InvokeLambdaFunctionTopic
TOPICS  arn:aws:sns:ap-northeast-1:000000000000:insightwatch-NotificationTopic
TOPICS  arn:aws:sns:ap-northeast-1:000000000000:test-topic

実行しても SNS トピック Publish-to-Lambda は結果に現れません。

aws sns list-subscriptions

SNS サブスクリプションの一覧を出力するコマンドです。

特定のトピックに関連づく SNS サブスクリプションのみをフィルタリングしてみます。

# 変数に SNS トピックの ARN を格納
% TOPIC_ARN="arn:aws:sns:ap-northeast-1:000000000000:Publish-to-Lambda"

# コマンド実行
% aws sns list-subscriptions\
  --query "Subscriptions[?TopicArn=='${TOPIC_ARN}']"
[
    {
        "SubscriptionArn": "arn:aws:sns:ap-northeast-1:000000000000:Publish-to-Lambda:8851927b-d074-4a48-84e2-f01f2a6f6e7d",
        "Owner": "000000000000",
        "Protocol": "email",
        "Endpoint": "chiba.yukihiro@example.com",
        "TopicArn": "arn:aws:sns:ap-northeast-1:000000000000:Publish-to-Lambda"
    },
    {
        "SubscriptionArn": "arn:aws:sns:ap-northeast-1:000000000000:Publish-to-Lambda:b54a1670-6021-4da7-9a58-86ba7ff436e1",
        "Owner": "000000000000",
        "Protocol": "lambda",
        "Endpoint": "arn:aws:lambda:ap-northeast-1:000000000000:function:sns-message-python",
        "TopicArn": "arn:aws:sns:ap-northeast-1:000000000000:Publish-to-Lambda"
    }
]

すでに存在しない SNS トピック Publish-to-Lambda に関連づけられた SNS サブスクリプションが得られます。

list-subscriptions-by-topic

SNS トピックを指定し、それに関連づく SNS サブスクリプションの一覧を出力するコマンドです。

# 変数に SNS トピックの ARN を格納
% TOPIC_ARN="arn:aws:sns:ap-northeast-1:000000000000:Publish-to-Lambda"

# コマンド実行
% aws sns list-subscriptions-by-topic\
  --topic-arn $TOPIC_ARN

An error occurred (NotFound) when calling the ListSubscriptionsByTopic operation: Topic does not exist

SNS トピック Publish-to-Lambda が存在しないとしてエラーになります。


ここまでの結果で、SNS トピックを削除してもそこに関連づいた SNS サブスクリプションは削除されない、ということが確認できました。

同名の SNS トピックを作成し直したらどうなるの?

Publish-to-Lambda という名称の SNS トピックを改めて作成してみました。

新 Publish-to-Lambda に勝手にサブスクリプションが関連づけられることはありませんでした。

Amazon_SNS_New_topic

旧 Publish-to-Lambda トピックに関連づいていた SNS サブスクリプションの画面で以下を押下すると新 Publish-to-Lambda に遷移するというややこしい事態になります。

SNS_Subscripstions_old

事情を知らない人が見ると混乱しかしないので、誰かを困らせたい時以外はこのような操作(SNS トピックを作り直す)はやめておきましょう。

すでに存在しない SNS トピックに関連づいた SNS サブスクリプションを一括で削除する

関連づく SNS トピックを持たない SNS サブスクリプションは、言ってみれば無駄な存在です。改めて別の SNS トピックに関連づけるような操作もできません。

ということで、一括で削除したくなります。以下のスクリプトを作成しました。

名称例:TrashUnnecessarySubscriptions.sh

#!/bin/bash

# SNSトピック一覧を取得
topics=$(aws sns list-topics | jq -r '.Topics[].TopicArn')

# SNSサブスクリプション一覧を取得
subscriptions=$(aws sns list-subscriptions)

# トピックに紐づかないサブスクリプションを削除
echo $subscriptions | jq -c '.Subscriptions[]' | while read subscription; do
  subscriptionArn=$(echo $subscription | jq -r '.SubscriptionArn')
  topicArn=$(echo $subscription | jq -r '.TopicArn')

  # サブスクリプションのtopicArnがtopicsに含まれていなければ削除
  if ! echo $topics | grep $topicArn > /dev/null
  then
    aws sns unsubscribe --subscription-arn $subscriptionArn
    echo "削除したサブスクリプション: $subscriptionArn"
  fi
done

これは以下環境で動作確認しています。

  • macOS
    • Ventura 13.4.1
    • zsh
    • jq 1.6
    • AWS CLI 2.9.1
  • AWS CloudShell
    • bash
    • Jq 1.5
    • AWS CLI 2.12.2

実行イメージは以下の通り。

% sh TrashUnnecessarySubscriptions.sh
削除したサブスクリプション: arn:aws:sns:ap-northeast-1:000000000000:Publish-to-Lambda:8851927b-d074-4a48-84e2-f01f2a6f6e7d
削除したサブスクリプション: arn:aws:sns:ap-northeast-1:000000000000:Publish-to-Lambda:b54a1670-6021-4da7-9a58-86ba7ff436e1

いちいちスクリプトを作るのも面倒、という剛気な方は以下のワンライナーでどうぞ。

topics=$(aws sns list-topics | jq -r '.Topics[].TopicArn') && subscriptions=$(aws sns list-subscriptions) && echo $subscriptions | jq -c '.Subscriptions[]' | while read subscription; do subscriptionArn=$(echo $subscription | jq -r '.SubscriptionArn') && topicArn=$(echo $subscription | jq -r '.TopicArn') && if ! echo $topics | grep $topicArn > /dev/null; then aws sns unsubscribe --subscription-arn $subscriptionArn && echo "削除したサブスクリプション: $subscriptionArn"; fi; done

終わりに

SNS トピックを削除してもそれに紐づく SNS サブスクリプションは削除されない、という話でした。AWS ドキュメントの記述を見た時に捉えた内容とは真逆の結果だったので、試してみることは大事だなと改めて感じました。

削除済みの SNS トピックに関連づいた SNS サブスクリプションを残しておいてもいいことはないと思いますので、定期的にお掃除しましょう。一括で削除する内容もいい感じにまとまったので、参考になれば幸いです。

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

参考

脚注

  1. なので、この記述が何を表しているのかがちょっとわかっていません。SNSサブスクリプションというリソースそのものの削除有無について言及しているわけではない、ということなのでしょうか、、