【AWS S3】大量のオブジェクトが保存された複数のS3バケットをシェルで一括削除

# これはなに？
大量にあるS3バケットの削除方法についてのメモです。
自分の例だとS3バケットが100個近くあり、オブジェクトも600万オブジェクト(12TB)くらい、その時の消す策として参考になればと思います。

## 事前準備
### S3バケットへの全ての書き込みをバケットポリシーで拒否するようにします。
```json:policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ALLWtite",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::バケット名/*"
    }
  ]
}
```
・下記のライフサイクル設定後などにも、システムが動いていて書き込みが行われ続けるとS3バケットの削除が行えないため、書き込みを全て拒否します。
### S3ライフサイクルを設定して1日後に全てのオブジェクトが消えるようにします。
```rule.json
{
  "Rules": [
    {
      "Filter": {
        "Prefix": ""
      },
      "Status": "Enabled",
      "Expiration": {
        "Days": 1
      },
      "NoncurrentVersionExpiration": {
        "NoncurrentDays": 1
      },
      "AbortIncompleteMultipartUpload": {
        "DaysAfterInitiation": 1
      },
      "ID": "DeleteObject"
    }
  ]
}
```
### バージョニングの設定を無効化する
・今回、後述しますがCLIでS3バケットを削除するので下記に該当するためバージョニングの無効化を行います。
> バージョニングが有効な場合は、AWS CLI を使用して S3 バケットを削除できません。
https://aws.amazon.com/jp/premiumsupport/knowledge-center/delete-bucket-s3-console-cli/

### ポイント
・S3ライフサイクルでオブジェクトを消す理由としてはコンソールから**[空にする]**などで削除すると、凄く時間がかかります。
・AWSの[ドキュメント](https://aws.amazon.com/jp/premiumsupport/knowledge-center/s3-empty-bucket-lifecycle-rule/)にも記載があるのですが、期限失効してから実際にオブジェクトがバケットから削除されるには**数日のラグ**がある場合があるので、削除に必要な期間を1日とせず数日バッファを持っておいた方が得策かと思います。

> 削除対象としてマークされたオブジェクトに対する料金は請求されなくなります。ただし、ルールが実行されてバケットが空になるまでに数日かかる場合があります。これは、オブジェクトバージョンを失効させることと、削除マーカーをクリーンアップすることが非同期ステップであるためです。


### 上記、設定をするスクリプト
```
#!/bin/bash -x
#使い方：bash bucket-update.sh バケット名_リージョン

target_info=(${1//_/ })

bucket_name=${target_info[0]}
bucket_arn="arn:aws:s3:::${bucket_name}/*"
region=${target_info[1]}

echo "START"

#バケットポリシー作成
cat policy.json| jq '.Statement[].Resource|="'${bucket_arn}'"' > tmp && mv tmp policy.json
echo "Policy内容"
cat policy.json

#バケットポリシー書き換え
aws s3api put-bucket-policy --bucket ${bucket_name} --policy file://policy.json --region ${region}
echo "書き換え後Policy確認"
aws s3api get-bucket-policy --bucket ${bucket_name} --output text --region ${region} | jq .

#ライフサイクル設定
aws s3api put-bucket-lifecycle-configuration --bucket ${bucket_name} --lifecycle-configuration file://rule.json --region ${region}
echo "設定後ライフサイクル確認"
aws s3api get-bucket-lifecycle --bucket ${bucket_name} --region ${region}

#バージョニング無効設定
aws s3api put-bucket-versioning --bucket ${bucket_name} --versioning-configuration Status=Suspended --region ${region}
echo "バージョニング無効化確認"
aws s3api get-bucket-versioning --bucket ${bucket_name} --region ${region}

echo "END"
```
## 削除
・オブジェクトをCLIで全て削除してから、コンソールでバケットを全て削除しようかと思いましたが、S3のコンソールでは複数選択できなかったので、CLIで実行することに。

### まず、S3バケット名が記載されたリストを作成します。
```
#!/bin/bash -x
#使い方：bash create-bucket-list.sh

BUCKETLIST=bucket.list

#####################################
#バケットリスト作成
#####################################
echo "バケットリスト作成開始"
aws s3 ls | awk '{print $3}' > $BUCKETLIST
echo "完了"

```
### 全削除
```
#!/bin/bash -x
#使い方：bash all-bucket-delete.sh

BUCKETLIST=./bucket.list

#####################################
#確認
#####################################
function ConfirmExecution() {
  echo "スクリプトを実行しますか？ y/n"
  read input

  if [ -z $input ] ; then
    ConfirmExecution
  elif [ $input = 'y' ] ; then
    echo "スクリプトを実行します。"
  else
    echo "スクリプトを終了します。"
    exit 1
  fi
}

ConfirmExecution
#####################################
#バケット削除
#####################################

echo "バケット削除開始"
for line in `cat ${BUCKETLIST}`
do
  aws s3 rb s3://${line}
done
echo "完了"

#####################################
#バケット削除確認
#####################################
echo "バケット削除確認"
aws s3 ls | awk '{print $3}'
echo "完了(何も表示されなければOK)"
```

・間違って実行してしまうと怖いので一応、保険で確認するスクリプトを埋め込んでます。
・S3バケット削除のコマンドで<span style="color: #d61b09">--force</span>を今回は付けてないので、空のバケット以外は一応消えないかと。

## まとめ
・実際に消すとなると色々と考慮すべき点などあるので参考になれば幸いです。