AWS:S3のクロスアカウントアクセスを試してみよう

2022/8/27

(最終更新: 2022/8/27

アイキャッチ

はじめに

同じ社内の複数システムでファイルをやりとりしたい場合があると思います。その際、双方AWSであればS3のクロスアカウントアクセスが利用できます。

バケットポリシーを設定することで実現してみます。

構成図 構成図

オンプレのシステムは、別のシステムにファイル連携したいときはネットワーク接続をする必要がありましたが、AWSは簡単でいいですね。

後編はこちら。クロスアカウントのS3からイベントを受け取り、自身のLambda関数を発火させてみます。→AWS:クロスアカウントのS3イベントをSNSで検知する | エンジニアを目指す日常ブログ

前提

  • AWSの2つのアカウント(アカウントA、アカウントB)が作成できていること。
  • AWSのマネジメントコンソールに、2つのアカウントでそれぞれログインできること。

下準備:S3バケットとLambda関数の作成

(アカウントA)S3バケット作成

アカウントA(データを作る側のシステム)でS3バケットを作成します。

バケットを作成します

社内データを模擬しているので、当然パブリックアクセスはブロックします。

パブリックアクセスはブロックします

(アカウントB)Lambda関数作成

アカウントB(データを使わせてもらう側のシステム)で、S3にアクセスする処理を書くためのLambda関数を準備します。 もちろんLambdaでなくても良いです。

今回はPythonで作ります。

Lambda関数

今回、Lambda関数のみ、うっかり東京リージョンではなく、バージニア北部リージョンに作成してしまいました。。東京リージョンでも同じようにできると思います。

練習用のS3バケット作成

次の章で、練習としてアカウントBのLambda関数からアカウントBのS3バケットにアクセスするので、アカウントB側にも適当にS3バケットを作っておきます。

練習①:Lambda関数から自分のアカウントのS3バケットにアクセスする

(アカウントB)LambdaのIAMロールにS3アクセス権限追加

設定⇒アクセス権限から、「実行ロール」に設定されているロールをクリックします。

実行ロールを編集

今回は細かい設定はせず、S3へのフルアクセス権限を付けます。 JSONを直接書いても良いですし、ビジュアルエディターから設定しても良いです。

S3フルアクセス権限

IAMロールにもともとアタッチしていたポリシーに対して、以下の部分(S3に関するオブジェクト)を追加できればOKです。

IAMPolicy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:us-east-1:<アカウントID>:*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:us-east-1:<アカウントID>:log-group:/aws/lambda/<Lambda関数名>:*"
        },
        {            "Sid": "VisualEditor2",            "Effect": "Allow",            "Action": "s3:*",            "Resource": "*"        }    ]
}

(アカウントB)Lambdaから自分のアカウントのS3にアクセス

S3バケットの中身を1件取得するLambda関数

Lambda関数の中身を書きます。

Lambda関数

PythonでAWSのリソースを触るためにはboto3というモジュールを利用します。

import boto3
import json

def lambda_handler(event, context):
    print("--------")
    print(event)

    client = boto3.client('s3')

    obj = client.list_objects(Bucket='<バケット名>')

    print("--------")
    print(obj['Contents'][0])
    print("--------")

(参考)

取得できるobjの中身は以下のようになっています。(固有情報は--------に書き換えてます) 上記のソースコードでは、Contentsの1件目を取ってきています。

{
    ResponseMetadata: {
        RequestId: "----------",
        HostId: "----------",
        HTTPStatusCode: 200,
        HTTPHeaders: {
            "x-amz-id-2":
                "----------",
            "x-amz-request-id": "----------",
            date: "Sun, 28 Aug 2022 02:57:37 GMT",
            "x-amz-bucket-region": "ap-northeast-1",
            "content-type": "application/xml",
            "transfer-encoding": "chunked",
            server: "AmazonS3",
        },
        RetryAttempts: 1,
    },
    IsTruncated: False,
    Marker: "",
    Contents: [
        {
            Key: "folder01/",←フォルダ
            LastModified: datetime.datetime(2022, 8, 27, 12, 2, 30, (tzinfo = tzlocal())),
            ETag: '"----------"',
            Size: 0,
            StorageClass: "STANDARD",
            Owner: {
                DisplayName: "----------",
                ID: "----------",
            },
        },
        {
            Key: "folder01/test.txt",←ファイルのパス
            LastModified: datetime.datetime(2022, 8, 27, 12, 2, 39, (tzinfo = tzlocal())),
            ETag: '"----------"',
            Size: 10,
            StorageClass: "STANDARD",
            Owner: {
                DisplayName: "----------",
                ID: "----------",
            },
        },
        {
            省略
        }
    ],
    Name: "<バケットの名前>",
    Prefix: "",
    MaxKeys: 1000,
    EncodingType: "url",
};

