infra/stacks/external-secrets/main.tf
Viktor Barzin c670cb7118
Some checks failed
ci/woodpecker/push/default Pipeline failed
eso: Phase 2 — migrate all 104 ExternalSecrets + 2 ClusterSecretStores to v1
The API rewrite half of the ESO 0.12->2.6 migration (last k8s-1.35 compat-gate
blocker). Done on chart 0.16.2, which serves BOTH external-secrets.io/v1beta1
and v1, so this is the safe window — MUST land before 0.17 removes v1beta1
(there is no conversion webhook). Pure apiVersion bump, schema is byte-identical:
106 occurrences (104 ExternalSecrets + 2 ClusterSecretStores vault-kv/vault-database)
across 73 .tf files, v1beta1 -> v1, no other field changes.

Validated live first on tandoor (single, non-coupled, synced ES): the
kubernetes_manifest apiVersion bump forces a REPLACE; the target Secret is
cascade-GC'd for ONE ~0.3s poll then ESO recreates it (identical value re-synced
from Vault, new UID) and the ES returns SecretSynced=True on v1. Running pods
keep their mounted copy through the sub-second blip. All 110 target Secrets were
snapshotted to /tmp first as a backstop.

CI applies the changed stacks serially (staged rollout); watching aggregate ES
sync back to 108 synced (2 pre-existing dead: instagram-poster, payslip-ingest).
Next: Phase 3 climb 0.16.2 -> 2.6.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 19:13:04 +00:00

92 lines
2.6 KiB
HCL

resource "kubernetes_namespace" "external_secrets" {
metadata {
name = "external-secrets"
labels = {
tier = local.tiers.cluster
"keel.sh/enrolled" = "true"
}
}
lifecycle {
# KYVERNO_LIFECYCLE_V1: goldilocks-vpa-auto-mode ClusterPolicy stamps this label on every namespace
ignore_changes = [metadata[0].labels["goldilocks.fairwinds.com/vpa-update-mode"]]
}
}
resource "helm_release" "external_secrets" {
name = "external-secrets"
namespace = kubernetes_namespace.external_secrets.metadata[0].name
repository = "https://charts.external-secrets.io"
chart = "external-secrets"
# ESO 0.12->2.6 migration (2026-06-21, docs/plans/2026-06-21-eso-0.12-to-2.x-migration-design.md).
# Stepped one minor at a time on k8s 1.34; rewrite all 104 CRs v1beta1->v1 at 0.16.2 before 0.17.
version = "0.16.2"
# Added for the migration: auto-rollback a failed hop's helm upgrade (ESO had no
# rollback safety net) and wait for the controller Deployment to be Ready first.
atomic = true
timeout = 600
values = [yamlencode({
installCRDs = true
})]
}
# --- ClusterSecretStore for Vault KV v2 ---
resource "kubernetes_manifest" "css_vault_kv" {
manifest = {
apiVersion = "external-secrets.io/v1"
kind = "ClusterSecretStore"
metadata = { name = "vault-kv" }
spec = {
provider = {
vault = {
server = "http://vault-active.vault.svc.cluster.local:8200"
path = "secret"
version = "v2"
auth = {
kubernetes = {
mountPath = "kubernetes"
role = "eso"
serviceAccountRef = {
name = "external-secrets"
namespace = "external-secrets"
}
}
}
}
}
}
}
depends_on = [helm_release.external_secrets]
}
# --- ClusterSecretStore for Vault Database Engine ---
resource "kubernetes_manifest" "css_vault_db" {
manifest = {
apiVersion = "external-secrets.io/v1"
kind = "ClusterSecretStore"
metadata = { name = "vault-database" }
spec = {
provider = {
vault = {
server = "http://vault-active.vault.svc.cluster.local:8200"
path = "database"
version = "v1"
auth = {
kubernetes = {
mountPath = "kubernetes"
role = "eso"
serviceAccountRef = {
name = "external-secrets"
namespace = "external-secrets"
}
}
}
}
}
}
}
depends_on = [helm_release.external_secrets]
}