📝

S3 バケットを空にして削除する Chrome 拡張機能を作ってみた

に公開

空ではない S3 バケットコンソールから削除する場合、以下の手順を実施する必要があります。

  1. S3 バケットを空にする
  2. S3 バケットを削除する

また、バケットポリシーでバケットの削除が禁止されている場合にはバケットポリシーも削除する必要があります。


コンソールからの操作だと 1 操作ずつ画面遷移が必要であるため、ワンクリックで削除する拡張機能を作ってみました。

1. Lambda 関数の作成

以下の設定で作成しました。

  • ランタイム: Python 3.13
  • IAM ロール: AdministratorAccess 権限を付与
  • コード: 以下の通り
import boto3
import json

s3 = boto3.client('s3')

def lambda_handler(event, context):
    print("Received event:", json.dumps(event))
    
    try:
        body = json.loads(event.get("body", "{}"))
    except Exception:
        body = {}
    
    bucket_name = body.get("bucket_name")
    if not bucket_name:
        return {
            'statusCode': 400,
            'body': json.dumps({'error': 'bucket_name is required'})
        }

    bucket_name = bucket_name.strip()

    try:
        delete_all_objects(bucket_name)
        delete_bucket_policy(bucket_name)
        s3.delete_bucket(Bucket=bucket_name)

        return {
            'statusCode': 200,
            'body': json.dumps({'message': f'{bucket_name} deleted successfully'})
        }
    except Exception as e:
        return {
            'statusCode': 500,
            'body': json.dumps({'error': str(e)})
        }

def delete_all_objects(bucket_name):
    s3_resource = boto3.resource('s3')
    bucket = s3_resource.Bucket(bucket_name)
    bucket.object_versions.delete()

def delete_bucket_policy(bucket_name):
    try:
        s3.delete_bucket_policy(Bucket=bucket_name)
        print(f"Deleted bucket policy for {bucket_name}")
    except s3.exceptions.ClientError as e:
        if e.response['Error']['Code'] == 'NoSuchBucketPolicy':
            print("No bucket policy to delete.")
        else:
            raise

2. API Gateway の作成

以下の設定で作成しました。

  • プロトコル: REST
  • API エンドポイントタイプ: リージョン
  • メソッド: POST
    • 統合タイプ: Lambda
    • Lambda プロキシ統合: True
    • Lambda 関数: 手順 1 で作成した関数
  • CORS: OPTION, POST で有効化
    • その他の CORS 設定はデフォルト
  • ステージ名: dev

3. 拡張機能のフォルダとファイル作成

ローカル PC 内に任意のフォルダを作成し、以下のファイルを作成します。

  • manifest.json
  • popup.js
  • popup.html

今回は以下のようなフォルダ構成にしました。

s3-delete-extension/
├── manifest.json
├── popup.js
├── popup.html

4. ファイルの編集

各種ファイルの内容は以下の通りです。
your-api-gateway-url の部分を手順 2 でデプロイした API Gateway の URL に置換します。

manifest.json
manifest.json
{
  "manifest_version": 3,
  "name": "S3 Bucket Deleter",
  "version": "1.0",
  "description": "Delete an S3 bucket via API Gateway",
  "permissions": ["activeTab", "storage"],
  "action": {
    "default_popup": "popup.html"
  },
  "host_permissions": [
    "your-api-gateway-url"
  ]
}
popup.js
popup.js
document.getElementById("deleteBtn").addEventListener("click", async () => {
  const bucket = document.getElementById("bucketName").value.trim();
  const resultDiv = document.getElementById("result");

  if (!bucket) {
    resultDiv.textContent = "⚠️ バケット名を入力してください。";
    return;
  }

  resultDiv.textContent = "⏳ 削除中...";

  try {
    const response = await fetch(
      "your-api-gateway-url",
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ bucket_name: bucket }),
      }
    );

    const data = await response.json();

    if (response.ok) {
      resultDiv.textContent = "✅ 削除成功: " + data.message;
    } else {
      resultDiv.textContent = "❌ エラー: " + (data.error || "不明なエラー");
    }
  } catch (err) {
    resultDiv.textContent = "❌ 通信エラー: " + err.message;
  }
});
popup.html
popup.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>S3 Bucket Deleter</title>
    <style>
      body {
        font-family: sans-serif;
        width: 250px;
        padding: 10px;
      }
      input {
        width: 100%;
        margin-bottom: 10px;
        padding: 5px;
      }
      button {
        width: 100%;
        padding: 5px;
        cursor: pointer;
      }
      #result {
        margin-top: 10px;
        font-size: 0.9em;
      }
    </style>
  </head>
  <body>
    <h3>Delete S3 Bucket</h3>
    <input id="bucketName" type="text" placeholder="Bucket name" />
    <button id="deleteBtn">Delete</button>
    <div id="result"></div>

    <script src="popup.js"></script>
  </body>
</html>

5. Chrome で読み込む

Chrome の拡張機能のページから手順 3 で作成したフォルダを読み込みます。

6. 動作確認

S3 コンソールからバケット一覧を表示します。

今回はテスト用に Elastic Beanstalk 環境作成時に自動作成される S3 バケットを削除してみます。
Elastic Beanstalk 用の S3 バケットにはオブジェクトが保存されており、削除禁止のバケットポリシーも定義されています。

拡張機能をクリックして S3 バケット名を入力します。

Delete ボタンをクリックします。

削除成功の表示を確認し、S3 コンソールのバケット一覧からも対象のバケットが削除されていれば OK です。

なお、AWS 公式ツールではないので拡張機能の使用は自己責任でお願いします。

まとめ

今回は S3 バケットを空にして削除する Chrome 拡張機能を作ってみました。
どなたかの参考になれば幸いです。

Discussion