カテゴリー
インフラ プログラミング

任意のホストからAWS SDKを使ってS3へファイルをアップロードする

はじめに

AWS上に構築したシステムに、システム外の外部ホストからファイルをアップロードするような業務はよくあると思います。やっていることは単純なのですが、ちょっとした手順になりますのでまとめておきます。なお、今回は AWS SDK for Python (boto3) を使ったプログラムを紹介します。

バケットの準備

外部ホストからのアップロードファイルを受け取るバケットを作成します。

アップロードユーザーの作成

今回、s3botosampleuser というユーザーを作成して、これを外部ホスト向けに割り当てることにしましょう。AWS マネジメントコンソールのホームで「Identify & Access Management」(IAM) を探してクリックします。

図1. IAMの選択

左側メニューで「ユーザー」をクリックして、ボタン「新規ユーザーの追加」をクリックします。

図2. 新規ユーザーの作成

ユーザー名として「s3botosampleuser」を入力して、ボタン「作成」をクリックします。

図3. ユーザー名の入力

ユーザーが作成されると、このユーザー向けの「アクセスキーID」と「シークレットアクセスキー」が発行されます。「ユーザーのセキュリティ認証情報を表示」をクリックすると表示されますので、控えておきます。

続いて、バケットにアクセス権限を追加するときに、ユーザー「s3botosampleuser」のARN (Amazon Resource Name) も必要になりますのでここで確認しておきます。この画面を閉じて、ユーザー一覧の画面に戻ります。

図4. クレデンシャルのダウンロード

ユーザー「s3botosampleuser」をクリックします。

図5. 作成したユーザーの選択

画面右上に表示される「概要」から、「ユーザーのARN」を控えておきます。

図6. 作成したユーザーのARNの確認

これで、ユーザーの作成は完了です。

バケットの作成とアクセス許可設定

s3botosample というバケットを用意して、ここにアップロードしてもらうようにしましょう。AWS マネジメントコンソールのホームで「S3」を探してクリックします。S3 のバケット一覧画面が表示されますので、ボタン「バケットを作成」をクリックします。

図7. バケット作成の開始

バケット名に「s3botosample」を、リージョンは適当なものをそれぞれ入力して、ボタン「作成」をクリックします。

図8. バケット名とリージョンの入力

バケット一覧に「s3botosample」が追加されます。引き続き、アクセス許可設定を進めます。「s3botosample」をクリックしてください。

図9. 作成したバケットの選択

画面右上のボタン「プロパティ」をクリックしてください。バケット「s3botosample」についての情報が表示されます。セクション「アクセス許可」にある「バケットポリシーの追加」をクリックしてください。

図10. バケットポリシーの設定開始

今回はポリシージェネレーターを使って、GUIでポリシー情報を生成してみます。「AWS Policy Generator」をクリックしてください。

図11. ポリシーの作成開始

別ウィンドウでポリシージェネレーターが表示されます。まず、バケットの中身の一覧を表示できるように、アクション「ListBucket」を許可します。下表のとおり入力して、ボタン「Add Statement」をクリックしてください。

項目
Select Type of PolicyS3 BucketPolicy
EffectAllow
Principal先ほど控えたユーザーのARN
AWS ServiceAmazon S3
ActionsListBucket
Amazon Resource Name (ARN)arn:aws:s3:::s3botosample
表1. ListBucketの詳細
図12. ListBucketの許可

一度画面が下までスクロールして、生成されるポリシー情報が1件分表形式で表示されます。引き続き、バケットにファイルをアップロードできるようにアクション「PutObject」を許可したいので、画面を上までスクロールして2つ目のポリシー情報を入力します。下表のとおり入力して、ボタン「Add Statement」をクリックしてください。

