既存のDynamoDBのテーブルを複製するシェルスクリプト (元テーブルと同じテーブルを作成＆元テーブルに入ってたアイテムも登録する)

# やりたいこと
既存のAWSのDynamoDBに存在するテーブル群を、そのまま複製したい
(`TABLE_A` というテーブルがあるとき、`TABLE_A_COPY` というテーブルを作りたい)

- `TABLE_A_COPY` は、 `TABLE_A` と同じテーブル定義であってほしい
- `TABLE_A_COPY` には、 `TABLE_A` に登録されているアイテムがすべて登録されていてほしい
- 上記を複数テーブルに対して行いたい

ちなみに、その理由は「テストで使いたいから」ですが、やってること自体はテストと関係ないので他の用途でも使えると思います。

# 大雑把な流れ
以下の処理を、複製したいテーブルの数だけ繰り返す

1. 既存のテーブルのテーブル定義を取得して、ファイルに出力する
1. 1 で作成したファイルを、`aws dynamodb create-table` で使用できる形に整形する (そのままだと、不要なデータがいっぱいあってエラーになるので)
1. 2 で整形したファイルを使って、新しいテーブルを作成する
1. 既存のテーブルに登録されているアイテム情報を取得して、ファイルに出力する
1. 4 で作成したファイルからアイテム情報を読み込み、新しいテーブルに登録する

# 使用するツール

- [AWS CLI] (https://aws.amazon.com/jp/cli/)
- [jq] (https://stedolan.github.io/jq/)

# 実際のスクリプト (データ作成先はローカル)
```shell:copy_table_for_test.sh
#!/bin/bash

# 元テーブルのテーブル定義から、create-table用のテーブル定義ファイルを作成する
get_table_data() {
  # AWSから元テーブルのテーブル定義を取得してファイルに出力
  aws dynamodb describe-table --table-name ${original_table_name} > ${original_table_file}

  # AWSから出力したテーブル定義を、create-tableに使用できる形に成形してファイルに出力
  cat ${original_table_file} |
  jq '.Table' |
  jq '.TableName = "'${new_table_name}'"' |
  jq 'del(.TableStatus)' |
  jq 'del(.CreationDateTime)' |
  jq 'del(.ProvisionedThroughput.LastIncreaseDateTime)' |
  jq 'del(.ProvisionedThroughput.NumberOfDecreasesToday)' |
  jq 'del(.TableSizeBytes)' |
  jq 'del(.ItemCount)' |
  jq 'del(.TableArn)' |
  jq 'del(.TableId)' |
  jq 'del(.LatestStreamLabel)' |
  jq 'del(.LatestStreamArn)' |
  jq 'del(.GlobalSecondaryIndexes[]?.IndexStatus)' |
  jq 'del(.GlobalSecondaryIndexes[]?.IndexSizeBytes)' |
  jq 'del(.GlobalSecondaryIndexes[]?.ItemCount)' |
  jq 'del(.GlobalSecondaryIndexes[]?.IndexArn)' |
  jq 'del(.GlobalSecondaryIndexes[]?.ProvisionedThroughput.NumberOfDecreasesToday)' > ${new_table_file}
}

# 元テーブルのテーブル定義ファイルを元に、テスト用のテーブルを新規作成する
create_table() {
  aws dynamodb create-table --cli-input-json file://${new_table_file} --endpoint-url http://localhost:8000
}

# 元テーブルに登録されているアイテム情報を、ファイルに出力する
get_items_data() {
  aws dynamodb scan --table-name ${table_name} > ${original_items_file}
}

# 元テーブルのアイテム情報ファイルを元に、テスト用のテーブルにアイテムを登録する
put_items() {
  # 登録対象のアイテム数
  item_length=$(cat ${original_items_file} | jq ".Items | length")

  # アイテム数 > 0 の場合のみ、テスト用テーブルにアイテムを登録する
  # (アイテムが無いのに put-itemコマンド実行しようとするとエラーになるので)
  if [ ${item_length} -gt 0 ]; then
    for i in $( seq 0 $((${item_length} - 1)) ); do
      item=$(cat ${original_items_file} | jq ".Items[${i}]")
      aws dynamodb put-item --table-name ${new_table_name} --item "${item}" --endpoint-url http://localhost:8000
    done
  fi
}

# テスト用に複製するテーブル群のテーブル名を指定
target_tables=("TABLE_A" "TABLE_B" "TABLE_C")

for table_name in ${target_tables[@]}; do
  # テーブル名
  original_table_name=${table_name}
  new_table_name=${original_table_name}_copy

  # テーブル定義ファイル名
  original_table_file=original_table_${original_table_name}.json
  new_table_file=new_table_${new_table_name}.json

  # アイテム情報ファイル名
  original_items_file=original_items_${original_table_name}.json

  get_table_data
  create_table
  get_items_data
  put_items
done
```

# 注意点

- 手元のテーブル群に対してはこれで成功したけど、コピー元のテーブルの設定によっては `get_table_data()` 内でやってるJSONファイル整形処理をもっと増やさないといけない可能性があります。
- 上記のスクリプトでは、新しいテーブルの作成先はローカルの DynamoDB にしています。
`--endpoint-url http://localhost:8000` を削除すると、ローカルじゃなくてクラウド上にテーブル作成します。

# 参考
- [既存のDynamoDBテーブルからテーブル定義を作成する]
(https://www.hands-lab.com/tech/entry/188.html)
- [既存のDynamoDBのテーブルをjsonフォーマットに変換するワンライナー] (https://qiita.com/uzresk/items/db4c7c7d6d783159c26c)