別アカウントからS3にアップロードされたファイルのACLを変更する

# はじめに
過去に「[別アカウントからS3にアップロードされたファイルの所有者を変更する](https://qiita.com/handy-dd18/items/61150784c3cf608288b3)」という記事を書きましたが、今回はアップロードしたファイルのACLをアクセスコントロールポリシーを使用して変更する方法について試してみました。


# 手順の前に
## ACL(アクセスコントロールリスト)とは
まずACLとは何かといいますと、S3で設定可能なアクセス管理機能の一つで、S3バケットポリシーと同じくS3バケットに設定が可能になっています。

ACLを使用することで別のAWSアカウントやAWSアカウントを持つすべてのユーザー等の単位で読み取りや書き込みなどのアクセスを許可することができます。

多くの場合、S3バケットを保有するAWSアカウントとは別のAWSアカウントからファイルをアップロードされる際にアクセス許可を与えるのに使用されます。

それ以外の細かい制御はIAMとバケットポリシーで行うようにAWSドキュメントにも記載があります。


## ACLの種類
ACLには2つ種類があり、1つがS3バケットに紐づくACLで、もう1つがオブジェクトに紐づくACLです。
2つのACLで異なる設定がされていた場合はオブジェクトのACLが優先されるようになってます。

・S3バケット単位ACL
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/ed558fea-f1f0-3d79-51bf-b1b1d426757e.png)

・オブジェクト単位ACL
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/715edb63-f652-49a2-4d75-4e85ef3eee11.png)


## ACLが設定されるタイミング
S3バケットのACLはS3バケットを作成したタイミングで設定されます。
パブリックアクセスを拒否した環境だとデフォルトでバケット所有者アカウントのみアクセスが許可されています。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/c278f974-c5c1-03a1-dfc1-c81b2c816db9.png)

オブジェクトのACLはアップロード時に特に指定しなければS3バケットのACLと同じものが設定されます。
ただし、S3バケットのACLに別のAWSアカウントのアクセス許可がされていたとしても、それはオブジェクトのACLには反映されません。

その場合は手動でオブジェクトのACLに設定する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/ee2e0dcc-89b7-3972-2240-441718d8c5e5.png)

CLIも準備されているので、手動で設定する場合はコマンドで設定したほうが楽だと思います。
・[put-object-acl](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object-acl.html)


# 実行手順
## 事前準備
環境は前回の流用ですが、最低限以下は既に整っていることとします。
・ファイルアップロードするAWSアカウントとS3のあるAWSアカウントは別
・アップロード元AWSアカウント上でEC2を準備
・EC2にアタッチされたIAMロールの権限を使用する
・S3バケットのポリシーでIAMロールを許可
・アップロード用のサンプルファイルはEC2上に作成済み


## ファイルアップロード
EC2から以下のCLIでファイルをアップロードします。

```
$ aws s3 cp .\test1.txt s3://test-tmp-20210611/test/
```

アップロードされたファイルをS3コンソールからみるとアクセス許可がないためエラーが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/aa05ec11-4b8b-be55-f6c7-4047e34b907c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/7afe95cc-0cc0-e712-b3f4-bd3e120b779c.png)

以下のコマンドでアップロードしたファイルに設定されたオブジェクトACLを確認してみます。

```
$ aws s3api get-object-acl --bucket test-tmp-20210611 --key test/test1.txt
```

諸事情でモザイクかけてますが、アップロード元のAWSアカウント情報が設定されたACLとなっているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/2d560b4e-176d-ab81-3da9-e12d23fc45ba.png)

以下のコマンドでアップロード先のS3バケットACLも確認してみましょう。

```
aws s3api get-bucket-acl --bucket test-tmp-20210611
```

モザイクかけてますがIDの頭文字が異なることから別のAWSアカウントの情報が設定されたACLであることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/1a10b872-2678-bf3d-c3bf-11646895bdf4.png)


## ACL変更
ACLが異なることが確認できたところでオブジェクトACLを変更していきます。
変更にはAWS CLIの[put-object-acl](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object-acl.html)を使用します。

そのコマンドには「--access-control-policy」というオプションがあり、JSON形式でACLを指定することで対象のオブジェクトに指定のACLで更新することができます。

JSON例は以下です。
先ほどコマンドで確認したACLがそのまま使えるので、それをベースに必要な設定だけを追加します。

```
{
  "Grants": [
    {
      "Grantee": {
        "DisplayName": "string",
        "EmailAddress": "string",
        "ID": "string",
        "Type": "CanonicalUser"|"AmazonCustomerByEmail"|"Group",
        "URI": "string"
      },
      "Permission": "FULL_CONTROL"|"WRITE"|"WRITE_ACP"|"READ"|"READ_ACP"
    }
    ...
  ],
  "Owner": {
    "DisplayName": "string",
    "ID": "string"
  }
}
```

作成したJSONがこちらです。
Grantsのパラメータにアップロード先のAWSアカウントの名前を正規IDを追加しました。

```s3bucket_acl.json
{
    "Owner": {
        "DisplayName": "アップロード元AWSアカウント",
        "ID": "アップロード元AWSアカウントID"
    },
    "Grants": [
        {
            "Grantee": {
                "DisplayName": "アップロード元AWSアカウント",
                "ID": "アップロード元AWSアカウントID",
                "Type": "CanonicalUser"
            },
            "Permission": "FULL_CONTROL"
        },
        {
            "Grantee": {
                "DisplayName": "アップロード先AWSアカウント",
                "ID": "アップロード先AWSアカウントID",
                "Type": "CanonicalUser"
            },
            "Permission": "FULL_CONTROL"
        }
    ]
}
```

この時の注意点としては、Ownerをアップロード元のAWSアカウントと一致させることです。
どうやらPutObjectACLリクエストを実行するAWSアカウントとACLのOwnerは一致している必要があるようです。
それが異なる場合は403エラーによりコマンドが失敗します。

JSONファイルが作成できたらEC2で以下のコマンドを実行して、オブジェクトACLを変更します。

```
$ aws s3api put-object-acl --bucket test-tmp-20210611 --key test/test1.txt --access-control-policy file://s3bucket_acl.json
```

## ACL変更確認
S3コンソールから確認してみます。

最初はエラーでACL自体が見れませんでしたが、更新したことでオブジェクトACLに外部アカウントが追加されています。
この外部アカウントがアップロード先AWSアカウントのものなので、コンソールからも見えるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/b61e5002-7dd3-6803-dc6b-4e4eec9bf194.png)

## ちなみに
JSONのOwnerをアップロード先AWSアカウントに変更して、同じようにEC2からコマンドを実行してみた結果がこちらです。
「403 Access Denied」が出ていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/96284f28-8eff-5a78-b179-64b41cd224c7.png)

どうしてもアクセスコントロールポリシーで設定したいという要件がないのであれば、以下の「--acl bucket-owner-full-control」オプションをつけて実行すると同じ結果になるので、こちらの方が簡単で済むと思います。

```
$ aws s3api put-object-acl --bucket test-tmp-20210611 --key test/test2.txt --acl bucket-owner-full-control
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/dfe20516-7913-79af-83ea-972f1feddaab.png)


# 終わりに
S3のACLについてよく理解していなかったので調べるついでに試してみました。
複数のAWSアカウントが関わってくると調べるだけだとわかりづらいので、やっぱり手を動かして確認するのが一番ですね。