【AWS】AWSアカウントをまたいでS3 Bucket間コピーする（実践編）【クロスアカウント】

# はじめに
下記の記事で説明した構成パターンについて、一部、複雑なものがあります。
この記事は、当該構成の具体的な設定内容・挙動を説明することを目的とします。

【AWS】AWSアカウントをまたいでS3 Bucket間コピーする（解説編）【クロスアカウント】
https://qiita.com/tmiki/items/cef01fa5d34c55dbbeae


# 想定構成
- ファイルコピー元AWSアカウント
    - AWSアカウントID: 111111111111111
    - S3 Bucket: inter-bucket-copy-src
    - IAM User: src-user
    - IAM Role: src-role
- ファイルコピー先AWSアカウント
    - AWSアカウントID: 999999999999999
    - S3 Bucket: inter-bucket-copy-dst
    - IAM User: dst-user
    - IAM Role: dst-role


# 実践
## Type2: コピー元S3 BucketにBucket Policyを付与する
下記と同等の内容です。
https://qiita.com/tmiki/items/cef01fa5d34c55dbbeae#type2-%E3%82%B3%E3%83%94%E3%83%BC%E5%85%83s3-bucket%E3%81%ABbucket-policy%E3%82%92%E4%BB%98%E4%B8%8E%E3%81%99%E3%82%8B%E3%82%88%E3%82%8A%E8%A4%87%E9%9B%91

