Terraform R2 Backend 移行手順 (O-10)

現状 (2026-04-28)

  • R2 bucket parky-terraform-state: ✅ 既に存在 (CF account 5d8f6201...)
  • R2 API Token: ❌ 未発行 (CF API では発行不可、Dashboard 操作必要)
  • Terraform state: dev のみ local 状態 (infra/terraform/envs/dev/terraform.tfstate)。stg / prod は state 未生成
  • Backend 設定ファイル: scaffold (infra/terraform/backend.tf.example) のみ存在
  • Terraform 専用 CF API token: ✅ 発行済 (S-11 / 1P item 3vlg2ojdtsffd6j4xde3aiveqa)

移行手順

1. R2 API Token 発行 (Dashboard)

CF API では発行不可。Dashboard から手動で実施:

  1. Cloudflare Dashboard → R2 → Manage R2 API TokensCreate API Token
  2. 設定:
    • Token name: parky-terraform-state-rw
    • Permissions: Object Read & Write
    • Specify bucket(s): parky-terraform-state のみ
    • TTL: なし (永続) — Terraform CI 用なので revoke 時は手動
  3. 発行された Access Key ID + Secret Access Key を控える (Secret はこの 1 回のみ表示)

2. 1Password に保存

# op-cache-bypass: one-time write
export OP_SERVICE_ACCOUNT_TOKEN=$(cat ~/.op/sa_token.txt)

op item create \
  --vault="p3ezteh54f3msvl4wqyw7gbiam" \
  --category="API Credential" \
  --title="Cloudflare|R2 Access Key|Terraform State" \
  "access_key_id[concealed]=<paste>" \
  "secret_access_key[concealed]=<paste>" \
  "bucket=parky-terraform-state" \
  "endpoint=https://5d8f6201999f8965395396c4674cbe2d.r2.cloudflarestorage.com" \
  "issued_on=YYYY-MM-DD" \
  "purpose=Terraform R2 backend state storage"

その後 item ID を控え、reference_1password_items.md に追記。

3. backend.tf を各 env に配置

cd infra/terraform
for env in dev stg prod; do
  cp backend.tf.example envs/$env/backend.tf
  sed -i "s|<env>|$env|g" envs/$env/backend.tf
done

4. ローカル env vars 注入 (terraform init 用)

# 1Password から R2 access key 取得
ITEM_ID="<step 2 で控えた item id>"
export AWS_ACCESS_KEY_ID=$(bash scripts/op-cache/op-cache.sh get \
  "op://p3ezteh54f3msvl4wqyw7gbiam/${ITEM_ID}/access_key_id")
export AWS_SECRET_ACCESS_KEY=$(bash scripts/op-cache/op-cache.sh get \
  "op://p3ezteh54f3msvl4wqyw7gbiam/${ITEM_ID}/secret_access_key")
# CF API token (Terraform-only) も別途
export CLOUDFLARE_API_TOKEN=$(bash scripts/op-cache/op-cache.sh get \
  "op://p3ezteh54f3msvl4wqyw7gbiam/3vlg2ojdtsffd6j4xde3aiveqa/credential")

5. State 移行 (各 env で 1 回だけ)

cd infra/terraform/envs/dev
terraform init -migrate-state
# 既存 terraform.tfstate → s3://parky-terraform-state/dev/terraform.tfstate へ転送

stg / prod は state 未生成なので terraform init だけで OK (新規 backend 取得)。

6. State 移行確認後、local backup 削除

cd infra/terraform/envs/dev
terraform plan  # 0 diff であること
# OK なら local state file を削除
rm -f terraform.tfstate terraform.tfstate.backup terraform.tfstate.*.backup

7. CI workflows の更新

.github/workflows/terraform.yml (or 該当 deploy workflow) に追加:

- name: Load secrets from 1Password
  uses: 1password/load-secrets-action@<sha>
  with:
    export-env: true
  env:
    OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SA_TOKEN }}
    AWS_ACCESS_KEY_ID: op://...
    AWS_SECRET_ACCESS_KEY: op://...
    CLOUDFLARE_API_TOKEN: op://p3ezteh54f3msvl4wqyw7gbiam/3vlg2ojdtsffd6j4xde3aiveqa/credential

ロールバック

State 移行後に問題が起きた場合:

  1. R2 bucket から state を aws s3 cp (R2 endpoint 指定) で local にダウンロード
  2. backend.tf を削除 (または local に戻す)
  3. terraform init -migrate-state -force-copy で local に戻す

完全 rollback できるよう、移行直後の R2 state object を別 key (*.bootstrap-snapshot) に複製しておくと安全。

関連

  • TF 専用 CF API token (S-11): 1P item 3vlg2ojdtsffd6j4xde3aiveqa (PJ|Parky vault)
  • backend.tf scaffold: infra/terraform/backend.tf.example
  • 元の Terraform inventory: .work/parky/terraform-inventory/inventory_2026_04_26.md
↗ Source markdown (terraform-backend-migration.md)