CloudFrontの大量の標準ログオブジェクトに時刻に基づいたプレフィックスを付与してみた

CloudFrontの大量の標準ログオブジェクトに時刻に基づいたプレフィックスを付与してみた

標準ログV2移行後の形式に合わせたい場合に
Clock Icon2025.02.17

大量のログオブジェクトに対してまとめて時刻に基づいたプレフィックスを付与したい

こんにちは、のんピ(@non____97)です。

皆さんはCloudFrontの標準ログ(S3 Legacy)で出力された大量のログオブジェクトに対して、まとめてに時刻に基づいたプレフィックスを付与したいと思ったことはありますか? 私はあります。

標準ログV2が登場したことによって、S3バケットに出力させる際にパーティションの指定ができるようになりました。

https://dev.classmethod.jp/articles/cloudfront-access-log-update-202411/

従来は以下記事で紹介しているように、S3バケットにログオブジェクトがPUTされたらLambda関数などを用いてオブジェクトキーを変更を行う手間が必要でした。

https://dev.classmethod.jp/articles/aws-cdk-cloudfront-s3-website-log-analytics/

やったー

というところですが、元々Lambda関数を用いてプレフィックスを付与していなかった場合は、大量のログオブジェクトがS3バケット内にフラットに存在している形になります。

標準ログV2を導入するにあたって、既存ログオブジェクトのプレフィックスを付与したい場面もあるでしょう。

Lambda関数で大量のログオブジェクトに対して実行すると、それなりの実行時間となり、Lambda関数の課金が気になります。そのため、今回はシェルスクリプトで既存ログオブジェクトにプレフィックスをまとめて付与してみました。

スクリプトの紹介

CloudFrontの標準ログのログオブジェクトはE21YV8QMTCNCR6.2025-01-24-05.15058aee.gzというように${distribution_id}.${year}-${month}-${day}-${hour}.${id}.gzというフォーマットです。

標準ログV2のログオブジェクトの一般的なプレフィックスは${distribution_id}/${year}/${month}/${day}/${hour}/であると思うので、ログオブジェクトのキーから計算することが可能です。

これを利用して各ログオブジェクトからプレフィックスを組み立てます。

スクリプトは以下のとおりです。

#!/bin/bash

set -euo pipefail

###################
# デフォルト設定値
###################
readonly MAX_PARALLEL=20 # s3 mv の同時実行数
readonly RETRY_COUNT=3
readonly RETRY_WAIT=2
readonly LOG_DIR="logs"
DRYRUN=false
DRYRUN_LIMIT=10 # デフォルトの表示件数
TARGET_OBJECT_LIST=""
SHOW_ALL=false # 全件表示フラグ

###################
# 初期化関数
###################
initialize() {
  TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
  TEMP_DIR="$(mktemp -d)"
  readonly TIMESTAMP
  readonly TEMP_DIR
  readonly LOG_FILE="${LOG_DIR}/move_${TIMESTAMP}.log"
  readonly ERROR_LOG="${LOG_DIR}/errors_${TIMESTAMP}.log"
}

###################
# 使用方法の表示
###################
show_usage() {
  echo "Usage: $0 -b BUCKET_NAME -a AWS_ACCOUNT_ID [OPTIONS]"
  echo ""
  echo "Required:"
  echo "  -b, --bucket BUCKET_NAME    S3 bucket name"
  echo "  -a, --account ACCOUNT_ID    AWS account ID"
  echo ""
  echo "Options:"
  echo "  -d, --dryrun             Show planned moves without executing"
  echo "  -ol, --object-list        Use existing object list instead of fetching from S3"
  echo "  -n, --limit NUMBER        Number of entries to show in dryrun mode (default: 10)"
  echo "  -A, --show-all            Show all entries in dryrun mode"
  echo "  -h, --help                Show this help message"
  echo ""
  echo "Example:"
  echo "  $0 -b my-bucket -a 123456789012 --dryrun"
  echo "  $0 -b my-bucket -a 123456789012 --dryrun --limit 20"
  echo "  $0 -b my-bucket -a 123456789012 --dryrun --show-all"
  echo "  $0 --bucket my-bucket --account 123456789012 --file target_object_list.txt"
}

