【データ基盤構築/AWS】IAMのPassRoleで権限を渡すこととロールにポリシーアタッチして権限を渡すことの違い

# 今回の課題
以下の前回の記事の機能を実装する際に、権限まわりでエラーが発生してしまったので解決した方法を記録する。
また、一応は解決できたが、解決できた理由がイマイチ理解できていなかったため、色々調査することにした。

https://qiita.com/Ayumu-y/items/8e2b0ee51a8bdb78df02

## 発生した問題を解決する

### 前提
#### 使用している権限
* Lambdaには`practice-Lambda-RDStoS3-role-idais11p`というロールで権限が渡されている。
    * S3にアクセスや操作をできるようにするための権限(`s3:GetObject`や`s3:DeleteObject`など)を持ったポリシーをアタッチしている。
    * こちらのロールの信頼されたエンティティは以下となっている。(Lambda実行時に`AssumeRole`によって、ロールが所持しているポリシーの権限をLambdaが使える)
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```


#### 使用しているLambdaのコード
前回の記事から引用した、下記のLambdaコードを実行する。
```python
import json
import boto3
import time
from botocore.client import ClientError
from datetime import datetime

rds = boto3.client('rds')
s3 = boto3.resource('s3')

S3_BUCKET_NAME="s3bucket" # 出力先のS3バケット名
S3_OBJECT_NAME="s3object" #出力先のS3オブジェクト
IAM_ROLE_ARN="arn:aws:iam::************:role/s3OperationsRole" # S3にエクスポートする際に使用するロールのARN
KMS_KEY_ID="arn:aws:kms:ap-northeast-1:************:key/31b0e6b4-54a8-4b32-97c3-98e3ff4412be" # 作成したKMSキーのARN


# create_snapshot()を呼び出せば、[prefix-YYYY-mm-dd-HH-MM]というスナップショットの作成が始まるように定義    
def create_snapshot(prefix, instanceid):
    newsnapshotid = "-".join([prefix, datetime.now().strftime("%Y-%m-%d-%H-%M")])
    rds.create_db_snapshot(
        DBSnapshotIdentifier=newsnapshotid,
        DBInstanceIdentifier=instanceid
    )
    
# check_snapshot_created()を呼び出すと、RDSのStatusが「available」になるまで待機する関数
# lambda_handler()にて、create_snapshot()と同時に呼び出すことで、スナップショットを作成可能な状態まで待ってスナップショット作成の処理が走るように定義している。
def check_snapshot_created(prefix, instanceid):
    snapshots = rds.describe_db_snapshots(DBInstanceIdentifier=instanceid)['DBSnapshots']
    # RDSのStatusが「available」でなければ、２０秒間待機して、check_snapshot_createdをもう一度呼び出すということを繰り返す。
    for snapshot in snapshots:
        if snapshot['Status'] != "available":
            time.sleep(20)
            check_snapshot_created(prefix, instanceid)
            
def export_snapshot(prefix, instanceid):
    # rdsのスナップショットの情報を`snapshots`に入れる
    snapshots = rds.describe_db_snapshots(DBInstanceIdentifier=instanceid, SnapshotType='manual')['DBSnapshots']
    # snapshotが新しい順にソートして、`snapshots`に入れる
    snapshots = sorted(snapshots, key=lambda x: x['SnapshotCreateTime'], reverse=True)
    # 一番新しいスナップショットを`snapshot`に入れる
    snapshot = snapshots[0]
    newsnapshotid = "-".join([prefix, datetime.now().strftime("%Y-%m-%d-%H-%M")])

    # 一番新しいスナップショットをS3にエクスポートする
    response = rds.start_export_task(
        ExportTaskIdentifier=newsnapshotid,
        SourceArn=snapshot['DBSnapshotArn'],
        S3BucketName=S3_BUCKET_NAME,
        S3Prefix=S3_OBJECT_NAME,
        IamRoleArn=IAM_ROLE_ARN,
        KmsKeyId=KMS_KEY_ID,
        )
    return(response)
    
            
def lambda_handler(event, context):
    bucket = S3.Bucket("s3bucket")
    snapshot_prefix = 'mysnapshot'
    instance = 's3object'

    # スナップショットを作成
    create_snapshot(snapshot_prefix, instance)
    # スナップショットが作成可能な状態になるまで待機
    check_snapshot_created(snapshot_prefix, instance)
    
    # s3のオブジェクトを削除
    bucket.objects.filter(Prefix=S3_OBJECT_NAME).delete()
    export_snapshot(snapshot_prefix, instance)