項目
Select Type of PolicyS3 BucketPolicy
EffectAllow
Principal先ほど控えたユーザーのARN
AWS ServiceAmazon S3
ActionsPutObject
Amazon Resource Name (ARN)arn:aws:s3:::s3botosample/*
表2. PubObjectの詳細

最後の項目「Amazon Resource Name (ARN)」の末尾に注意してください。
 アクション「ListBucket」はバケット自身に対するポリシーなので末尾に「/」が付きませんが、アクション「PutObject」はパケットの中身であるObject(ファイルやディレクトリ)に対するポリシーとなりますので、末尾に「/*」が必要になります。

図13. PutObjectの許可

2つのアクションについてポリシー情報を作成できたら、ボタン「Fenerate Policy」をクリックしてください。

図14. 構成したポリシーの生成

先ほど入力した2つのポリシー情報がJSONベースのテキストに変換されて表示されますので、これをコピーします。S3 バケットの詳細情報の画面(ポリシージェネレーターを表示する前の画面)に戻り、この内容を貼り付けて保存してください。

図15. ポリシーの適用

下記は生成されたJSONベースのテキストのサンプルです。

{
    "Version": "2012-10-17",
    "Id": "PolicyXXXXXXXXXXXXX",
    "Statement": [
        {
            "Sid": "StmtXXXXXXXXXXXXX",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::XXXXXXXXXXXX:user/s3botosampleuser"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::s3botosample"
        },
        {
            "Sid": "StmtXXXXXXXXXXXXX",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::XXXXXXXXXXXX:user/s3botosampleuser"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::s3botosample/*"
        }
    ]
}

これでバケットの作成と、アクセス許可設定が完了しました。

アップロード元の環境整備

今回、ファイルのアップロードには AWS SDK for Python (boto3) を使います。botoCentOS7 + Python3.5 の環境でも動作しました。

(venv35)$ pip install boto3

アクセスキーのインストール

控えておいた、ユーザー s3botosample 向けの「アクセスキーID」と「シークレットアクセスキー」をホストにインストールします。boto3 をインポートすると自動的に適用されるため、Pythonプログラム中に「アクセスキーID」と「シークレットアクセスキー」を記述する必要がなくなります。

(venv35)$ mkdir -p ~/.aws

~/.aws/credentials

[default]
aws_access_key_id = <アクセスキーID>
aws_secret_access_key = <シークレットアクセスキー>

「アクセスキーID」と「シークレットアクセスキー」はダブルクオートなどで囲まず、そのまま記述します。

アップロードプログラムの作成と実行

単純にするため、カレントディレクトリにプログラムファイルを作成します。同じくカレントディレクトリにファイル「sample.txt」があり、これをアップロードするようなプログラムを作成しました。

今回、ファイルの一覧取得(ListBucket)とファイルのアップロード(PutObject)のみ許可するポリシーを適用しています。ファイルをダウンロードできないこと、ファイルを削除できないことも確認しました。
 【リファレンス】S3 — Boto 3 Docs 1.3.1 documentation

s3botosample.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import boto3

s3 = boto3.resource('s3')
client = s3.Bucket('s3botosample')

print('01. show list objects in bucket.')
for obj in client.objects.limit(10):
    print(obj.Object)

print('02. upload file.')
#client.upload_file('sample.txt', 'sample/sample-uploaded.txt')
with open('sample.txt', 'rb') as f:
    result = client.put_object(Key='sample/sample-uploaded.txt', Body=f)
print(result)

print('03. show list objects in bucket again.')
for obj in client.objects.limit(10):
    print(obj.Object)

print('04. try to download file.')
try:
    client.download_file('sample/sample-uploaded.txt', 'sample-downloaded.txt')
except Exception as e:
    print(e)

print('05. try to delete file.')
delete_query = {
    'Objects': [{
            'Key': 'sample/sample-uploaded.txt'
        }]
}
response = client.delete_objects(Delete=delete_query)
print(response)

このプログラムではS3向けの「ハイレベルクライアント」を使用しています。より細かい操作が可能な「ローレベルクライアント」は client = boto3.client(‘s3’) とすると利用できるようになります。

ファイルのアップロードには client.upload_file(‘sample.txt’, ‘sample/sample-uploaded.txt’) と記述しても構わないのですが、成功時にレスポンスがないため、client.put_object(Key=’sample/sample-uploaded.txt’, Body=f) を使用しています。

S3のバケットでは、実はファイルやディレクトリという概念はなく、すべてオブジェクトとして扱われます。このプログラムではバケット直下にsampleというディレクトリのようなものをmkdirなどせずに指定してアップロードしていますが、S3のバケットからしてみればオブジェクト「sample/sample-uploaded.txt」が来るんだな、という感じです。

アップロード実行

作成したプログラムを実行すると、下記のような結果になりました。

(venv35)$ python s3botosample.py
01. show list objects in bucket.
02. upload file.
s3.Object(bucket_name='s3botosample', key='sample/sample-uploaded.txt')
03. show list objects in bucket again.
.create_resource of s3.ObjectSummary(bucket_name='s3botosample', key='sample/sample-uploaded.txt')>
04. try to download file.
An error occurred (403) when calling the HeadObject operation: Forbidden
05. try to delete file.
{'Errors': [{'Key': 'sample/sample-uploaded.txt', 'Message': 'Access Denied', 'Code': 'AccessDenied'}], 'ResponseMetadata': {'RequestId': 'F09B8B2FC7FE5F8C', 'HostId': '5cI109DInfkOSCm+R6ewEF2IfHZp7m/+FMGKOKDlASI3mzza25tJIuQLJLOqXXJzXdXpAOPPKXw=', 'HTTPStatusCode': 200}}

01. ではバケットにはまだ何もないので出力はありません。
 02. でオブジェクトが帰ってきており、ファイルのアップロードに成功しています。
 03. ではいまアップロードしたファイルがオブジェクトとして取得できています。
 04. ダウンロードを許可していないため、403エラー(権限なしエラー)になっています。
 05. 削除も許可していないため、こちらはエラーレスポンスがオブジェクトとして帰ってきています。

おわりに

AWS SDKを使うと、AWSの外にあるホストでも簡単にファイルを転送できます。バッチ処理結果の共有場所、バックアップアーカイブの保存場所、あるいはCMSのパブリッシュ結果のデプロイ先にしたWebサイトの運用など、いろいろな用途に使えそうですね!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください