Saltar a contenido

ADR-0006 · IaC y CI/CD: Terraform monorepo + GitHub Actions OIDC + trunk-based

  • Status: Accepted (revisado 2026-05-13)
  • Date: 2026-04-30 · Revisado: 2026-05-13
  • Deciders: Cristian Fernández (Zerviz Group)
  • Related: ADR-0002 (tenancy), docs/discovery/00-phase1-prerequisites.md 4.1–4.4 + Anexo A-05.

Revisión 2026-05-13 — Cuenta única + prefijo zen-

Cambio de alcance aprobado: Fase 2 se construye sobre la cuenta existente 450972188274 (no se crea cuenta ze-dev aún). Promoción a cuenta dedicada se hace después de la primera versión revisada.

  • Naming: zen-{env}-<resource-type>-<purpose> para todos los recursos nuevos (diferenciado del prefijo legacy ze-* que no se toca).
  • Tags obligatorios: Project=zengine-v2, ManagedBy=terraform, Environment={dev|qa|prod}, Service=<bounded-context>, Tenant={shared|<uuid>}, CostCenter, DataClassification.
  • Aislamiento lógico: IAM policies de los roles zen-{env}-github-deployer con Condition: aws:ResourceTag/Project=zengine-v2, para impedir tocar recursos ze-* legacy.
  • AWS Organizations / OUs: diferido a fase posterior; el bloque "Cuentas AWS" de abajo se mantiene como referencia para la promoción futura.
  • Backend Terraform: zen-terraform-state-{env} + zen-terraform-locks-{env} (S3+DDB en la misma cuenta).
  • Branch protection diferida (2026-05-13): la org zerviz está en plan Free y GitHub no permite branch protection ni rulesets sobre repos privados Free. Compensación temporal: PRs por convención + revisión humana antes del merge. El script scripts/setup-branch-protection.sh queda listo para correr apenas se upgrade a GitHub Team (requerimiento antes de tocar prod).
  • Dominios públicos (sobre zervizdev.com propiedad del owner; Route53 hosted zone gestionada en infra/global/dns/):
  • Frontal cliente: zen.zervizdev.com (prod), dev.zen.zervizdev.com, qa.zen.zervizdev.com → SPA cliente, APIs públicas y webhooks externos.
  • Admin interno: zen-admin.zervizdev.com (prod), dev.zen-admin.zervizdev.com, qa.zen-admin.zervizdev.com → consola admin (builder, reporting).
  • Cognito Hosted UI: auth.{env}.zen.zervizdev.com (ACM certs en us-east-1 para CloudFront/Cognito).
  • Los dominios legacy *.zengine.online siguen sirviendo al legacy y no se tocan.

Context

Asentado: Terraform como IaC, backend S3+DDB lock (patrón Lab golden path), GitHub Actions con OIDC, trunk-based con envs dev|qa|prod, monorepo único zerviz/zengine-platform con infra/envs/{dev,qa,prod}/.

Decision

