S3バケットのライフサイクルルールを一括で更新するスクリプトを作成してみた

2021.08.22

7月からアノテーション テクニカルサポートチームにJOINしました 川崎です。

S3のライフサイクル設定

S3のライフサイクル設定を使用すると、バケット内のオブジェクトのライフサイクル管理を指定することができます。

前回のブログでは、AWS CLIの s3api を使用して、指定のライフサイクルルールが付与されていないS3バケットをチェックするスクリプトを作成しました。

S3のライフサイクル設定を更新する際の課題

ライフサイクル設定をチェックするスクリプトができれば、 ライフサイクル設定を更新するよう、スクリプトを修正するのは、一見簡単そうに思えます。

既存設定が消えてしまう

ところが、一点注意が必要なところがあり、それが下記ブログの内容になります。

→ 新しいルールを追加
→ 新規にルールを追加したい場合は、まずは既存のルールと新規ルールをまとめたJSONを用意します。

例を挙げて説明いたします。 rule="rule1"を指定してスクリプトを実行し、結果が

bucket-c failure

となったと仮定します。

bucket-c にrule1とは別のルール「rule2」が適用されていた場合、 put-bucket-lifecycle-configuration API でrule1を適用した場合、 既存のルール(rule2)は一旦削除され、単に「rule1」のみが適用された状態となります。

「rule2」を残した状態で「rule1」を追加するには、

新規にルールを追加したい場合は、まずは既存のルールと新規ルールをまとめたJSONを用意します。

という対応が必要となります。 この処理を実現するのは結構ハードルが高いのではないでしょうか。

それもjqで解決できますよ

前回の記事でも、JSONパーサー「jq」が活躍しました。 CLIの実行結果はJSON形式で返ってきますので、jqコマンドを利用して、必要な情報だけをフィルタするという形で利用しました。

今回はjqコマンドで、既存のルールと新規ルールをマージしたJSONを用意します。

スクリプト作成

update-s3-lifecycle-conf.bash

#!/bin/bash

# 検索するライフサイクル・ルール名を指定
target_rule_name="delete-incomplete-multipart-upload-7days"
# 上記のライフサイクル・ルールの内容を指定
target_rule_json=$(cat <<EOS
{
    "Rules": [
        {
            "ID": "${target_rule_name}",
            "Filter": {},
            "Status": "Enabled",
            "AbortIncompleteMultipartUpload": {
                "DaysAfterInitiation": 7
            }
        }
    ]
}
EOS
)

# 更新前の既存のライフサイクル・ルールを保存しておく日時ベース([年月日-時分秒])のフォルダを作成
dir1="$(date "+%Y%m%d-%H%M%S")"
mkdir $dir1
echo "$target_rule_json" > $dir1/target_rule.json

# AWSアカウントの全てのバケットを参照する
for bucket in $(aws s3 ls | awk '{print $3}');
# 動作確認時はアンコメント、「bucket_list.txt」ファイルにバケット名を記述するとそのバケットだけチェックします
#for bucket in $(cat bucket_list.txt);
do
  # バケット名を表示
  echo -ne $bucket "\t"
  # バケットのライフサイクル・ルールを確認し、ターゲットルールをSELECT
  result=$(aws s3api get-bucket-lifecycle-configuration --bucket $bucket 2>&1 | jq -r --arg rule $target_rule_name '.Rules[] | select(.ID==$rule) | .ID' 2>&1)
  # コマンドの終了ステータス
  exit_status=$?
  # 終了ステータスが0(成功) AND 出力結果が空文字列でない場合
  if [ $exit_status -eq 0 -a "$result" != "" ]; then
    # 指定したライフサイクル・ルールが存在する場合
    echo "success";
  else
    # 指定したライフサイクル・ルールが存在しない場合
    echo -n "failure -> ";

    # 再度バケットのライフサイクル・ルールを確認
    result2=$(aws s3api get-bucket-lifecycle-configuration --bucket $bucket 2>&1)
    # コマンドの終了ステータス
    exit_status=$?
  
    # 終了ステータスが0(成功) AND 出力結果が空文字列でない場合
    if [ $exit_status -eq 0 -a "$result2" != "" ]; then
        # 他のライフサイクル・ルールが存在する場合
        echo -n "found other rule(s).";
        # 他のライフサイクル・ルールを保存しておく
        echo "$result2" > ${dir1}/${bucket}_before-rule.json
        # 他のライフサイクル・ルールと指定のルールをマージしJSONファイルとして出力
        jq -n '{ Rules: [ inputs.Rules ] | add }' ${dir1}/${bucket}_before-rule.json ${dir1}/target_rule.json > ${dir1}/${bucket}_after-rule.json
        # マージしたJSONファイルを新しいライフサイクル・ルールとして設定する
        aws s3api put-bucket-lifecycle-configuration --bucket $bucket --lifecycle-configuration file://${dir1}/${bucket}_after-rule.json
    else
        # ライフサイクル・ルールが1つも存在しない場合
        echo -n "found no rule.";
        # 指定のルールを新しいライフサイクル・ルールとして設定する
        aws s3api put-bucket-lifecycle-configuration --bucket $bucket --lifecycle-configuration file://${dir1}/target_rule.json
    fi;
    echo " updated.";
  fi; 
