1
0

More than 1 year has passed since last update.

CFnでSNSアクセスポリシーを自動追加する【Lambda-backedカスタムリソース】

Last updated at Posted at 2022-02-21

背景

例えばEventBridgeから既存のAWS SNSトピックをパブリッシュするルールを作成するとき。
1. AWSコンソール上から手動でEventBridgeを作成した場合
→ パブリッシュ先SNSのアクセスポリシーにも自動的にEventBridgeの許可ルールが追加されます。
2. 一方、CLIやCFnから同様にEventBridgeを作成した場合
→ SNSにアクセス許可が自動追加されることはありません。そのままではEventBridgeからのパブリッシュがSNS側で拒否されてしまいます。

そのため、EventBridgeからの許可ポリシーもCFnで自動追加できないかなーと思いました。
(手動でポチポチ追加しても良いけど、現場では多数のAWSアカウントを管理しているため、簡単に追加できるよう自動化したい。要は楽したい。)

解決策1と懸念点

クラメソさんの記事にヒントをもらって、Lambda-BackedカスタムリソースをCFnに記述しアクセスポリシーを自動更新できました。
AWS SDK(python)でset_topic_attributesにより更新できます。

懸念点

ただ、このset_topic_attributesだとSNSアクセスポリシーの「追加」ではなく「上書き」になります。
そのためもし既存のSNSトピックに予め別のアクセスポリシーが追加されている状態でこのカスタムリソースを適用した場合、予めあったポリシーは削除されてしまいます。
こういうトラブルの原因になるカスタムリソースは作りたくないなと思い、改善策を模索しました。

解決策2

答えはシンプル。SDKのget_topic_attribetsでSNSのトピックポリシーを取得しEventBridgeのアクセス許可を追記して、set_topic_attributesするだけ。
カスタムリソースで使用したLambda関数は以下の通り。
TopicArnAwsAccountIdはカスタムリソースから渡すパラメータです。

lambda-addSnsAccessPolicy.py
import json
import boto3
import logging
import cfnresponse

logger = logging.getLogger()
logger.setLevel(logging.INFO)

sns = boto3.client('sns')

def lambda_handler(event, context):
  logger.info("event == {}".format(event))
  response = None

  topicArn = event['ResourceProperties']['TopicArn']
  awsAccountId = event['ResourceProperties']['AwsAccountId']

  addPolicy = {
    "Sid": "AWSEvent_Allow",
    "Effect": "Allow",
    "Principal": {
      "Service": "events.amazonaws.com"
    },
    "Action": "sns:Publish",
    "Resource": topicArn,
    "Condition": {
      "StringEquals": {
        "AWS:SourceAccount": awsAccountId
      }
    }
  }

  if event['RequestType'] == 'Create':
    # 既存のトピックポリシーを取得
    attributes = sns.get_topic_attributes(TopicArn = topicArn)
    policy = eval(attributes["Attributes"]["Policy"])
    logger.info("policy = {}".format(policy))

    # EventBridgeからのパブリッシュ許可を追記
    policy["Statement"].append(addPolicy)
    logger.info("updatepolicy = {}".format(policy))

    # トピックポリシーを上書き
    responce=sns.set_topic_attributes(
      TopicArn=topicArn,
      AttributeName='Policy',
      AttributeValue=json.dumps(policy)
    )

  if event['RequestType'] == 'Update':
    cfnresponse.send(event, context, cfnresponse.SUCCESS, {})

  if event['RequestType'] == 'Update':
    cfnresponse.send(event, context, cfnresponse.SUCCESS, {})

  logger.info("response == {}".format(responce))
  cfnresponse.send(event, context, cfnresponse.SUCCESS, responce)

このLambda関数、カスタムリソース等のCloudFormationテンプレートはGitHubリポジトリにあげています。
詳しくご覧になりたい方はどうぞ。

実際にやってみる

まず、既存SNSのアクセスポリシーを確認します。デフォルトのポリシーと、S3からのパブリッシュ許可が設定されている状態です。

