AWSのLambdaとS3連携でハマる所



最近AWSのLambdaがお気に入りで、凝っています。

line-bot作ってみたり、PA-APIのプロキシ作ってみたりと、自分で超手軽にAPIを作れてしまうところがイイですね。

色々触ってみた結果、POSTされたデータをファイルとして保存したいぞと思ったんですが、S3というAWS上のストレージと連携させる必要があるようで、こいつがまた厄介だったので、忘れないようにメモ書きです。

LambdaからS3に保存するコードは超簡単

# ①ライブラリのimport
import boto3
from datetime import datetime
 
print('Loading function')      # ②Functionのロードをログに出力
 
s3 = boto3.resource('s3')      # ③S3オブジェクトを取得
 
# ④Lambdaのメイン関数
def lambda_handler(event, context):
    
    bucket = 'your_backet_name'    # ⑤バケット名を指定
    key = 'test_' + datetime.now().strftime('%Y-%m-%d-%H-%M-%S') + '.txt'  # ⑥オブジェクトのキー情報を指定
    file_contents = 'Lambda test'  # ⑦ファイルの内容
    
    obj = s3.Object(bucket,key)     # ⑧バケット名とパスを指定
    obj.put( Body=file_contents )   # ⑨バケットにファイルを出力
    return

コード自体はめっちゃ簡単ですね。
調べてすぐ出てくるレベルでNode.jsとかのコードもすぐ出てきました。

参考サイトは以下。

問題はAccessDenied

ちゃきちゃきっとコピペってとりあえずテストします。

API GateWayにトリガーしてとりあえずAnyでGetします。
Putなんですが、Getします。

Lambda execution failed with status 200 due to customer function error: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied.

AccessDeniedというエラーが出現して上手くいきません。よく見るやつですね。まぁなんかアレです。パーミッションとかそんな系の、アイツですね。

ソースコードはすぐ見つかるんですが、割とこの辺解説されてないことが多いので、どっちかというとこの方が困ります。

S3はアクセス権限とか色々ややこしい

調べてみるまでもなく、S3へのアクセス権がないであろう事は容易に想像できるエラーメッセージですが、問題はそれをどうやったら設定できるのか?ってことです。

っていうか、同じアカウントで作ったLambdaとS3の間に、何のアクセス権が必要なんでしょうか?

僕の設定が悪いだけで普通の人は勝手に連携されるのがディフォルトなんでしょうか?世知辛い世の中ですね。

バケットにアクセス権限を設定する

なんかS3では一番親のフォルダをバケットと呼ぶらしいです。フォルダって呼べよ。分かりにくい。せめてディレクトリとかやろ。

バケットポリシー

こんな感じです。Json形式で書き込むみたいなんですが、何書きゃいいのかサッパリわからないですね。

下の方にポリシージェネレータとか言う、ここに書き込むためのJsonを簡単に作れるヤツもあるんですが、それで作った結果、何故かエラーで進みませんでした。Principalがどうのこうの。

そろそろ嫌になってきましたね。
ていうことで、公式のドキュメントとか色々調べた結果、出来たポリシーがこちら。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Principal": {
                "AWS": "arn:aws:iam::xxxxxxxx:role/service-role/lambda-role-xxxxxx"
            },
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::AccountBBucketName/*"
        }
    ]
}

AccountBBucketName

この箇所はアクセス権限を与えたいバケット名を入れるところです。

arn:aws:iam::xxxxxxxx:role/service-role/lambda-role-xxxxx

この箇所はLambdaの実行ロールというやつです。ぶっちゃけあんまり意味は分かってませんが、AWSのサービスのパーツ毎に割り振られるIDみたいなもんですかね。

Lambdaの場合はこの辺から確認できます。

これでとりあえず、エラー回避できました。