まずは構成をおさらいしましょう。
![type2.PNG](https://qiita-image-store.s3.amazonaws.com/0/137777/0fd31b1c-98bc-b3ab-2349-f1b086ca20d3.png)


### ファイルコピー元AWSアカウントのAWSリソース設定
#### S3 Bucket: inter-bucket-copy-src
Bucket Policyは以下のようになります。
dst-roleの権限でコピー操作を行うこととなりますので、当該Roleからの参照権限が必要になります。

```json:BucketPolicy
{
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "SourceBucketPolicy-1",
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::999999999999999:role/dst-role"
        },
        "Action": [
            "s3:ListBucket",
            "s3:GetObject"
        ],
        "Resource": [
            "arn:aws:s3:::inter-bucket-copy-src",
            "arn:aws:s3:::inter-bucket-copy-src/*"
        ]
    }
}
```

#### IAM User: arn:aws:iam::111111111111111:user/src-user
Permissionsは以下のように、AssumeRoleを実行する権限さえあれば十分です。
対象のResourceには、具体的なRole名を指定することもできますが、「*」を指定した方が管理しやすいと思います。

```json:permissions
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```

#### IAM Role: arn:aws:iam::111111111111111:role/src-role
※このケースでは利用しません


### ファイルコピー先AWSアカウントのAWSリソース設定
#### S3 Bucket: inter-bucket-copy-dst
※Bucket Policyは不要

#### IAM Role: arn:aws:iam::999999999999999:role/dst-role
PermissionsとTrust Relationshipsは以下のようになります。

- Permissions

コピー元S3 Bucketへの参照権限、およびコピー先S3 Bucketへの書込権限が全て必要です。

```json:permissions
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::inter-bucket-copy-src",
        "arn:aws:s3:::inter-bucket-copy-src/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::inter-bucket-copy-dst/*"
      ]
    }
  ]
}
```

- Trust Relationships

src-userにAssumeRoleさせるための設定を追加します。

```json:TrustRelationships
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111111:user/src-user"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```


### 手順
手順を確認していきます。

1. src-userは、dst-roleをAssumeRoleするようAPIを発行する。
2. temporary credentialsを取得する。temporary credentialsはdst-roleの権限でAWS APIを発行できる。
3. temporary credentialsを利用してPUT Object - Copyを発行する。
4. inter-bucket-copy-src上のファイルをinter-bucket-copy-dstにコピーする。

以下から、具体的なコマンド、AWS APIのレスポンス等を詳しく見ていきます。

#### 1. src-userは、dst-roleをAssumeRoleするようAPIを発行する。
項番1,2は併せて説明します。

#### 2. temporary credentialsを取得する。temporary credentialsはdst-roleの権限でAWS APIを発行できる。
まず、適当なLinuxマシンを用意し、AWS CLIをインストールします。
src-userのAWS Credentialsを準備し、AWS CLIにセットします。

```bash
$ aws configure
AWS Access Key ID [None]: AKIA****************
AWS Secret Access Key [None]: duOb********************************n6mM
Default region name [None]: ap-northeast-1
Default output format [None]: json
```

AssumeRoleを実行します。オプション「--role-session-name」の値は必須ですが、任意の文字列を指定することができます。
「AccessKeyId」「SecretAccessKey」「SesionToken」という値が返却されたことが分かります。

```bash
$ aws sts assume-role --role-arn "arn:aws:iam::999999999999999:role/dst-role" --role-session-name "you-can-put-any-string"
{
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAXXXXXXXXXXXXXXXXX:you-can-put-any-string",
        "Arn": "arn:aws:sts::999999999999999:assumed-role/dst-role/you-can-put-any-string"
    },
    "Credentials": {
        "SecretAccessKey": "0hq4**********************************",
        "SessionToken": "FQoD************************************************************************************************************************************************************************************************************",
        "Expiration": "2018-04-29T12:57:34Z",
        "AccessKeyId": "ASIAXXXXXXXXXXXXXXXX"
    }
}
```

#### 3. temporary credentialsを利用してPUT Object - Copyを発行する。
項番3,4は併せて説明します。

#### 4. inter-bucket-copy-src上のファイルがinter-bucket-copy-dstにコピーされる。
まず、AssumeRoleの結果、返却された値は下記の通りでした。

|Item Name|Value|
|---|---|
|AccessKeyId|ASIAXXXXXXXXXXXXXXXX|
|SecretAccessKey|0hq4**********************************|
|SesionToken|FQoD************************************************************************************************************************************************************************************************************|

この値を環境変数にセットすることで、AWS CLIから利用できるようになります。

```bash
$ export AWS_ACCESS_KEY_ID="ASIAXXXXXXXXXXXXXXXX"
$ export AWS_SECRET_ACCESS_KEY="0hq4**********************************"
$ export AWS_SESSION_TOKEN="FQoD************************************************************************************************************************************************************************************************************"
```

GetCallerIdentityを実行することで、現在のユーザを確認することができます。

```bash
$ aws sts get-caller-identity
{
    "Account": "999999999999999",
    "UserId": "AROAXXXXXXXXXXXXXXXXX:you-can-put-any-string",
    "Arn": "arn:aws:sts::999999999999999:assumed-role/dst-role/you-can-put-any-string"
}
```

ここまでくれば、後は簡単です。
単純に、AWS CLIでS3オブジェクトのコピーを行うだけです。

```bash
$ aws s3 sync s3://inter-bucket-copy-src/any-dir/ s3://inter-bucket-copy-dst/any-dir/
```


## Type2.1: コピー元S3 BucketにBucket Policyを付与し、IAM Role for EC2 instancesを併用する
上記の構成において、AssumeRoleする主体がIAM Userではなく、EC2 Instance Profileになるケースです。

![type2.1.PNG](https://qiita-image-store.s3.amazonaws.com/0/137777/8d22fc15-c107-f542-1a29-c4ba006c0c56.png)

### ファイルコピー元AWSアカウントのAWSリソース設定
#### S3 Bucket: inter-bucket-copy-src
Bucket Policyは以下のようになります。
dst-roleの権限でコピー操作を行うこととなりますので、当該Roleからの参照権限が必要になります。

```json:BucketPolicy
{
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "SourceBucketPolicy-1",
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::999999999999999:role/dst-role"
        },
        "Action": [
            "s3:ListBucket",
            "s3:GetObject"
        ],
        "Resource": [
            "arn:aws:s3:::inter-bucket-copy-src",
            "arn:aws:s3:::inter-bucket-copy-src/*"
        ]
    }
}
```

#### IAM User: arn:aws:iam::111111111111111:user/src-user
※このケースでは利用しません


#### IAM Role: arn:aws:iam::111111111111111:role/src-role
PermissionsとTrust Relationshipsは以下のようになります。

- Permissions

Permissionsは以下のように、AssumeRoleを実行する権限さえあれば十分です。

```json:permissions
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```


- Trust Relationships

EC2 Instance Profileとして利用するための設定が必要です。これは通常、よく利用しているものですね。

```json:TrustRelationships
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

### ファイルコピー先AWSアカウントのAWSリソース設定
#### S3 Bucket: inter-bucket-copy-dst
※Bucket Policyは不要

#### IAM Role: arn:aws:iam::999999999999999:role/dst-role
PermissionsとTrust Relationshipsは以下のようになります。

- Permissions

コピー元S3 Bucketへの参照権限、およびコピー先S3 Bucketへの書込権限が全て必要です。

```json:permissions
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::inter-bucket-copy-src",
        "arn:aws:s3:::inter-bucket-copy-src/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::inter-bucket-copy-dst/*"
      ]
    }
  ]
}
```

- Trust Relationships

src-roleにAssumeRoleさせるための設定を追加します。

```json:TrustRelationships
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111111:role/src-role"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```


### 手順
手順を確認していきます。
1. EC2インスタンスにsshログインし、当該EC2インスタンスに紐づくRole（src-role）のTemporary Credentialsを取得する
2. src-roleの権限で、dst-roleをAssumeRoleするようAPIを発行する。
3. dst-roleに紐づくtemporary credentialsを取得する。このtemporary credentialsはdst-roleの権限でAWS APIを発行できる。
4. dst-roleのtemporary credentialsを利用してPUT Object - Copyを発行する。
5. inter-bucket-copy-src上のファイルがinter-bucket-copy-dstにコピーされる。