###################
# 引数のバリデーション
###################
validate_args() {
  # S3バケットの指定
  if [[ -z "${BUCKET_NAME}" ]]; then
    echo "Error: Bucket name is required"
    show_usage
    exit 1
  fi

  # AWSアカウントIDの指定
  if [[ -z "${ACCOUNT_ID}" ]]; then
    echo "Error: AWS account ID is required"
    show_usage
    exit 1
  fi

  # AWSアカウントIDの形式チェック(12桁の数字)
  if ! [[ "${ACCOUNT_ID}" =~ ^[0-9]{12}$ ]]; then
    echo "Error: Invalid AWS account ID format. Must be 12 digits."
    exit 1
  fi

  # 移動対象のオブジェクト一覧をまとめたファイル一覧の存在確認
  if [[ -n "${TARGET_OBJECT_LIST}" && ! -f "${TARGET_OBJECT_LIST}" ]]; then
    echo "Error: Specified file ${TARGET_OBJECT_LIST} does not exist."
    exit 1
  fi

  # dry run時に表示する件数の確認
  if [[ -n "${DRYRUN_LIMIT}" ]] && ! [[ "${DRYRUN_LIMIT}" =~ ^[0-9]+$ ]]; then
    echo "Error: Limit must be a positive number"
    exit 1
  fi
}

###################
# 終了時のクリーンアップ処理
###################
cleanup() {
  if [[ -d "${TEMP_DIR}" ]]; then
    rm -rf "${TEMP_DIR}"
  fi
}

###################
# ユーティリティ関数
###################
log() {
  local message
  message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
  echo "${message}" | tee -a "${LOG_FILE}"
}

get_object_count() {
  wc -l <"$1"
}

###################
# 指定したS3バケット内のオブジェクト一覧を取得する関数
###################
fetch_s3_objects() {
  local target_object_list
  target_object_list="${TEMP_DIR}/target_object_list.txt"

  # ターゲットとなるS3ログオブジェクト一覧のファイルが指定された場合
  if [[ -n "${TARGET_OBJECT_LIST}" ]]; then
    log "Using existing object list: ${TARGET_OBJECT_LIST}"
    cp "${TARGET_OBJECT_LIST}" "${target_object_list}"
  # ターゲットとなるS3ログオブジェクト一覧のファイルが指定されていない場合
  else
    log "Checking objects in bucket: ${BUCKET_NAME}"

    # バケットが存在し、アクセス可能か確認
    if ! aws s3api head-bucket --bucket "${BUCKET_NAME}" >/dev/null 2>&1; then
      log "Error: Unable to access bucket: ${BUCKET_NAME}"
      exit 1
    fi

    # バケット直下の.gzオブジェクトを確認し、0なら終了
    local first_gz_object
    first_gz_object=$(aws s3api list-objects-v2 \
      --bucket "${BUCKET_NAME}" \
      --delimiter "/" \
      --query "Contents[?contains(Key, '/') == \`false\` && ends_with(Key, '.gz')][Key]" \
      --max-items 1 \
      --output text 2>/dev/null || echo "")

    if [[ "${first_gz_object}" == "None" ]] || [[ -z "${first_gz_object}" ]]; then
      log "No .gz object found in bucket root: ${BUCKET_NAME}"
      exit 0
    fi

    log "Fetching object from bucket: ${BUCKET_NAME}"
    aws s3 ls "s3://${BUCKET_NAME}/" | grep '\.gz$' | awk '{print $4}' >"${target_object_list}"
  fi

  local target_objects_length
  target_objects_length=$(get_object_count "${target_object_list}")
  log "Total objects found: ${target_objects_length}"
}

###################
# S3オブジェクトから移動先のオブジェクトのプレフィックスを計算する関数
###################
parse_cloudfront_log_prefix() {
  local object_key="$1"
  local distribution_id
  local timestamp
  local year
  local month
  local day
  local hour

  distribution_id=$(echo "${object_key}" | cut -d'.' -f1)
  timestamp=$(echo "${object_key}" | grep -o '[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}')
  year=$(echo "${timestamp}" | cut -d'-' -f1)
  month=$(echo "${timestamp}" | cut -d'-' -f2)
  day=$(echo "${timestamp}" | cut -d'-' -f3)
  hour=$(echo "${timestamp}" | cut -d'-' -f4)

  echo "AWSLogs/${ACCOUNT_ID}/CloudFront/${distribution_id}/${year}/${month}/${day}/${hour}/"
}

