Salut, bande de hackers des internets ! 🦊

Aujourd'hui, on va parler d'un truc que tout le monde repousse parce que « ça marche déjà » : la gestion des DNS. Tu sais, ces enregistrements que tu bidouilles à la main dans le dashboard Cloudflare, à 23h, en croisant les doigts pour ne pas flinguer ton mail en supprimant le mauvais TXT. Spoiler : j'ai arrêté de flipper. Et c'est Terraform qui m'a sauvé.

Le DNS géré à la souris, ce petit stress permanent

Mon setup perso, c'est 5 zones (stranix.net, code79.com, dbfactory.io, et deux-trois autres) et une bonne cinquantaine d'enregistrements. Des A, des CNAME par paquets, du MX, du TXT pour le SPF/DKIM… le bazar habituel.

Le problème, ce n'est pas de créer un record. C'est tout le reste :

  • Aucun historique. « C'était quoi déjà, l'ancienne valeur de ce CNAME ? » → aucune idée.
  • Aucun filet. Un clic de trop sur 🗑️ et ton service est down, sans undo.
  • Aucune revue. Tu appliques direct en prod, à l'aveugle, en te disant « ça devrait passer ».

Si tu as lu mon article Adieu DynDNS, tu sais déjà que je bricole mes DNS Cloudflare au script pour mon IP dynamique. Là, on passe carrément la vitesse au-dessus : gérer toute la config DNS en Infrastructure as Code.

De l'Infra-as-Code sur un truc « aussi bête » que des DNS ? Sérieux ?

Oui, et justement parce que c'est bête et en prod. Un DNS qui casse, ça casse en silence : le site répond encore… jusqu'à ce que le TTL expire et que le téléphone sonne. Mettre ça dans Terraform, c'est gagner trois trucs magiques : un git diff avant chaque changement, un historique complet dans Git, et un rollback en une commande. Bref, un vrai filet.

(Petit disclaimer : je fais ça en perso. Au taf j'ai d'autres outils. Mais le principe est identique.)

La mise en place — et LE piège qui m'a bouffé une heure

Côté structure, rien de sorcier. Un fichier pour le provider, un pour les variables, une petite table des zones, et un fichier d'enregistrements par domaine. On épingle le provider en v4 (la 5.x a des breaking changes, on évite) :

terraform {
  required_version = ">= 1.5"
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 4.0"   # 4.x : la 5.x casse des trucs
    }
  }
}

Les zones, c'est juste une table nom → ID (les IDs Cloudflare ne sont pas des secrets) :

locals {
  zones = {
    "stranix.net"  = "8c2b3262…"
    "code79.com"   = "8cc5269f…"
    "dbfactory.io" = "4043bbb8…"
    # …
  }
}

Et là, premier terraform plan… et ça pète. Une erreur cryptique, aussi bavarde qu'un chat qui te regarde depuis le frigo :

Erreur Terraform Authentication error 10000

Le token est bon. Les droits Zone:DNS:Edit sont là. Je régénère. Toujours 10000. Je régénère encore. Toujours pareil. Trois tokens plus tard, la révélation : le provider Cloudflare v4 refuse les tokens de compte (ceux qui commencent par cfat_) et veut un token utilisateur (cfut_), à créer depuis Mon Profil → Jetons API. Toute cette galère… pour un préfixe. Un underscore. Voilà voilà. 🤦

Importer sa prod sans rien casser (le moment qui serre les fesses)

Deuxième subtilité : mes 54 records existent déjà chez Cloudflare. Hors de question de les recréer (bonjour le downtime). La bonne méthode, c'est terraform import : on aspire l'existant dans le state, sans rien toucher côté Cloudflare. J'ai même utilisé un token en lecture seule pour cette étape — ceinture et bretelles.

# import.sh — on aspire l'existant dans le state
terraform import cloudflare_record.stranix_hawking_a \
  "8c2b3262…/88ff9271…"
# … × 54

Ensuite, on repasse au token cfut_ avec les droits d'écriture, et on lance le plan. Et c'est qu'on respire :

terraform plan : 0 to add, 52 to change, 0 to destroy

Le réflexe qui sauve : tu lis le plan et tu vérifies qu'il n'y a aucun - (destroy) ni replace. Ici : 0 to destroy. Les ~ change, c'est juste Terraform qui normalise les records importés (il ajoute des champs par défaut). Inoffensif. Tu peux apply l'esprit tranquille.

Petit détail qui boucle la boucle avec l'article DynDNS : mon home.stranix.net est volontairement exclu de Terraform. Lui, c'est mon script DDNS qui le met à jour tout seul quand mon IP change. Chacun son job.

Ce que ça change, concrètement

  • Mes DNS sont dans Git. Chaque modif = un commit, avec message et date. Fini le « qui a touché à ça ? ».
  • Avant chaque changement, terraform plan me montre exactement ce qui va bouger. Plus jamais à l'aveugle.
  • Une bourde ? git revert + apply, et je suis revenu à l'état d'avant. Le rollback à portée de main.
  • Et cette petite peur de casser la prod ? Envolée.

Et après ?

Là, je lance encore mes plan/apply à la main depuis mon terminal. La prochaine étape, c'est de mettre tout ça dans un repo privé avec une CI qui fait le plan sur chaque Pull Request et applique tout seul une fois mergé (coucou Atlantis et GitHub Actions). Le GitOps pour les DNS, quoi. Mais ça, ce sera pour un prochain article. 😏

En attendant : si tes DNS vivent encore uniquement dans un dashboard, tu sais ce qu'il te reste à faire ce week-end. Ton toi-du-futur te remerciera le jour où tu casseras un truc (parce que tu casseras un truc, on casse tous un truc).

À bientôt pour un nouveau tuto geek-friendly ! 🚀

— le renard 🦊