Repositorio

  • Único monorepo zerviz/zengine-platform. Origen actual ZEngine_unificado se publica allí en cuanto Fase 1 cierre.
  • Workspaces npm: services/*, packages/*, apps/*. Build via turbo run build en CI.

Terraform layout

infra/
├── modules/                  # Reutilizable, sin estado propio
├── envs/{dev,qa,prod}/       # Stacks por env, cada uno con su backend
├── global/                   # IAM, KMS keys de plataforma, Route53, OIDC
└── tenants/<id>/             # Stacks tier físico, aplicados con var tenant_id
  • Backend por stack: S3 ze-terraform-state-<env> + DDB lock ze-terraform-locks-<env> (réplica del patrón Lab lab-ms-terraform-state / lab-ms-terraform-locks).
  • Provider versions locked en versions.tf por stack.
  • Workspace strategy: un workspace por stack (no terraform workspace mezclado). Cada infra/envs/<env>/ tiene su backend.tf propio.
  • Naming/Tags estándar (asentado checklist R-09..R-14):
  • Tags obligatorios (ver Revisión 2026-05-13): Project=zengine-v2, Environment={dev|qa|prod}, Tenant={shared|control-plane|<uuid>}, Service=<bounded-context>, CostCenter, DataClassification, ManagedBy=terraform.
  • Naming: zen-{env}-<resource-type>-<purpose> (prefijo zen- para diferenciar del legacy ze-*).
  • Linters obligatorios en PR: terraform fmt -check, terraform validate, tflint, tfsec, checkov. Falla bloquea merge.

Cuentas AWS (diferido a fase posterior — ver Revisión 2026-05-13)

  • Crear cuentas dedicadas vía AWS Organizations:
  • ze-platform (root org, sólo billing/policies).
  • ze-dev (cuenta dev, primera prioridad).
  • ze-qa, ze-prod (creadas cuando dev quede consolidado).
  • ze-prod-tenant-<id> para cada tenant físico.
  • Stack infra/global/organizations/ define OUs Workloads/{Dev,QA,Prod,Tenants} con SCPs (Service Control Policies) que prohíben acciones destructivas en prod sin break-glass role.

CI/CD (GitHub Actions + OIDC)

  • OIDC trust: rol ze-{env}-github-deployer en cada cuenta AWS, asumido por GH Actions vía aws-actions/configure-aws-credentials@v4 con condition token.actions.githubusercontent.com:sub = repo:zerviz/zengine-platform:ref:refs/heads/main (o tag específico para prod).
  • Workflows:
  • ci.yml (PR): lint TS + unit tests (Jest) + tfsec + checkov + semgrep p/owasp-top-ten,p/nodejs + npm audit --omit=dev. Sin AWS access.
  • cd-dev.yml (merge a main): plan + apply Terraform infra/envs/dev/ + build + deploy Lambdas + run integration tests LocalStack. Auto.
  • cd-qa.yml: aprobación manual + plan + apply qa + smoke tests + parity tests fixture-based (gate del cutover).
  • cd-prod.yml: aprobación manual de 2 personas + plan + apply prod + canary 10% → 50% → 100% por servicio + rollback automático si error rate > 1%.
  • tenant-onboard.yml (workflow_dispatch): aprovisiona stack infra/tenants/<id>/ para tier físico. Inputs: tenant_id, tier=physical, aws_account_id.
  • parity-gate.yml (scheduled + manual): corre tests/parity/ contra el ambiente nuevo y emite report. Bloquea cutover si falla.

Branching (asentado 4.4)

  • Trunk-based: main siempre desplegable.
  • Feature branches feat/<scope>-<desc> con PR mandatorio. Merge sólo via squash. Lint/CI mandatorio.
  • Hotfix: branch hotfix/<issue> desde el tag prod, deploy directo a prod con aprobación de emergencia.
  • Tags: v<major>.<minor>.<patch> SemVer en commits que llegan a prod. Release notes auto-generadas por release-please.

Migración legacy → nuevo (asentado 5.1 cutover total)

  • Ambiente actual 450972188274 queda frozen desde el día 1 de Fase 2 (no se aceptan PRs que toquen aws/step-1-read-api/).
  • Cutover: cuando parity-gate.yml esté verde + smoke tests qa + aprobación humana, se hace switch DNS / API GW custom domain en una ventana de mantenimiento.
  • Rollback plan: DNS revierte al endpoint legacy; el ambiente legacy permanece intacto durante 90 días post-cutover.

Consequences

Positive

  • Drift IaC eliminado (R-04). Todo cambio pasa por PR + plan + apply automatizado.
  • OIDC elimina secrets de larga vida en GH (no más AWS_ACCESS_KEY_ID en secrets de repo).
  • Trunk-based + canary + auto-rollback reducen riesgo en prod.

Negative

  • Inversión inicial alta (~2 sprints sólo para módulos Terraform + workflows).
  • Devs deben aprender Terraform + workflow gating.

Alternatives considered

  • AWS CDK: evaluado, descartado por preferencia explícita por Terraform (consistencia con Lab + ecosistema).
  • GitFlow: rechazado, complica releases en SaaS multi-tenant.
  • Terraform Cloud: rechazado por costo + ya tenemos S3+DDB pattern probado.
  • CodePipeline: rechazado, GH Actions ya es la fuente y OIDC es first-class.

Tenant-isolation impact

  • Stack tier físico aplicado en cuenta del tenant ⇒ aislamiento Terraform-level.
  • tenant-onboard.yml requiere aprobación manual + audit log.

Blast radius

  • SCPs en OU prod previenen iam:DeleteRole, s3:DeleteBucket, kms:ScheduleKeyDeletion salvo break-glass role.
  • Apply prod requiere 2-person approval.

Cost note

  • GitHub Actions: free para org pequeño; > 2k min/mes ~ USD 0.008/min.
  • AWS Organizations: gratis. Cuentas adicionales: gratis (paga su propio uso).
  • Terraform state S3+DDB: < USD 1/mes por env.

ISO 27001 controls touched

  • A.12.1.2 (gestión de cambios): PR + plan obligatorio.
  • A.12.1.4 (separación de entornos): cuentas AWS distintas dev/qa/prod.
  • A.12.6.1 (gestión de vulnerabilidades técnicas): tfsec + checkov + semgrep + dependabot.
  • A.14.2.2 (procedimientos de control de cambios): trunk-based + canary + rollback.

Sources

  • docs/discovery/00-phase1-prerequisites.md 4.1, 4.2, 4.3, 4.4, 5.1, 5.3 + Anexo A-05.
  • docs/discovery/07-lab-golden-path.md (patrón Terraform).
  • AWS docs: AWS Organizations, OIDC for GitHub Actions, SCPs.