###################
# 各オブジェクトに標準的なプレフィックスを付与する関数
###################
add_standard_prefix_to_log() {
  local object_key="$1"
  local new_prefix
  new_prefix=$(parse_cloudfront_log_prefix "${object_key}")
  local source_object_key="s3://${BUCKET_NAME}/${object_key}"
  local target_object_key="s3://${BUCKET_NAME}/${new_prefix}${object_key}"

  if [[ "${DRYRUN}" = true ]]; then
    aws s3 mv "${source_object_key}" "${target_object_key}" --dryrun
    return 0
  fi

  for attempt in $(seq 1 ${RETRY_COUNT}); do
    if [[ ${attempt} -gt 1 ]]; then
      log "Retry attempt ${attempt} for ${object_key}"
      sleep ${RETRY_WAIT}
    fi

    if aws s3 mv "${source_object_key}" "${target_object_key}" 2>/dev/null; then
      echo "${object_key}" >>"${TEMP_DIR}/processed.txt"
      return 0
    fi
  done

  echo "${object_key}" >>"${ERROR_LOG}"
  return 1
}

###################
# 指定したS3バケット内のオブジェクトに対して処理を行う関数
###################
process_cloudfront_logs() {
  local target_objects_length
  target_objects_length=$(get_object_count "${TEMP_DIR}/target_object_list.txt")
  log "Total objects to process: ${target_objects_length}"

  local processed=0
  local pids=()

  while IFS= read -r object_key; do
    if [[ "${DRYRUN}" = false || "${SHOW_ALL}" = true || ${processed} -lt ${DRYRUN_LIMIT} ]]; then
      # バックグラウンドで処理実行
      add_standard_prefix_to_log "${object_key}" &
      pids+=($!)

      # 並列実行数の上限に達したら待機
      if ((${#pids[@]} >= MAX_PARALLEL)); then
        wait "${pids[0]}"
        pids=("${pids[@]:1}")
      fi
    fi
    : $((processed++))

    # 100件ごとに進捗を表示
    if [[ "${DRYRUN}" = false && $((processed % 100)) -eq 0 ]]; then
      log "Progress: ${processed} objects processed"
    fi
  done <"${TEMP_DIR}/target_object_list.txt"

  # 残りのジョブの完了を待機
  wait

  # 結果の集計
  local completed
  completed=$(get_object_count "${TEMP_DIR}/processed.txt" 2>/dev/null || echo 0)
  local errors
  errors=$(get_object_count "${ERROR_LOG}" 2>/dev/null || echo 0)

  log "$(if [[ "${DRYRUN}" = true ]]; then echo "Dry run"; else echo "Migration"; fi) completed"
  if [[ "${DRYRUN}" = false ]]; then
    log "Objects processed: ${completed}"
    log "Errors: ${errors}"
  fi
}

###################
# メイン処理
###################
main() {
  log "Script started with bucket: ${BUCKET_NAME}, account: ${ACCOUNT_ID}"
  log "Mode: $(if [[ "${DRYRUN}" = true ]]; then echo "Dry run"; else echo "Execution"; fi)"

  fetch_s3_objects

  # 実行モードの場合、確認を求める
  if [[ "${DRYRUN}" = false ]]; then
    echo ""
    echo "This will move objects in bucket: ${BUCKET_NAME}"
    read -rp "Are you sure you want to proceed? [y/N] " response

    if [[ ! "${response}" =~ ^[Yy]$ ]]; then
      log "Operation cancelled by user"
      exit 0
    fi

    log "Proceeding with object movement"
  fi

  process_cloudfront_logs
}

###################
# コマンドライン引数の処理
###################
while [[ $# -gt 0 ]]; do
  case $1 in
  -b | --bucket)
    BUCKET_NAME="$2"
    shift 2
    ;;
  -a | --account)
    ACCOUNT_ID="$2"
    shift 2
    ;;
  -d | --dryrun)
    DRYRUN=true
    shift
    ;;
  -ol | --object-list)
    TARGET_OBJECT_LIST="$2"
    shift 2
    ;;
  -n | --limit)
    DRYRUN_LIMIT="$2"
    shift 2
    ;;
  -A | --show-all)
    SHOW_ALL=true
    shift
    ;;
  -h | --help)
    show_usage
    exit 0
    ;;
  *)
    echo "Error: Unknown option $1"
    show_usage
    exit 1
    ;;
  esac
done

# 引数のバリデーション
validate_args

# 初期設定
initialize
trap 'cleanup' EXIT
mkdir -p "${LOG_DIR}"

# メイン処理の実行
main

はい、せっかくなのでdryrun機能や、ログオブジェクト一覧を保存したファイルを読み込ませて実行する機能も追加してみました。

また、一件一件実行すると非常に時間がかかるため、MAX_PARALLELの数分だけバックグラウンドプロセスを用いた並列処理を行うようにしています。

やってみた

dry run