以下から、具体的なコマンド、AWS APIのレスポンス等を詳しく見ていきます。

#### 1. EC2インスタンスにsshログインし、当該EC2インスタンスに紐づくRole（src-role）のTemporary Credentialsを取得する
対象EC2インスタンスにsshログインします。
下記のドキュメントに従い、Temporary Credentialsを取得します。

IAM Roles for Amazon EC2 - Amazon Elastic Compute Cloud -> Retrieving Security Credentials from Instance Metadata
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html


```bash
$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/src-role
{
  "Code" : "Success",
  "LastUpdated" : "2012-04-26T16:39:16Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAIOSFODNN7EXAMPLE",
  "SecretAccessKey" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
  "Token" : "nczJ************************************************toKenExaMpLe",
  "Expiration" : "2017-05-17T15:09:54Z"
}
```


この値を環境変数にセットすることで、AWS CLIから利用できるようになります。

```bash
$ export AWS_ACCESS_KEY_ID="ASIAIOSFODNN7EXAMPLE"
$ export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
$ export AWS_SESSION_TOKEN="nczJ************************************************toKenExaMpLe"
```

GetCallerIdentityを実行することで、現在のユーザを確認することができます。

```bash
$ aws sts get-caller-identity
{
    "Account": "111111111111111",
    "UserId": "AROA*************QIOE:i-0123456789abcdeff",
    "Arn": "arn:aws:sts::111111111111111:assumed-role/src-role/i-0123456789abcdeff"
}
```


#### 2. src-roleの権限で、dst-roleをAssumeRoleするようAPIを発行する。
項番2,3は併せて説明します。

#### 3. dst-roleに紐づくtemporary credentialsを取得する。このtemporary credentialsはdst-roleの権限でAWS APIを発行できる。
これ以降の操作は、先ほどのType2のケースと同一です。

AssumeRoleを実行します。オプション「--role-session-name」の値は必須ですが、任意の文字列を指定することができます。
「AccessKeyId」「SecretAccessKey」「SesionToken」という値が返却されたことが分かります。

```bash
$ aws sts assume-role --role-arn "arn:aws:iam::999999999999999:role/dst-role" --role-session-name "you-can-put-any-string"
{
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAXXXXXXXXXXXXXXXXX:you-can-put-any-string",
        "Arn": "arn:aws:sts::999999999999999:assumed-role/dst-role/you-can-put-any-string"
    },
    "Credentials": {
        "SecretAccessKey": "0hq4**********************************",
        "SessionToken": "FQoD************************************************************************************************************************************************************************************************************",
        "Expiration": "2018-04-29T12:57:34Z",
        "AccessKeyId": "ASIAXXXXXXXXXXXXXXXX"
    }
}
```

#### 4. dst-roleのtemporary credentialsを利用してPUT Object - Copyを発行する。
項番4,5は併せて説明します。

#### 5. inter-bucket-copy-src上のファイルがinter-bucket-copy-dstにコピーされる。

AssumeRoleの結果、返却された値は下記の通りでした。

|Item Name|Value|
|---|---|
|AccessKeyId|ASIAXXXXXXXXXXXXXXXX|
|SecretAccessKey|0hq4**********************************|
|SesionToken|FQoD************************************************************************************************************************************************************************************************************|

この値を環境変数にセットすることで、AWS CLIから利用できるようになります。

```bash
$ export AWS_ACCESS_KEY_ID="ASIAXXXXXXXXXXXXXXXX"
$ export AWS_SECRET_ACCESS_KEY="0hq4**********************************"
$ export AWS_SESSION_TOKEN="FQoD************************************************************************************************************************************************************************************************************"
```

GetCallerIdentityを実行することで、現在のユーザを確認することができます。

```bash
$ aws sts get-caller-identity
{
    "Account": "999999999999999",
    "UserId": "AROAXXXXXXXXXXXXXXXXX:you-can-put-any-string",
    "Arn": "arn:aws:sts::999999999999999:assumed-role/dst-role/you-can-put-any-string"
}
```

ここまでくれば、後は簡単です。
単純に、AWS CLIでS3オブジェクトのコピーを行うだけです。

```bash
$ aws s3 sync s3://inter-bucket-copy-src/any-dir/ s3://inter-bucket-copy-dst/any-dir/
```

# おわりに
このケースは一見複雑そうにみえますが、一つ一つの仕組みを確認し、理解していくと、それほど難しいものでないことが分かります。
理解を深めていくにつれ、IAM/STSの仕組みのシンプルさに驚嘆し、またよく考えられていることを実感できると思います。