chore(infra): migrate Terraform state GitLab -> Scaleway Object Storage (Phase 2A)
All 4 modules (kapsule/public-gateway/dns/block-storage) migrated to s3 backend (bucket foxhunt-tfstate, fr-par, use_lockfile=true). plan=No changes verified per module + final re-plan; all 4 state objects present in bucket; lock smoke test passed (refresh-only apply, no orphan lockfile). GitLab http state left intact as fallback (removed in Phase 2C). Also fixed root.hcl path in spec/plan docs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
external Scaleway Object Storage (bucket `foxhunt-tfstate`), with native S3 lockfile locking.
|
||||
|
||||
**Architecture:** Back up all 4 states from GitLab first; flip the single `remote_state` block in
|
||||
`infra/live/root.hcl` from `http`→`s3`; migrate each module in-place with `terragrunt init
|
||||
`infra/live/production/root.hcl` from `http`→`s3`; migrate each module in-place with `terragrunt init
|
||||
-migrate-state`; gate each on `terragrunt plan = No changes`. GitLab state is left intact as a fallback
|
||||
(deleted later in 2C).
|
||||
|
||||
@@ -85,11 +85,11 @@ Expected: a serial number per module. Keep this output — the migrated state sh
|
||||
|
||||
## Task 2: Switch the backend in root.hcl (http → s3)
|
||||
|
||||
**Files:** Modify `infra/live/root.hcl`
|
||||
**Files:** Modify `infra/live/production/root.hcl`
|
||||
|
||||
- [ ] **Step 1: Replace the `remote_state` block**
|
||||
|
||||
In `infra/live/root.hcl`, replace the entire existing `remote_state { ... }` block (the `backend = "http"`
|
||||
In `infra/live/production/root.hcl`, replace the entire existing `remote_state { ... }` block (the `backend = "http"`
|
||||
GitLab block) with:
|
||||
```hcl
|
||||
# Remote state in Scaleway Object Storage (bucket foxhunt-tfstate, fr-par) — migrated off GitLab 2026-06-21.
|
||||
@@ -125,7 +125,7 @@ Leave the `generate "provider"` block and `inputs`/`locals` untouched.
|
||||
- [ ] **Step 2: Sanity-check the file parses**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
grep -A2 'backend = "s3"' infra/live/root.hcl && grep -c 'backend = "http"' infra/live/root.hcl
|
||||
grep -A2 'backend = "s3"' infra/live/production/root.hcl && grep -c 'backend = "http"' infra/live/production/root.hcl
|
||||
```
|
||||
Expected: shows the `s3` backend line; the `backend = "http"` count is `0`.
|
||||
|
||||
@@ -142,7 +142,7 @@ terragrunt init -migrate-state -force-copy -no-color 2>&1 | sed 's/.*tofu: //' |
|
||||
```
|
||||
Expected: a line like `Terraform has been successfully migrated to the "s3" backend!` (or `Successfully
|
||||
configured the backend "s3"`). **If it errors on checksum** (Scaleway quirk), add `skip_s3_checksum = true`
|
||||
to the `config` block in `infra/live/root.hcl` and re-run this step.
|
||||
to the `config` block in `infra/live/production/root.hcl` and re-run this step.
|
||||
|
||||
- [ ] **Step 2: GATE — plan must show No changes**
|
||||
```bash
|
||||
@@ -205,7 +205,7 @@ Expected: migration success line, then `No changes`. **STOP on any drift.**
|
||||
|
||||
## Task 5: Final verification + commit
|
||||
|
||||
**Files:** Modify (commit) `infra/live/root.hcl`; also commit generated `backend.tf` only if not gitignored.
|
||||
**Files:** Modify (commit) `infra/live/production/root.hcl`; also commit generated `backend.tf` only if not gitignored.
|
||||
|
||||
- [ ] **Step 1: Re-plan all 4 modules from a clean shell** (proves the s3 backend is authoritative)
|
||||
```bash
|
||||
@@ -233,7 +233,7 @@ echo "GitLab TF state is intentionally left in place as a 2A fallback; deleted i
|
||||
- [ ] **Step 4: Commit**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
git add infra/live/root.hcl
|
||||
git add infra/live/production/root.hcl
|
||||
# backend.tf is terragrunt-generated; add only if tracked (usually gitignored)
|
||||
git status --porcelain infra/live/production/*/backend.tf 2>/dev/null
|
||||
git commit -m "chore(infra): migrate Terraform state GitLab -> Scaleway Object Storage (Phase 2A)
|
||||
@@ -248,7 +248,7 @@ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
|
||||
---
|
||||
|
||||
## Rollback (if any module's post-migration plan shows drift)
|
||||
1. Restore the `http` backend block in `infra/live/root.hcl` (git checkout the file).
|
||||
1. Restore the `http` backend block in `infra/live/production/root.hcl` (git checkout the file).
|
||||
2. Re-attach GitLab state: in each affected module, `terragrunt init -migrate-state -force-copy` (copies
|
||||
back s3→http) OR `terragrunt init -reconfigure` (the GitLab state was never deleted).
|
||||
3. If state is corrupted, restore from `/tmp/tfstate-backup-<module>.tfstate` via `terragrunt state push`.
|
||||
@@ -257,7 +257,7 @@ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
|
||||
---
|
||||
|
||||
## Acceptance criteria (from spec)
|
||||
- `infra/live/root.hcl` uses the Scaleway s3 backend; committed. ✅ Task 2 + Task 5.4
|
||||
- `infra/live/production/root.hcl` uses the Scaleway s3 backend; committed. ✅ Task 2 + Task 5.4
|
||||
- All 4 modules: `terragrunt plan = No changes` on the new backend. ✅ Task 3.2, Task 4, Task 5.1
|
||||
- State objects at `foxhunt-tfstate/<module>/terraform.tfstate`. ✅ Task 3.3, Task 5.2
|
||||
- Lock smoke test passes. ✅ Task 3.4
|
||||
|
||||
@@ -24,7 +24,7 @@ OpenTofu v1.11.5 present) — no DynamoDB.
|
||||
|
||||
## Scope — what changes
|
||||
|
||||
1. **`infra/live/root.hcl`** — replace the single `remote_state` block: `backend = "http"` (GitLab) →
|
||||
1. **`infra/live/production/root.hcl`** — replace the single `remote_state` block: `backend = "http"` (GitLab) →
|
||||
`backend = "s3"` (Scaleway). New config:
|
||||
```hcl
|
||||
remote_state {
|
||||
@@ -87,7 +87,7 @@ backend changes.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- `infra/live/root.hcl` uses the Scaleway s3 backend; committed.
|
||||
- `infra/live/production/root.hcl` uses the Scaleway s3 backend; committed.
|
||||
- All 4 modules migrated: each shows `terragrunt plan = No changes` against the new backend.
|
||||
- State objects present in `foxhunt-tfstate` at `<module>/terraform.tfstate` (verified via `scw`/`mc`).
|
||||
- Lock smoke test passes (lock acquired + released, no orphan lockfile).
|
||||
|
||||
@@ -7,21 +7,31 @@ locals {
|
||||
project_id = get_env("SCW_DEFAULT_PROJECT_ID")
|
||||
}
|
||||
|
||||
# Remote state in GitLab Terraform state backend (project root/foxhunt, ID=1)
|
||||
# Token stored in k8s secret gitlab-terraform-state-token
|
||||
# Set TF_HTTP_USERNAME and TF_HTTP_PASSWORD env vars before running terragrunt.
|
||||
# Remote state in Scaleway Object Storage (bucket foxhunt-tfstate, fr-par) — migrated off GitLab 2026-06-21.
|
||||
# The s3 backend reads AWS_* env vars (set them to the Scaleway access/secret keys); SCW_* drives the provider.
|
||||
remote_state {
|
||||
backend = "http"
|
||||
backend = "s3"
|
||||
generate = {
|
||||
path = "backend.tf"
|
||||
if_exists = "overwrite"
|
||||
}
|
||||
config = {
|
||||
address = "${get_env("GITLAB_TF_STATE_URL", "https://tf.fxhnt.ai")}/api/v4/projects/1/terraform/state/${path_relative_to_include()}"
|
||||
lock_address = "${get_env("GITLAB_TF_STATE_URL", "https://tf.fxhnt.ai")}/api/v4/projects/1/terraform/state/${path_relative_to_include()}/lock"
|
||||
unlock_address = "${get_env("GITLAB_TF_STATE_URL", "https://tf.fxhnt.ai")}/api/v4/projects/1/terraform/state/${path_relative_to_include()}/lock"
|
||||
lock_method = "POST"
|
||||
unlock_method = "DELETE"
|
||||
bucket = "foxhunt-tfstate"
|
||||
key = "${path_relative_to_include()}/terraform.tfstate"
|
||||
region = "fr-par"
|
||||
|
||||
endpoints = {
|
||||
s3 = "https://s3.fr-par.scw.cloud"
|
||||
}
|
||||
|
||||
# Scaleway S3-compat: skip AWS-specific preflight calls
|
||||
skip_credentials_validation = true
|
||||
skip_region_validation = true
|
||||
skip_requesting_account_id = true
|
||||
skip_metadata_api_check = true
|
||||
|
||||
# OpenTofu-native lock (no DynamoDB)
|
||||
use_lockfile = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user