$ aws sns get-topic-attributes --topic-arn arn:aws:sns:ap-northeast-1:{AWSアカウントID}:TestTopic
(ポリシーの部分だけ抜粋)
"Policy": 
"{\"Version\":\"2008-10-17\",
\"Id\":\"__default_policy_ID\",
\"Statement\":[
↓デフォルトのポリシー
{\"Sid\":\"__default_statement_ID\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"SNS:GetTopicAttributes\",\"SNS:SetTopicAttributes\",\"SNS:AddPermission\",\"SNS:RemovePermission\",\"SNS:DeleteTopic\",\"SNS:Subscribe\",\"SNS:ListSubscriptionsByTopic\",\"SNS:Publish\"],\"Resource\":\"arn:aws:sns:ap-northeast-1:{AWSアカウントID}:TestTopic\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":\"{AWSアカウントID}\"}}},
↓S3からのパブリッシュ許可ポリシー
{\"Sid\":\"S3_Allow\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"s3.amazonaws.com\"},\"Action\":\"sns:Publish\",\"Resource\":\"arn:aws:sns:ap-northeast-1:{AWSアカウントID}:TestTopic\",\"Condition\":{\"StringEquals\":{\"AWS:SourceAccount\":\"{AWSアカウントID}\"}}}
]}"

カスタムリソースを含むCFnをデプロイします。
image.png
image.png

アクセスポリシーを再確認してみます。EventBridgeからのパブリッシュ許可ポリシーが追加されていることが確認できました!!もともと設定されていたポリシーも消されずに残ってますね。

$ aws sns get-topic-attributes --topic-arn arn:aws:sns:ap-northeast-1:{AWSアカウントID}:TestTopic
(ポリシーの部分だけ抜粋)
"Policy": 
"{\"Version\":\"2008-10-17\",
\"Id\":\"__default_policy_ID\",
\"Statement\":[
↓ もともとあったポリシー
{\"Sid\":\"__default_statement_ID\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"SNS:GetTopicAttributes\",\"SNS:SetTopicAttributes\",\"SNS:AddPermission\",\"SNS:RemovePermission\",\"SNS:DeleteTopic\",\"SNS:Subscribe\",\"SNS:ListSubscriptionsByTopic\",\"SNS:Publish\"],\"Resource\":\"arn:aws:sns:ap-northeast-1:{AWSアカウントID}:TestTopic\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":\"{AWSアカウントID}\"}}},
{\"Sid\":\"S3_Allow\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"s3.amazonaws.com\"},\"Action\":\"sns:Publish\",\"Resource\":\"arn:aws:sns:ap-northeast-1:{AWSアカウントID}:TestTopic\",\"Condition\":{\"StringEquals\":{\"AWS:SourceAccount\":\"{AWSアカウントID}\"}}},
↓ 今回追加されたEventBridgeからの許可ポリシー
{\"Sid\":\"AWSEvent_Allow\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"events.amazonaws.com\"},\"Action\":\"sns:Publish\",\"Resource\":\"arn:aws:sns:ap-northeast-1:{AWSアカウントID}:TestTopic\",\"Condition\":{\"StringEquals\":{\"AWS:SourceAccount\":\"{AWSアカウントID}\"}}}
]}"

最後に

調べてもこの自動追加方法の記事が意外と無く、自分で作ってみました。面倒なSNSアクセスポリシーの管理を楽にできて良かったです。
(解決策はシンプルですが、地味に右往左往してAWSサポートに聞いたりしてましたw)
カスタムリソースはあんまり使ってこなかったけど、自由度高くて便利ですね。良い勉強になりました。

参考

・【クラメソ記事】EventBridgeからSNSに通知ができなくてハマった話

・【ドキュメント】Amazon EventBridge のリソースベースのポリシーを使用する - Amazon SNS のアクセス許可
・【GitHubリポジトリ】CFn Lambda-backedカスタムリソースを使ってSNSトピックポリシー追加を自動化する

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0