```

## 発生したエラーの内容
Lambdaを実行すると、以下のエラーが発生してしまう。
```
"errorMessage": "An error occurred (AccessDenied) when calling the StartExportTask operation: User: arn:aws:sts::************:assumed-role/practice-Lambda-RDStoS3-role-idais11p/practice-lambda-PracticeLambda-R5dbYPSxzWpG
is not authorized to perform: iam:PassRole on resource: arn:aws:iam::334616138474:role/yamaguchi-practice-s3OperationsRole because no identity-based policy allows the iam:PassRole action",
```
StartExportTaskを実行しようとしたが、
`arn:aws:iam::334616138474:role/yamaguchi-practice-s3OperationsRole`に`iam:PassRole`権限がないため、StartExportTaskが実行できなかったとのこと。

## 解決策
エラーに記載されている通り、
`practice-Lambda-RDStoS3-role-idais11p`ロールに、
以下の権限を付与したポリシーを持たせることで、エラーを解決することができた。

※以下の`arn:aws:iam::************:role/practice-s3OperationsRole`というのは、S3を操作するための権限(`s3:GetObject`や`s3:DeleteObject`)のポリシーを持たせたロールのこと。
```json
{
    "Version": "2012-10-17",
    "Statement": [
            {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::************:role/practice-s3OperationsRole"
            }
    ]
}
```

以上で、発生した問題を無事解決することができたのだが、
どうして解決することができたのかしっかりできなかった...。


# 本題：なぜ解決できたのか理解する。
どうして上記のような対応で解決できたのか腑に落ちなかったため調査してみた。



### 理解できなかった点
以下の点が、理由が理解できなかった。

`前提`に記載の通り、LambdaからS3へのアクセスや操作をするための権限はLambdaに対して、既に付与しているにも関わらず、
<font color="red">StartExportTaskを実行するには、`iam:PassRole`でS3にアクセスするための権限を`practice-Lambda-RDStoS3-role-idais11p`ロールに対してパスするように</font>
というエラーが発生した。


## 知識の勉強
まずは基礎知識をしっかり学んだ。
### sts:AssumeRoleについて
IAMロールを引き受けるためのアクションのこと。

信頼されたエンティティにて`AssumeRole`を使うことで、
`Resource`に定義しているAWSサービス実行時に、ロールにアタッチしてあるポリシーの権限を一時的に引き受けることができるようになる。

例えば、この記事の一番上に記載の`信頼されたエンティティ`でいうと、
上記の`信頼されたエンティティ`が定義されたロールが所持しているポリシーの権限がLambdaを実行するときに、使用することができるようになるイメージ。

### iam:PassRoleについて
AWSリソースにIAMロールをパスして、IAMロールの使用を許可する権限。

AWSリソースは、他のAWSサービスと連携するためにIAMロールを必要とする。
`iam:PassRole`権限を持つユーザーやロールは、Lambda関数などのAWSサービスに必要なIAMロールを引き渡すことができる。




## 試行錯誤して分かったこと
基礎知識を学習してもイマイチ腑に落ちないので、
LambdaやIAMを操作してみることにした。

### 試行錯誤したこと
* `practice-Lambda-RDStoS3-role-idais11p`ロールから、S3を操作するための権限ポリシーを除去して、Lambdaのコードを実行してみた。
    * すると、Lambdaコードの`bucket.objects.filter(Prefix=S3_OBJECT_NAME).delete()`の部分でエラーが発生した。
    * 一方StartExportTask関数の処理は動いて、スナップショットの作成は行われていた。

### 分かったこと
試行錯誤をしてみて以下のことがわかった。
* Lambdaの`delete()`などのコードでS3を操作実行するときは、
`practice-Lambda-RDStoS3-role-idais11p`ロールに`s3:DeleteObject`などの、S3バケットを操作するためのポリシーを持たせる必要がある。

* `StartExportTask`を実行するときは、
StartExportTask内のパラメータで`arn:aws:iam::************:role/practice-s3OperationsRole`ロールのARN自体を使用する必要がある。
そのため、こちらの場合はS3を操作するためのポリシーをロールアタッチするのではなく、`iam:PassRole`で`arn:aws:iam::************:role/practice-s3OperationsRole`ロール自体をパスして使えるようにする必要があった。


# まとめ
Lambdaに対して、S3を操作するための権限を`iam:PassRole`でロールを渡す必要があるのは、
<font color="red">Lambdaのコード内でS3を操作するための権限を持つロールのARN自体を使用してS3を操作する場合</font>だということが分かった。

Lambdaに対して、S3を操作するための権限をIAMポリシーで渡す必要があるのは、
<font color="red">Lambda内で`delete()`などのコードを使ってS3を操作する場合</font>だということが分かった。