IAMユーザやLambdaからS3バケットにアクセスする方法

## はじめに
自分や他人のAWSアカウントが所有するS3バケットに対して、IAMユーザーやLambdaへのアクセス権限の付け方に少しハマったため、やり方を整理してメモとして残す。

## 今回整理した内容

１．**アカウントA**のS3バケットに、**アカウントA**で作成したIAMユーザがアクセスする方法（AWSコンソールを用いて）

２．**アカウントB**のS3バケットに、**アカウントA**で作成したIAMユーザがアクセスする方法（AWSコンソールを用いて）

３．**アカウントB**のS3バケットに、**アカウントA**のS3バケットにあるファイルを、Lambdaを実行してコピーする方法

４．**アカウントA**のS3バケットに、**アカウントA**で作成したIAMユーザがアクセスする方法（ローカルPCからAWS CLIを用いて）

※ここで言うアカウントは、AWSアカウントを指している。


## １．**アカウントA**のS3バケットに、**アカウントA**で作成したIAMユーザがアクセスする方法（AWSコンソールから）

### ■事前準備

**アカウントAのS3バケットに以下の様なバケットを作成したとする。（バケット名は世界全体でユニークな必要があるので、そこは重複しない様に任意な名前で作成）**

　・s3-test-bucket-00001
　・s3-test-bucket-00002

**アカウントA内でIAMユーザーを作成する（ポリシーのアタッチは後でする）**

### ■作成したIAMユーザが各S3バケットにアクセスするための設定

IAMユーザーにバケットへのアクセス権限を付与する方法は大きく２つある。

　**① バケットポリシーに記載**
　**② IAMユーザーにアタッチするポリシーに記載**


### ① バケットポリシーに記載する方法

AWSコンソールからS3サービスを開き、対象のバケットを選択する。
『アクセス許可』を開き、【バケットポリシー】に以下の様な記述をする。（説明のためコメントを入れている）

```json:
{
    "Version": "2012-10-17",
    "Statement": [

        ↓ List表示するための、権限設定（AWS cliなどから直接ダウンロード・アップロードをする場合は不要かもしれない）
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::*****************************"   ← IAMユーザーのARN
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::s3-test-bucket-00001"   ← バケットのARN
        },

        ↓ 行為権限の設定       
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::*****************************"   ← IAMユーザーのARN
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::s3-test-bucket-00001/*"   ← バケットののARN
        }
    ]
}
```
※【パブリックアクセスをすべて ブロック ⇒ **ON**】 でもIAMユーザーから接続可能。

上記設定ができている状態で、アクセス権付与したIAMユーザーがAWSコンソールにログインすれば、S3-ObjectのURLにアクセスした際にバケット内を見れて、`Action`で設定された操作が可能になる。
※URLは以下の様なものになるはず。
　https://s3.console.aws.amazon.com/s3/buckets/【バケット名】?region=【リージョン名】&tab=objects
　分からなければ、対象バケットの『オブジェクト』画面を開き、アドレスバーのURLをコピーすれば良い。


### ② IAMユーザーにアタッチするポリシーに記載する方法

以下の様に、IAMにアタッチするポリシーを作成する。

```json:
{
    "Version": "2012-10-17",
    "Statement": [

        ↓ List表示するための権限設定
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": [
                "arn:aws:s3:::s3-test-bucket-00001",
                "arn:aws:s3:::s3-test-bucket-00002"
            ]
        },

        ↓ 行為権限の設定
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::s3-test-bucket-00001/*"
            ]
        }
    ]
}

```
該当のIAMユーザーがこのポリシーをアタッチすれば、先程と同じように閲覧や操作が可能になる。
（上記の場合だと、1と2のバケット内のオブジェクトの存在は見れるけど、2のバケットに対してはGETやPUTの権限はない）

バケットへのアクセスの仕方は先程と同じで、以下の様なURLにアクセスする。
https://s3.console.aws.amazon.com/s3/buckets/【バケット名】?region=【リージョン名】&tab=objects

#### 特定のIAMユーザーに対して、AWSアカウント内のフォルダを全て表示したい場合は、以下の様な`Statement`をポリシーに追加すれば良い。

```json:
{
    "Version": "2012-10-17",
    "Statement": [

        ↓ ここが追加部分
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "*"
        },

        ↓ ここを消してしまうと、バケット内に入った時に何も表示されなくなり、コンソール上のGUI操作ができなくなる。
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": [
                "arn:aws:s3:::s3-test-bucket-00001",
                "arn:aws:s3:::s3-test-bucket-00002"
            ]
        },

        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::s3-test-bucket-00001/*"
            ]
        }
    ]
}

```

## ２．**アカウントB**のS3バケットに、**アカウントA**で作成したIAMユーザがアクセスする方法（AWSコンソールを用いて）