実際に試してみます。

まず、dry runです。

対象のS3バケット上には以下のようにログオブジェクトが存在しています。

>  s3-tree aurora-postgresql-log
aurora-postgresql-log
├── E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー14.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー15.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー16.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー2.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー3.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー4.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー5.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー6.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー7.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー8.gz
├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー9.gz
├── E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー17.gz
├── E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー18.gz
├── E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー19.gz
├── E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー20.gz
├── E21YV8QMTCNCR6.2025-01-25-06.e256b268のコピー21.gz
├── E21YV8QMTCNCR6.2025-01-26-06.e256b268のコピー22.gz
└── E21YV8QMTCNCR6.2025-01-27-23.e256b268のコピー23.gz

dry runで実行してみます。

>  bash move_cloudfront_s3_legacy_logs.sh \
         --bucket aurora-postgresql-log \
         --account <AWSアカウントID> \
         --dryrun
[2025-02-16 10:02:38] Script started with bucket: aurora-postgresql-log, account: <AWSアカウントID>
[2025-02-16 10:02:38] Mode: Dry run
[2025-02-16 10:02:38] Checking objects in bucket: aurora-postgresql-log
[2025-02-16 10:02:41] Fetching object from bucket: aurora-postgresql-log
[2025-02-16 10:02:42] Total objects found:       25
[2025-02-16 10:02:42] Total objects to process:       25
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー15.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー15.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー16.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー16.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー14.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー14.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz
[2025-02-16 10:02:44] Dry run completed

25件10件表示されました。

いずれも意図したプレフィックスが付与されています。

念のため全件確認したい場合は--show-allオプションを付与して下さい。こちらのオプションを付与すると、全件のdry run結果を確認することも可能です。

>  bash move_cloudfront_s3_legacy_logs.sh \
         --bucket aurora-postgresql-log \
         --account <AWSアカウントID> \
         --dryrun \
         --show-all
[2025-02-16 10:09:26] Script started with bucket: aurora-postgresql-log, account: <AWSアカウントID>
[2025-02-16 10:09:26] Mode: Dry run
[2025-02-16 10:09:26] Checking objects in bucket: aurora-postgresql-log
[2025-02-16 10:09:29] Fetching object from bucket: aurora-postgresql-log
[2025-02-16 10:09:31] Total objects found:       25
[2025-02-16 10:09:31] Total objects to process:       25
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー4.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー4.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー15.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー15.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー16.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー16.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー7.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー7.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー18.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/06/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー18.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー8.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー8.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー17.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/06/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー17.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー6.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー6.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー5.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー5.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー14.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー14.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー3.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー3.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー2.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー2.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー9.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー9.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-25-06.e256b268のコピー21.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/25/06/E21YV8QMTCNCR6.2025-01-25-06.e256b268のコピー21.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー19.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/06/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー19.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-26-06.e256b268のコピー22.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/26/06/E21YV8QMTCNCR6.2025-01-26-06.e256b268のコピー22.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー20.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/06/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー20.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-27-23.e256b268のコピー23.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/27/23/E21YV8QMTCNCR6.2025-01-27-23.e256b268のコピー23.gz
[2025-02-16 10:09:35] Dry run completed

また、--limit <件数>のオプションを付与すると、指定した件数だけdry runを実行します。

bash move_cloudfront_s3_legacy_logs.sh \
         --bucket aurora-postgresql-log \
         --account <AWSアカウントID> \
         --dryrun \
         --limit 1
[2025-02-16 10:02:52] Script started with bucket: aurora-postgresql-log, account: <AWSアカウントID>
[2025-02-16 10:02:52] Mode: Dry run
[2025-02-16 10:02:52] Checking objects in bucket: aurora-postgresql-log
[2025-02-16 10:02:54] Fetching object from bucket: aurora-postgresql-log
[2025-02-16 10:02:56] Total objects found:       25
[2025-02-16 10:02:56] Total objects to process:       25
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz
[2025-02-16 10:02:57] Dry run completed

実行モード

それでは実際にプレフィックスを付与するように実行してみます。

>  bash move_cloudfront_s3_legacy_logs.sh \
         --bucket aurora-postgresql-log \
         --account <AWSアカウントID>
