Files
factory/doc/runbooks/new-web-app/05-app-terraform.md
Gabriel Radureau 8330d82225 docs(runbooks): add "new web app" setup runbook under doc/runbooks/
Document, as a tree-docs tree, the end-to-end procedure to stand up a new
web application on the Arcodange platform — a mechanic spread across the
factory, tools and app repos with non-trivial ordering dependencies.

Covers: Gitea repo creation (org-secret inheritance), Postgres DB + owner
role (factory/postgres/iac), platform Vault declaration (gitea_cicd_<app>
+ policies, tools/hashicorp-vault/iac), the app Helm chart (VSO dynamic
secrets via pgbouncer), the app Terraform (app_roles module), the CI
workflows (tofu apply + image build, incl. the copy-pasted role pitfall),
and ArgoCD registration (factory/argocd/values.yaml). Adds a naming-
conventions concept page and an ordered checklist.

Wires the legacy doc/adr "setup hello world web app" item and the factory
README to the runbook. New docs live under doc/ (singular) per the PR #8
convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:22:30 +02:00

4.5 KiB

Factory > Doc > Runbooks > Nouvelle application web > 5. Terraform de l'app

5. Le Terraform de l'application

Status: Active Upstream: 2. Base de données, 3. Vault plateforme Downstream: 4. Chart Helm, 6. Workflows CI Related: Conventions de nommage


Summary

Le iac/ du dépôt déclare les ressources Vault propres à l'app : un rôle Postgres dynamique (postgres/creds/<app>), un rôle d'authentification Kubernetes (<app>), et les secrets de config KV. Le gros du travail est fait par un module partagé (app_roles, dans tools) ; le dépôt se contente de l'appeler avec son nom et d'ajouter ses secrets spécifiques.

Les trois fichiers

providers.tf — s'authentifier à Vault avec le rôle CI de l'app

terraform {
  required_providers {
    vault = { source = "vault", version = "4.4.0" }
  }
}
provider "vault" {
  address = "https://vault.arcodange.lab"
  auth_login_jwt {               # JWT fourni par la CI via TERRAFORM_VAULT_AUTH_JWT
    mount = "gitea_jwt"
    role  = "gitea_cicd_<app>"   # ← créé à l'étape 3 ; DOIT exister avant le 1er apply
  }
}

backend.tf — état distant sur GCS, préfixe par app

terraform {
  backend "gcs" {
    bucket = "arcodange-tf"
    prefix = "<app>/main"        # ← un préfixe d'état dédié par app
  }
}

main.tf — appeler le module partagé + secrets de l'app

module "app_roles" {
  source = "git::ssh://git@192.168.1.202:2222/arcodange-org/tools.git//hashicorp-vault/iac/modules/app_roles?depth=1&ref=main"
  name   = "<app>"
  # database = "<autre>"   # optionnel ; par défaut = name
}

# Exemple : secrets de config statiques de l'app, écrits dans kvv2/<app>/config
resource "vault_kv_secret_v2" "config" {
  mount     = module.app_roles.mount_paths.kvv2          # "kvv2"
  name      = format("%sconfig", module.app_roles.kvv2_path_prefix)  # "<app>/config"
  data_json = jsonencode({
    # … clés propres à l'app (ex. erp : DOLI_ADMIN_LOGIN/PASSWORD, DOLI_INSTANCE_UNIQUE_ID) …
  })
}

Ce que le module app_roles crée

modules/app_roles/main.tf :

Ressource Effet
vault_database_secret_backend_rolepostgres/creds/<app> À chaque demande : CREATE ROLE "…" LOGIN PASSWORD … VALID UNTIL … ; GRANT <app>_role TO "…". Le user éphémère hérite de <app>_role (donc des droits sur la base). À la révocation : REASSIGN OWNED … TO <app>_role + REVOKE.
vault_kubernetes_auth_backend_role<app> Lie le SA <app> du namespace <app> aux policies default + <app> (TTL 3600 s). C'est ce que VaultAuth cible (étape 4).

Sorties utiles : mount_paths ({k8s, pg, kvv2}), kvv2_path_prefix (<app>/), name, database.

Dépendances (à respecter)

Important

Ce Terraform suppose que deux choses existent déjà :

  • le rôle Postgres <app>_role (créé à l'étape 2) — sinon le GRANT <app>_role TO … du rôle dynamique est invalide ;
  • le rôle JWT gitea_cicd_<app> et la policy <app> (créés à l'étape 3) — sinon l'authentification du provider échoue / le rôle K8s ne peut référencer la policy.

Ce iac/ est appliqué par la CI de l'app, voir étape 6. Ne pas pousser ce dossier avant d'avoir appliqué les étapes 2 et 3.

Notes / contraintes

  • Le module est récupéré en SSH via le bot tofu_module_reader (cf. étape 1) ; ?ref=main&depth=1 épingle la branche et limite le clone.
  • L'état est isolé par prefix = "<app>/main" : pas de collision entre apps dans le bucket arcodange-tf.
  • erp et webapp montrent deux variantes : erp passe par module "app_roles" ; webapp inline encore les ressources (vault_database_secret_backend_role + vault_kubernetes_auth_backend_role) — préférer le module pour une nouvelle app.