これはIAMユーザーが属しているAWSアカウント内のS3ではないため、**クロスアカウントポリシー**というのが適用されることになる。簡単に言うと、アクセス制限の際に以下の **"両方"** を設定する必要がある。

　**① バケットポリシーに記載**（アカウントB側に設定する）
　**② IAMユーザーにアタッチするポリシーに記載**（アカウントA側に設定する）

具体的な設定としては、以下様に

```json:【アカウントB側のバケットポリシー】
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::**************************"    ← アカウントAのIAMユーザーのARN
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3::::**************************"    ← アカウントBのバケットのARN
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::**************************"    ← アカウントAのIAMユーザーのARN
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::**************************"    ← アカウントBのバケットのARN
        }
    ]
}
```

```json:【アカウントA側のIAMユーザーにアタッチするポリシー】
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::**************************"    ← アカウントBのバケットのARN
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::**************************"    ← アカウントBのバケットのARN
        }
   ]
}
```


## ３．**アカウントB**のS3バケットに、**アカウントA**のS3バケットにあるファイルを、Lambdaを実行してコピーする方法

Lambda関数からコピー先として、他人のS3バケットを指定したい時などは基本的なやり方は同じである。
Lambdaのアクセス権限に "実行ロール" として何かしらのロールが割り当てられていると思うが、そのロールにポリシーをアタッチしてあげればいい。（アタッチするポリシーは、②の【アカウントA側のIAMユーザーにアタッチするポリシー】と全く同じで問題ない。　※LambdaはアカウントA側で作成している前提。）　

しかし、ファイルを送られる側のS3バケットポリシーの中身は若干異なり、IAMユーザーのARNではなく、実行ロールのARNを指定する。　※LambdaのARNではないので注意！

```json:【アカウントB側のバケットポリシー】
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::**************************"    ← アカウントAのLambdaの実行ロールのARN
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3::::**************************"    ← アカウントBのバケットのARN
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::**************************"    ← アカウントAのLambdaの実行ロールのARN
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::**************************"    ← アカウントBのバケットのARN
        }
    ]
}
```


## ４．**アカウントA**のS3バケットに、**アカウントA**で作成したIAMユーザがアクセスする方法（ローカルPCからAWS CLIを用いて）

### ■ローカルPCの準備

まずはローカルPCにPythonを入れて、以下のコマンドより`awscli`と`boto3`をインストールする。

```
pip install awscli boto3`
```

`aws configure`を実行して、接続情報の設定をする。

```
aws configure

// 以下の様に入力を求められるので、アカウントAで作成したIAMユーザーの情報や、該当の
AWS Access Key ID :
AWS Secret Access Key :
Default region name :
Default output format :
```
※アクセスキーIDやシークレットアクセスキーが分からない場合は、こちらを参考に。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-quickstart.html


**以下のpythonファイルを作成する。（これを実行して、バケット内の【test.txt】を取得することを試してみる。）**

```py:
import boto3

s3 = boto3.resource('s3')

bucket = 's3-test-bucket-00001'
target_file = 'test.txt'    # バケット直下に入れたテストファイル名
to_path = '【ローカルPCの任意のパス（コピー後のファイル名含む）】'

s3.Bucket(bucket).download_file(target_file, to_path)
```

### ■IAMユーザーにアタッチするポリシーの記載中身

```json
{
    "Version": "2012-10-17",
    "Statement": [

        ↓ AWSコンソールからの操作を想定しないため、行為権限の設定ポリシーのみ
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:PutObjectAcl"
            ],
            "Resource":  "arn:aws:s3:::s3-test-bucket-00001/*",
        }
}

```
AWSコンソールからのアクセスではないが、同じAWSアカウント内のIAMユーザーとしてバケットにアクセスするため、バケットポリシーのみ記載だけで良いかと思ったが、エラーが出てしまうのでIAMユーザーにアタッチするポリシーも必要っぽい。

### ■バケットポリシーの記載中身
IPアドレスで接続許可をしているバケットポリシー。

```json:
{
    "Version": "2012-10-17",
    "Id": "SourceIP",
    "Statement": [
        {
            "Sid": "SourceIP",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::s3-test-bucket-00001/*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": "【接続を許可するIPアドレス範囲】"
                }
            }
        }
    ]
}
```

※AWSの公式ページにVPCエンドポイントやIPアドレスでアクセス制限をかけたい時のポリシーの書き方があるので、こちらを参考に。
　https://aws.amazon.com/jp/premiumsupport/knowledge-center/block-s3-traffic-vpc-ip/

**ポリシーの準備ができたら作成したPythonファイルを実行して、ちゃんと取得できていればＯＫ。**
**試しに許可するIPアドレスを変更したら、ちゃんと取得できなくなることも確認できた。**


### 以上、ポリシーを書きながら色々と試してみました！