AWS LambdaのPythonでS3のファイルを取得する

AWS LambdaのPythonでS3のファイルを取得する機会があったので、その手順をまとめました。
2021.01.31

AWS LambdaのPythonでS3のファイルを取得する機会があったので、その手順をまとめました。

はじめてLambdaを使ってみようと思うんだけど、どうやってLambdaを構築したらいいのかわからない! S3からのファイル取得のプログラムをどうやって書いたらいいのかわからない! と、いった方向けに、まず動かすことができるサンプルになると思います。

S3のファイルアップロード

ファイル取得先のS3バケットをあらかじめ構築しておきます。 今回は「python-get-object-XXXX」という名前で構築しています。 アップロードボタンを押してファイルをアップロードします。

次のような hello.json ファイルを作っておき、ファイルをドラッグしてS3へアップロードします。

hello.json

{
    "greeting": "Hello, World!"
}

アップロードが完了して、バケットにオブジェクトが表示できていれば問題ありません。

Lambdaの構築

Pythonを動かすLambdaを構築します。

Lambdaのマネジメントコンソールを開いて関数の作成ボタンをクリックします。

「一から作成」を選び、関数名を適当に入力してランタイムに「Python 3.8」を選択して、関数を作成します。

これでLambdaが構築できたので、Lamdbaの動作テストをしてみます。テストボタンをクリックします。

イベント名は適当に入力し、テストイベント内容は「{}(空のJSONファイル)」を設定して作成します。

テストイベントが作成できたら、「テスト」ボタンを押してテストを実行します。 次のような表示がされればテストは成功です。

LambdaでS3のファイルを取得する(できない)

とりあえずLambdaを動かすことができたので、次はboto3ライブラリを利用してS3のファイルを取得していきます。

プログラムを次のように書き換えてデプロイします。 バケット名はあらかじめ構築したS3のバケット名を入力してください。

import json
import boto3

# バケット名,オブジェクト名
BUCKET_NAME = 'python-get-object-XXXX'
OBJECT_KEY_NAME = 'hello.json'

s3 = boto3.resource('s3')

def lambda_handler(event, context):
    bucket = s3.Bucket(BUCKET_NAME)
    obj = bucket.Object(OBJECT_KEY_NAME)

    response = obj.get()    
    body = response['Body'].read()

    return json.loads(body.decode('utf-8'))

デプロイしてからテストをすると、次のようなエラーが発生してS3のファイル取得がうまくいきません。

これはLambdaがS3へアクセスする権限を持っていないため AccessDenied のエラーが発生しています。 よって、次はLambdaに対してS3へアクセスする権限をつけてあげます。

LambdaにS3の読み込み権限を付与する

Lambdaの実行権限はIAM Roleで管理されています。 アクセス権限タブの実行ロールで設定されているIAM Roleがわかります。 ここをクリックしてIAM Roleの編集をして、権限追加を行います。

このIAM Roleに対してポリシーをアタッチします。

今回はS3の読み込み権限を付与したいので AmazonS3ReadOnlyAccess のポリシーをアタッチします。

これでLambdaにS3の読み込み権限を付与できました。

LambdaでS3のファイルを取得する

S3のファイルを読み込む権限を付与したので、再度テストを実行してみます。 そうすると、次のように実行結果が表示され、S3の内容が取得できていることがわかります。

また、今回Resouce APIを利用してS3からファイルを取得しましたが、Client APIを利用した次のようなプログラムの書き方でも取得できます。

import json
import boto3

# バケット名,オブジェクト名
BUCKET_NAME = 'python-get-object-XXXX'
OBJECT_KEY_NAME = 'hello.json'

s3_client = boto3.client('s3')

def lambda_handler(event, context):
    response = s3_client.get_object(Bucket=BUCKET_NAME, Key=OBJECT_KEY_NAME)
    body = response['Body'].read()
    
    return json.loads(body.decode('utf-8'))

Resouce APIとClient APIの違いについては、次の弊社ブログで解説しています。 こちらも合わせて御覧ください。

終わりに

LambdaからS3のファイルを取得する始めの一歩としてサンプルになるように本ブログを書きました。

boto3ライブラリを利用してS3のオブジェクトをgetした際のレスポンスは、次のようにドキュメントに記載されています。

{
    'Body': StreamingBody(),
    'DeleteMarker': True|False,
    'AcceptRanges': 'string',
    'Expiration': 'string',
    'Restore': 'string',
    'LastModified': datetime(2015, 1, 1),
    'ContentLength': 123,
    'ETag': 'string',
    'MissingMeta': 123,
    'VersionId': 'string',
    'CacheControl': 'string',
    'ContentDisposition': 'string',
    'ContentEncoding': 'string',
    'ContentLanguage': 'string',
    'ContentRange': 'string',
    'ContentType': 'string',
    'Expires': datetime(2015, 1, 1),
    'WebsiteRedirectLocation': 'string',
    'ServerSideEncryption': 'AES256'|'aws:kms',
    'Metadata': {
        'string': 'string'
    },
    'SSECustomerAlgorithm': 'string',
    'SSECustomerKeyMD5': 'string',
    'SSEKMSKeyId': 'string',
    'BucketKeyEnabled': True|False,
    'StorageClass': 'STANDARD'|'REDUCED_REDUNDANCY'|'STANDARD_IA'|'ONEZONE_IA'|'INTELLIGENT_TIERING'|'GLACIER'|'DEEP_ARCHIVE'|'OUTPOSTS',
    'RequestCharged': 'requester',
    'ReplicationStatus': 'COMPLETE'|'PENDING'|'FAILED'|'REPLICA',
    'PartsCount': 123,
    'TagCount': 123,
    'ObjectLockMode': 'GOVERNANCE'|'COMPLIANCE',
    'ObjectLockRetainUntilDate': datetime(2015, 1, 1),
    'ObjectLockLegalHoldStatus': 'ON'|'OFF'
}

今回はS3に保存されているファイルの内容だけ取得したかったので Body だけ読み込んでいます。 こういったドキュメントを参照して、どの情報が必要かプログラム内で取捨選択したりして、いろいろ試してみてください。