今回の課題
以下の前回の記事の機能を実装する際に、権限まわりでエラーが発生してしまったので解決した方法を記録する。
また、一応は解決できたが、解決できた理由がイマイチ理解できていなかったため、色々調査することにした。
発生した問題を解決する
前提
使用している権限
- Lambdaには
practice-Lambda-RDStoS3-role-idais11p
というロールで権限が渡されている。- S3にアクセスや操作をできるようにするための権限(
s3:GetObject
やs3:DeleteObject
など)を持ったポリシーをアタッチしている。 - こちらのロールの信頼されたエンティティは以下となっている。(Lambda実行時に
AssumeRole
によって、ロールが所持しているポリシーの権限をLambdaが使える)
- S3にアクセスや操作をできるようにするための権限(
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
使用しているLambdaのコード
前回の記事から引用した、下記のLambdaコードを実行する。
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」でなければ、20秒間待機して、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
)のポリシーを持たせたロールのこと。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::************:role/practice-s3OperationsRole"
}
]
}
以上で、発生した問題を無事解決することができたのだが、
どうして解決することができたのかしっかりできなかった...。
本題:なぜ解決できたのか理解する。
どうして上記のような対応で解決できたのか腑に落ちなかったため調査してみた。
理解できなかった点
以下の点が、理由が理解できなかった。
前提
に記載の通り、LambdaからS3へのアクセスや操作をするための権限はLambdaに対して、既に付与しているにも関わらず、
StartExportTaskを実行するには、iam:PassRole
でS3にアクセスするための権限をpractice-Lambda-RDStoS3-role-idais11p
ロールに対してパスするように
というエラーが発生した。
知識の勉強
まずは基礎知識をしっかり学んだ。
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コードの
分かったこと
試行錯誤をしてみて以下のことがわかった。
-
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
でロールを渡す必要があるのは、
Lambdaのコード内でS3を操作するための権限を持つロールのARN自体を使用してS3を操作する場合だということが分かった。
Lambdaに対して、S3を操作するための権限をIAMポリシーで渡す必要があるのは、
Lambda内でdelete()
などのコードを使ってS3を操作する場合だということが分かった。