Lambdaのリソースポリシーについて

## AWS Lambdaのリソースポリシー
Lambdaの権限設定にはリソースポリシー・IAMポリシーの２つがあります。

リソースポリシーは、簡単に言えばLambdaを呼び出す側に対するパーミッションを与えます。
また、IAMポリシーはLambdaに対してリソースを呼び出し操作するためのパーミッションを与えるものです。

両者の違いや、AWS CLIを利用したポリシーの設定などに関してはClassMethodさんの「[AWS LambdaのIAMポリシーとリソースポリシーを理解しよう](https://dev.classmethod.jp/cloud/aws/policies-for-lambda/)」という記事が非常にわかりやすいです。

ちなみに、2019年5月23日現在のマネジメントコンソールでは、
<img width="1446" alt="スクリーンショット 2019-05-23 23.46.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223534/5ffa0c13-5dbf-bdb5-5842-01ef443fe100.png">
Designerの鍵マークのアイコンをクリックすると関数のポリシーを閲覧することができます。
変更はAWS CLIから行う必要があります（つまりマネジメントコンソールの"関数のポリシー"は参照用の機能となっている）。

AWS CLI AddPermisionの公式リファレンス（英語）は[こちら](https://docs.aws.amazon.com/cli/latest/reference/lambda/add-permission.html)です。

本稿は前者、リソースポリシーに関する具体的な話です。


## 概要
本稿で取り上げるケースは以下の３つです。

+ CloudWatch EventsからLambda関数をトリガーする場合
+ ALB(Application Load Balancer)からLambda関数をトリガーする場合
+ SNS(Simple Notification Service)を利用してクロスアカウントでLambdaをトリガーする場合

### 1. CloudWatch EventsからLambda関数をトリガーする場合

Lambda関数をスケジュールされたイベントによって実行するようなケースでは、Cloud Watch Eventsが主に用いられるかと思います。

特定のLambda関数をトリガーするCloudWatch Eventsの設定方法は主に以下のようなものが考えられます。

+ Lambdaマネジメントコンソールでトリガーを設定する。
+ CloudWatchマネジメントコンソールで新規ルールを作成し、そのターゲットとしてLambda関数を指定する。
+ CloudFormation（SAM)によるデプロイ

このうち、CloudFormationによるデプロイ時には明示的にリソースポリシーを作成しなければいけません。

```yaml
  # 明示的にリソースポリシーを定義する
  LambdaEventPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: !Ref Lambda
      Principal: 'events.amazonaws.com'
      SourceArn: !GetAtt Rule.Arn
```

また、すでにCloudWatchマネジメントコンソールででルールを作成したLambdaのエイリアスを削除し、同名で作り直した場合などもAWS CLIを利用してAddPermission APIをコールして再設定をしたり、マネジメントコンソール上でルールの更新や再作成を行う必要があります。


### 2. ALB(Application Load Balancer)からLambda関数をトリガーする場合

ALBはパスベースのルーティングによってLambda関数をトリガーすることが可能ですが、ターゲットとなるLambda関数のリソースポリシーではALBからのInvokeを許可する必要があります。

ALBからLambdaを呼び出すようにしているケースで、ログに"Access Denied"などと出力されてトリガーされていない場合はLambda関数においてリソースベースのポリシーが設定されていない可能性があります。

公式リファレンス「[Lambda関数の準備](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/lambda-functions.html#prepare-lambda-function)」では、

```sh
aws lambda add-permission \
--function-name lambda-function-arn-with-alias-name \ 
--statement-id elb1 \
--principal elasticloadbalancing.amazonaws.com \
--action lambda:InvokeFunction \
--source-arn target-group-arn
```
上記のようなコマンドによる設定方法が紹介されています。

構成管理としては、JSONファイルで設定を保存しておき、`--cli-input-json`オプションを利用することをお勧めします。
→2019/7/20更新: 現在はCLoudFormationにて定義可能です。

また、同リファレンスにもあるようにLambda関数名にエイリアスを指定することで特定のバージョンを指定することも可能ですが、こちらもエイリアスを同名で再作成した場合などは再設定が必要となることに注意が必要です。

### 3. SNS(Simple Notification Service)を利用してクロスアカウントでLambdaをトリガーする場合
SNSを利用すると、クロスアカウント間でのLambdaトリガーが可能です。

例えば、アカウントAのLambda関数Aにおいて処理結果をSNSによって特定のトピックに対してPublishします。
そのサブスクリプションのエンドポイントをアカウントBのLambda関数Bとして指定し、アカウントB側においてそのサブスクリプションの確認（Confirm）を行います。

さて、同一アカウントにおいてこのような連携を行った際はリソースポリシーに特定の記述をする必要はないのですが、クロスアカウントでは設定を直接してあげる必要があります。

```sh
$ aws lambda add-permission \
  --function-name Lambda関数B \
  --statement-id SID(sns-x-accountなど) \
  --action "lambda:InvokeFunction" \
  --principal sns.amazonaws.com \
  --source-arn arn:aws:sns:[regeon]:[アカウントAのID]:[トピック名]
```

詳しくは、ClassMethodさんの[クロスアカウントなLambdaをSNS TopicにSubscribeする](https://dev.classmethod.jp/cloud/aws/crossaccount-lambda-subscribing-sns-topic/)がわかりやすいです。