レイヤーを含むLambda関数からAmazon S3バケットにアクセスしようとして難儀した話


つい最近Lambda関数の実装をしていまして、その中で困ったことと解決したことがあったので、自分への備忘も兼ねて共有します。

一応エチケットシートとして。この記事は投稿時点(2021/6/20時点)において検証した結果に基づいており、今後Amazon Web ServicesのBoto3ライブラリの仕様が変更された場合には、必ずしもこの検証通りの結果にならない可能性があることだけ、ご了承ください。

やろうとしていたこと

  • Amazon S3バケットからS3.PutObjectを行う処理のみを、Lambda関数のレイヤーとして切り出す
  • 上記で作成したレイヤーをimportしているビジネスロジックの部分をLambda関数上で実装する
  • レイヤー、Lambda関数ともにPython3.8で実装
  • Lambda関数を実行すると、S3バケットにファイルが格納される

ちなみにレイヤーのコードはざっくりこんな感じです。もうほとんどBoto3のドキュメントのお作法そのまんまです。

import boto3

def put_object(fileObject, bucketName, filePrefix, Key):

    if filePrefix != '':
        key = filePrefix + Key

    s3 = boto3.resource('s3')
    object = s3.Object(bucketName, Key)

    response = object.put(
        Body=fileObject,
        ContentEncoding='utf-8',
        ContentType='text/plain'
    )

困ったこと

このレイヤーを含むLambda関数をリリースして、同じAWSアカウント内にS3バケットを作成。ブロックパブリックアクセスは有効にしています。

ブロックパブリックアクセスを有効にしている場合でも、デフォルトでは特に作成したS3バケットへのファイルの格納は普通にできる、と思っていたのですが、いざLambda関数を実行してみると、ファイルが格納されないわけです。で、デバッグのためにCloudWatch Logsの該当ロググループを確認すると、

botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

お決まりのAccess Deniedが出力されているわけで。

危ないと知りながらも、一旦S3バケットのブロックパブリックアクセスを無効にしてみたり、それでも解決しないので、再度ブロックパブリックアクセスを有効にして、Lambdaの実行ロールに対してPutObjectとPutObjectACLを有効にしてあげるバケットポリシーを書いてみたりしてもどうにも解決せず、途方に暮れていました。

解決方法

鍵はバケットポリシーの書き方に違いない! というところまではなんとなく勘がついていたので、試しに以下のようにバケットポリシーを書き換えてあげたら、普通に該当するS3バケットにファイルが格納されるようになったではないですか!

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:sts::(AWSアカウント):assumed-role/(レイヤー名)",
                    "arn:aws:iam::(AWSアカウント):role/(Lambda関数の実行ロール)",
                    "arn:aws:sts::(AWSアカウント):assumed-role/(Lambda関数の実行ロール)/(Lambda関数名)"
                ]
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::(S3バケット名)/*"
        }
    ]
}

どうも、同一アカウント内でS3バケットに対する操作を行う場合でも、Lambda関数とレイヤーからの操作を受け入れるためのassumed roleを明示的に書かないと、S3バケットに対する操作ができないようです。

今回の構成では、Amazon S3向けのVPC Endpointは実装していないので、Lambda関数からS3へのアクセスの通信が、一旦インターネットに出ていっているが故に、明示的にこういったポリシーの設定をしてあげないとダメなのかもしれません。まだVPC Endpointを設定していないので、断定的なことは言えないのですが。

まぁとにかくやりたいことは実現できたのですが、Amazon S3とLambda関数の関係は、まだまだ勉強しなければいけないですね。。。日々精進です。

カテゴリー: AWS タグ: , , , パーマリンク