デプロイ

「Deploy」をクリックしデプロイ。

テスト実行

「Test」をクリックして実行します。テスト内容は適当でよいです。

Contentsの中身が取れたことがわかります。 Contentsの中身が取れたことがわかります

練習①は終わり。

練習②:別のアカウント(アカウントA)のS3にアクセス

次に、そのまま別アカウントのS3にアクセスしようとどうなるか試してみます。結果は当然アクセスNGになります。

(アカウントB)Lambda関数を書き換える

Lambda関数でバケット名を指定している部分を、

アカウントA(データを作る側のシステム)のバケットに変更します。

obj = client.list_objects(Bucket='<バケット名>')

(アカウントB)アクセス拒否された

デプロイしてからテストを実行すると、アクセスが拒否されたことがわかります。

※たまに前回の結果が残ってしまうので、画面更新をしてからTestを押してください。

アクセス拒否のログ

{
  "errorMessage": "An error occurred (AccessDenied) when calling the ListObjects operation: Access Denied",
  "errorType": "ClientError",
  "requestId": "xxxx",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 19, in lambda_handler\n    obj = client.list_objects(Bucket='<バケット名>')\n",
    "  File \"/var/runtime/botocore/client.py\", line 391, in _api_call\n    return self._make_api_call(operation_name, kwargs)\n",
    "  File \"/var/runtime/botocore/client.py\", line 719, in _make_api_call\n    raise error_class(parsed_response, operation_name)\n"
  ]
}

本番:別アカウントのS3にアクセスする

(アカウントA)バケットポリシーを変更

アカウントA(データを作る側のシステム)のS3バケットの設定を変更します。

バケット→「アクセス許可」タブ→「バケットポリシー」→編集を選びます。

バケットポリシー」→編集

以下の記載を追加します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "GetObject",
            "Effect": "Allow",
            "Principal": {                "AWS": "<<アカウントBのLambdaに設定したIAMロール(arn:aws:iam::~~)>>"            },            "Action": "s3:*",
            "Resource": [                "arn:aws:s3:::<<バケット名>>",                "arn:aws:s3:::<<バケット名>>/*"            ]        }
    ]
}

アカウントBのLambdaに設定したIAMロールarn:aws:iam::~~)」は、IAM設定画面から確認することができます。

IAM設定画面

(参考)https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/example-walkthroughs-managing-access-example4.html

(アカウントB)Lambda関数から再度アクセス

今度は情報を取得することができました。

ログ

おわりに

Lambdaを使って違うアカウントのS3にアクセスすることができました。

後編:S3にファイルが置かれたことを通知する

アカウントA(データを作る側のシステム)のS3にファイルがおかれたときに、新しいファイルを使った処理をアカウントB(データを使わせてもらう側のシステム)が実行したい場合があります。

そこで次回は、アカウントAからアカウントBに、ファイルを置いたことを教えてあげて、アカウントBの処理が発火する仕組みを考えたいと思います。

AWS:クロスアカウントのS3イベントをSNSで検知する

AWS:クロスアカウントのS3イベントをSNSで検知する

AWSで、ほかのアカウントに対してもS3でデータ提供することが可能です。AWS初心者が試してみました。

https://bunsugi.com/s3-cross-acount-sns-lambda

参考文献



個別連絡はこちらへ→Twitterお問い合わせ

プロフィール

プロフィールイメージ

はち子

事業会社のシステム部門で働きはじめて5年目の会社員。システム企画/要件定義/システムアーキテクチャ等。

Twitter→@bun_sugi

過去の記事について

はてなブログに掲載の記事(主にプログラミングメモ)についてはこちらに掲載しております。(本ブログに移行中)

タグ一覧

関連記事

Copyright© 2023, エンジニアを目指す日常ブログ

お問い合わせ|プライバシーポリシー