クロスアカウントなLambdaをSNS TopicにSubscribeする

AmazonLambda

西澤です。今回は、SNS Topicに異なるAWSアカウントのLambdaをSubscribeする手順をまとめておきます。下記の公式ドキュメントに丁寧な手順が書かれてはあるのですが、確認の手順等、注意事項がいくつかあったので、それを補う形で書いてみたいと思います。

概要

今回整理するのは、AWSアカウントA(111111111111)のSNS Topicから、AWSアカウントB(222222222222)のLambda Functionを呼び出すことができるようにする為に必要となる権限設定の手順です。

SNS_Lambda_CrossAccount

SNS TopicやLambda Functionそのものの作成手順には触れません。それぞれ既に作成済であることを前提とします。公式ドッキュメンとには一部AWS Management Consoleからは行えない操作が含まれているとの記載がありましたが、この記事ではGUIで設定することもできたので、合わせてご紹介したいと思います。

クロスアカウント権限を設定するためのすべてのオプションが AWS コンソールから使用できるわけではないため、プロセス全体をセットアップしている AWS CLI を使用します。 さまざまなアカウントから AWS Lambda を Amazon SNS で使用 - AWS Lambda

アカウントAでSNS Topicにクロスアカウント権限付与

SNS Topicには権限範囲を規定するポリシーを設定することができます。こちらの作業はGUIからの設定も可能です。

edit_topic_policy

念の為、初期状態のSNS Topicの権限を確認してみましょう。AWS:SourceOwnerの条件を見るとわかりますが、デフォルトでは、SNS Topicは同一AWSアカウントからの権限以外は許されていません。

$ aws sns get-topic-attributes \
  --profile account-a \
  --topic-arn arn:aws:sns:us-west-2:111111111111:TestTopic \
  --query Attributes.Policy \
  --output text | jq .
{
  "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",
        "SNS:Receive"
      ],
      "Resource": "arn:aws:sns:us-west-2:111111111111:TestTopic",
      "Condition": {
        "StringEquals": {
          "AWS:SourceOwner": "111111111111"
        }
      }
    }
  ]
}

ちなみに、AWS CLIの特定KeyのValueがJSON形式の場合には、--queryオプションで適切に対象を絞った上で、--output textオプションを使うと不要なエスケープ等が削除されて、そのまま扱えるJSONとして出力されます。改行が無いと読みづらい為、さらにここでは、jqコマンドで読みやすく改行されるようにしました。

では、手順に従って、こちらのSNS Topicの権限を修正してみます。

$ aws sns add-permission \
  --profile account-a \
  --topic-arn arn:aws:sns:us-west-2:111111111111:TestTopic \
  --label lambda-access \
  --aws-account-id 222222222222 \
  --action-name Subscribe ListSubscriptionsByTopic Receive

先ほどと比較してみましょう。このSNS Topicに対して、アカウントB(222222222222)にSubscribe等の一部操作の権限が追加されたことがわかると思います。

$ aws sns get-topic-attributes \
  --profile account-a \
  --topic-arn arn:aws:sns:us-west-2:111111111111:TestTopic \
  --query Attributes.Policy \
  --output text | jq .
{
  "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",
        "SNS:Receive"
      ],
      "Resource": "arn:aws:sns:us-west-2:111111111111:TestTopic",
      "Condition": {
        "StringEquals": {
          "AWS:SourceOwner": "111111111111"
        }
      }
    },
    {
      "Sid": "lambda-access",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::222222222222:root"
      },
      "Action": [
        "SNS:Subscribe",
        "SNS:ListSubscriptionsByTopic",
        "SNS:Receive"
      ],
      "Resource": "arn:aws:sns:us-west-2:111111111111:TestTopic"
    }
  ]
}

アカウントBでLambda Functionにクロスアカウント権限付与

続けてLambda側の権限設定です。虎塚さんの素晴らしいブログにも記載されていますが、先日のアップデートでAWS Management Consoleから、Lambda FunctionのInvoke権限を見れるようになっています。ただし、こちらは執筆時点では参照専用の機能である為、直接編集操作はできないようです。

Lambda Functionを作成しただけの初期状態では、何の権限、ポリシーも割り当てられていない状態でした。

lambda_function_policy_1

$ aws lambda get-policy \
  --profile account-b \
  --function-name test \
  --output text

An error occurred (ResourceNotFoundException) when calling the GetPolicy operation: The resource you requested does not exist.

それでは、手順に従ってクロスアカウントからのアクセス権限を追加してみます。

$ aws lambda add-permission \
  --profile account-b \
  --function-name test \
  --statement-id sns-x-account \
  --action "lambda:InvokeFunction" \
  --principal sns.amazonaws.com \
  --source-arn arn:aws:sns:us-west-2:111111111111:TestTopic
{
    "Statement": "{\"Sid\":\"sns-x-account\",\"Resource\":\"arn:aws:lambda:us-west-2:222222222222:function:test\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"sns.amazonaws.com\"},\"Action\":[\"lambda:InvokeFunction\"],\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:sns:us-west-2:111111111111:TestTopic\"}}}"
}

このLambda Functionに対して、アカウントA(111111111111)のSNS TopicからInvokeFunctionする権限が追加されたことがわかると思います。

lambda_function_policy_2

$ aws lambda get-policy \
  --profile account-b \
  --function-name test \
  --output text | jq .
{
  "Version": "2012-10-17",
  "Id": "default",
  "Statement": [
    {
      "Sid": "sns-x-account",
      "Effect": "Allow",
      "Principal": {
        "Service": "sns.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:us-west-2:222222222222:function:test",
      "Condition": {
        "ArnLike": {
          "AWS:SourceArn": "arn:aws:sns:us-west-2:111111111111:TestTopic"
        }
      }
    }
  ]
}

ちなみに、SNS Topic側のポリシーが修正されていることが前提にはなりますが、このLambdaへの権限追加の操作をGUIからもっと簡単に設定する方法がわかりました。

Add Triggerから、設定できるSNS Topicは通常同一アカウント内のものなのですが、これをTopic ARNで直接指定することで、設定できることを確認しました。

lambda_add_trigger_1

問題なく設定できているようです。

lambda_add_trigger_2

InvokeFunctionの権限を付与する、という設定は、Trigger設定をすることと同値であることがわかりました。

アカウントBでLambda FunctionをアカウントAのSNS TopicにSubscribe

ここまでできれば、もう終わったも同然なのですが、最後に1つ注意があります。Subscribeするのは、アカウントB(222222222222)から設定するということです。

create_subscription

AWS CLIから設定する場合も同様です。

$ aws sns subscribe \
  --profile account-b \
  --topic-arn arn:aws:sns:us-west-2:111111111111:TestTopic \
  --protocol lambda \
  --notification-endpoint arn:aws:lambda:us-west-2:222222222222:function:test
{
    "SubscriptionArn": "arn:aws:sns:us-west-2:111111111111:TestTopic:29c7b08a-69c1-4ebb-af25-3be8bebf7e2c"
}

設定した後であれば、アカウントA(111111111111)のTopicからSubscribeされたことを確認、削除することは可能ですが、Subscribe設定はLambda FunctionのOwner側で行うというところを間違えないようにしましょう。

topic_detail

まとめ

SNS Topicのポリシーは理解していたのですが、LambdaのFunctionポリシーがちょっと理解しづらいと感じました。複数のAWSアカウントをまとめて操作する際には、クロスアカウントでの権限設定が必須となりますので、理解して設定できるようにしておくと良いですね。

どこかの誰かのお役に立てば嬉しいです。