From a125515ca069dc1f8d66cb980ffc4c7975eb89e7 Mon Sep 17 00:00:00 2001 From: Konstantin Renner Date: Fri, 26 Jun 2026 23:14:07 +0200 Subject: [PATCH] ci: one-off manual migrate-tfstate pipeline (Garage nas-1 -> Hetzner) Migrates the OpenTofu remote state to Hetzner Object Storage. Garage is read-only (state pull only), so reversible. Manual trigger only. Co-Authored-By: Claude Opus 4.8 (1M context) --- .woodpecker/migrate-tfstate.yml | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .woodpecker/migrate-tfstate.yml diff --git a/.woodpecker/migrate-tfstate.yml b/.woodpecker/migrate-tfstate.yml new file mode 100644 index 00000000..38e15ba3 --- /dev/null +++ b/.woodpecker/migrate-tfstate.yml @@ -0,0 +1,62 @@ +# One-off, manual-only: migrate the OpenTofu remote state from the nas-1 +# Garage S3 to Hetzner Object Storage. Garage is only READ (state pull) and +# never written/deleted, so the source stays intact and the move is fully +# reversible (just revert providers.tf to Garage). Trigger: +# woodpecker pipeline create ... --var PIPELINE=migrate-tfstate +when: + - event: manual + evaluate: 'PIPELINE == "migrate-tfstate"' + +steps: + - name: migrate-tfstate + image: gitea.renner.uno/gitea_admin/ci-tofu:2026-04-29 + directory: opentofu + environment: + TF_IN_AUTOMATION: "true" + TF_VAR_proxmox_api_url: + from_secret: proxmox_api_url + TF_VAR_proxmox_user: + from_secret: proxmox_user + TF_VAR_proxmox_token_id: + from_secret: proxmox_token_id + TF_VAR_proxmox_token_secret: + from_secret: proxmox_token_secret + TF_VAR_unifi_api_key: + from_secret: unifi_api_key + TF_VAR_unifi_api_url: + from_secret: unifi_api_url + TF_VAR_hetzner_s3_access_key: + from_secret: hetzner_s3_access_key + TF_VAR_hetzner_s3_secret_key: + from_secret: hetzner_s3_secret_key + GARAGE_ACCESS: + from_secret: tf_garage_access + GARAGE_SECRET: + from_secret: tf_garage_secret + HETZNER_ACCESS: + from_secret: hetzner_s3_access_key + HETZNER_SECRET: + from_secret: hetzner_s3_secret_key + commands: + - set -eu + - echo "### 1/4 init Garage (source) + pull the live state ###" + - tofu init -upgrade -backend-config="access_key=$GARAGE_ACCESS" -backend-config="secret_key=$GARAGE_SECRET" + - tofu state pull > /tmp/state.json + - BYTES=$(wc -c < /tmp/state.json); echo "pulled state: $BYTES bytes" + - test "$BYTES" -gt 2000 + - RES=$(grep -c '"type":' /tmp/state.json || true); echo "resource entries: $RES" + - test "$RES" -gt 10 + - echo "### 2/4 repoint the backend block to Hetzner (checkout only) ###" + - sed -i 's#https://s3.nas-1.renner.uno#https://fsn1.your-objectstorage.com#' providers.tf + - sed -i 's#bucket *= *"tfstate"#bucket = "renner-uno-tfstate"#' providers.tf + - sed -i 's#region *= *"garage"#region = "fsn1"#' providers.tf + - sed -i 's#use_path_style *= *true#use_path_style = false#' providers.tf + - grep -E 'bucket|key|endpoint|region|use_path_style' providers.tf + - echo "### 3/4 reconfigure to Hetzner (target) + push ###" + - tofu init -reconfigure -backend-config="access_key=$HETZNER_ACCESS" -backend-config="secret_key=$HETZNER_SECRET" + - tofu state push /tmp/state.json + - echo "### 4/4 verify state now lives on Hetzner ###" + - tofu state list > /tmp/list.txt + - 'echo "migrated resources: $(wc -l < /tmp/list.txt)"' + - test "$(wc -l < /tmp/list.txt)" -gt 10 + - head -20 /tmp/list.txt -- 2.54.0