done
  • JSONパーサー「jq」を使用しておりますので、jqがインストールされた環境で実行してください。
  • 環境変数「AWS_DEFAULT_PROFILE」を指定して、スクリプトを実行してください。 (例: export AWS_DEFAULT_PROFILE=dev)
  • スクリプトの4行目で、検索するライフサイクルルール名を指定してください。
  • スクリプトの6行目以下で、ライフサイクル・ルールの内容を指定してください。
  • 指定のライフサイクルポリシーが付与されているかどうか、「ライフサイクルルール名」のみを使い判定されます。(ルールの内容は参照されません)
  • スクリプトを実行すると、更新前の既存のライフサイクル・ルールを保存しておく日時ベース([年月日-時分秒])のフォルダが作成されます。

実行結果の例

実行前の状態

target_rule_name:rule1

bucket-a 既存ルール: なし
bucket-b 既存ルール: rule1
bucket-c 既存ルール: rule2

この状態で実行した場合の実行例は、下記のようになります。

# 実行例
$ bash update-s3-lifecycle-conf.bash
bucket-a    failure -> found no rule. updated.
bucket-b    success
bucket-c    failure -> found other rule(s). updated.
  • bucket-a は、既存ルールがない状態なので、failure(→ 指定のライフサイクルポリシー名が付与されていない)で、新規にtarget_ruleが適用されます。
  • bucket-b は、指定のライフサイクルポリシー名が付与されているので「success」と表示されます。
  • bucket-c は、failure(→ 指定のライフサイクルポリシー名が付与されていない)で、既存のrule2と新規のrule1がマージされた内容が適用されます。

  • 日時ベースのフォルダには、以下のファイルが保存されます。

    • スクリプトの6行目以下に記述した、ライフサイクル・ルールの内容。(ファイル名:target_rule.json)
    • マージしたJSONファイルを新しいライフサイクル・ルールとして設定する場合(bucket-cのケース)、次の2つのファイルが保存されます。
      • [bucket-name]_before-rule.json (マージ前の既存のルール)
      • [bucket-name]_after-rule.json (マージ後のルール)

実行後の状態

bucket-a 適用ルール: rule1
bucket-b 適用ルール: rule1
bucket-c 適用ルール: rule1、rule2

すべてのバケットにrule1が適用されました。

まとめ

今回は、S3バケットのライフサイクルルールを一括で更新するスクリプトを作成してみました。

今回のスクリプトでも「jq」コマンドを活用しました。 jqコマンドは、JSONから値を抜き出したり、整形して表示できる、JSON向けのsed/awk/grepみたいなコマンドです。

近頃は、WebサービスがJSONを出力したり、 AWS CLIが JSONを出力しますので、それらを扱うのに「jq」はとても便利なコマンドです。

クラウドの本質はAPI、とよく言われますが、 AWSもすべてのサービスでAPIが提供され、APIですべてをコントロールします。 AWSサービスの本質も、APIにより連携する「ビルディングブロック」です。

そのビルディングブロックは、コマンドラインを操作してAPIを利用するAWS CLIから実行することができます。 AWS CLIからの出力もJSON形式ですので、その場面でも、JSONを加工するJSONパーサー「jq」コマンドが活躍します。

今回のような、やや複雑な処理でも「JSONの万能ナイフ=jq」で実現することが可能でした。 機会がありましたら、jq で処理可能な内容の幅広さについて、ご紹介する記事を書きたいと考えております。

この記事を、日々の運用の効率化に役立てていただけたら幸いです。