[2025-02-16 10:10:13] Script started with bucket: aurora-postgresql-log, account: <AWSアカウントID>
[2025-02-16 10:10:13] Mode: Execution
[2025-02-16 10:10:13] Checking objects in bucket: aurora-postgresql-log
[2025-02-16 10:10:15] Fetching object from bucket: aurora-postgresql-log
[2025-02-16 10:10:16] Total objects found:       25

This will move objects in bucket: aurora-postgresql-log
Are you sure you want to proceed? [y/N] y
[2025-02-16 10:10:21] Proceeding with object movement
[2025-02-16 10:10:22] Total objects to process:       25
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー7.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー7.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー18.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/06/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー18.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー16.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー16.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー17.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/06/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー17.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー15.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー15.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー14.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー14.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー5.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー5.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー4.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー4.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー9.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー9.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー3.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー3.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー8.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー8.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー6.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー6.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー2.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー2.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー19.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/06/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー19.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー20.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/06/E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー20.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-26-06.e256b268のコピー22.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/26/06/E21YV8QMTCNCR6.2025-01-26-06.e256b268のコピー22.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-25-06.e256b268のコピー21.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/25/06/E21YV8QMTCNCR6.2025-01-25-06.e256b268のコピー21.gz
move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-27-23.e256b268のコピー23.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/27/23/E21YV8QMTCNCR6.2025-01-27-23.e256b268のコピー23.gz
[2025-02-16 10:10:28] Migration completed
[2025-02-16 10:10:28] objects processed:       25
[2025-02-16 10:10:28] Errors: 0

正常に実行が完了しました。

S3バケット内のオブジェクト一覧を確認してみます。

>  s3-tree aurora-postgresql-log
aurora-postgresql-log
└── AWSLogs
    └── <AWSアカウントID>
        └── CloudFront
            └── E21YV8QMTCNCR6
                └── 2025
                    └── 01
                        ├── 24
                        │   ├── 05
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー14.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー15.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー16.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー2.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー3.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー4.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー5.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー6.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー7.gz
                        │   │   ├── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー8.gz
                        │   │   └── E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー9.gz
                        │   └── 06
                        │       ├── E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー17.gz
                        │       ├── E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー18.gz
                        │       ├── E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー19.gz
                        │       └── E21YV8QMTCNCR6.2025-01-24-06.e256b268のコピー20.gz
                        ├── 25
                        │   └── 06
                        │       └── E21YV8QMTCNCR6.2025-01-25-06.e256b268のコピー21.gz
                        ├── 26
                        │   └── 06
                        │       └── E21YV8QMTCNCR6.2025-01-26-06.e256b268のコピー22.gz
                        └── 27
                            └── 23
                                └── E21YV8QMTCNCR6.2025-01-27-23.e256b268のコピー23.gz

はい、各オブジェクトに意図したプレフィックスが付与されていますね。

事前に用意したオブジェクトリストを用いて実行

事前に用意したオブジェクトリストを用いて実行することも可能です。

以下のようにオブジェクト名のリストを保存したテキストファイルを用意します。

./target.txt
E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz
E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz
E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz
E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz
E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz
E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz
E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz

こちらを指定して実行します。

>  bash move_cloudfront_s3_legacy_logs.sh \
         --bucket aurora-postgresql-log \
         --account <AWSアカウントID> \
         --object-list target.txt \
         --dryrun
[2025-02-17 09:09:02] Script started with bucket: aurora-postgresql-log, account: <AWSアカウントID>
[2025-02-17 09:09:02] Mode: Dry run
[2025-02-17 09:09:02] Using existing object list: target.txt
[2025-02-17 09:09:02] Total objects found:        7
[2025-02-17 09:09:02] Total objects to process:        7
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー11.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー10.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー12.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268のコピー13.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.15058aee.gz
(dryrun) move: s3://aurora-postgresql-log/E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz to s3://aurora-postgresql-log/AWSLogs/<AWSアカウントID>/CloudFront/E21YV8QMTCNCR6/2025/01/24/05/E21YV8QMTCNCR6.2025-01-24-05.e256b268.gz
[2025-02-17 09:09:04] Dry run completed

指定したログオブジェクトに対してdry runが実行されたことが分かります。

標準ログV2移行後の形式に合わせたい場合に

CloudFrontの大量の標準ログオブジェクトに時刻に基づいたプレフィックスを付与してみました。

標準ログV2移行後の形式に合わせたい場合に使用してみて下さい。

この記事が誰かの助けになれば幸いです。

以上、クラウド事業本部 コンサルティング部の のんピ(@non____97)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.