L'enfer du décor : Quand le YAML cesse d'être simple
J'ai vu des équipes entières passer trois jours à débugger une indentation d'espace manquante dans un fichier CloudFormation de quatre mille lignes. Le format YAML, conçu à l'origine pour être une simple syntaxe de sérialisation de données conviviale, est devenu par accident le moteur de l'Infrastructure as Code. Aujourd'hui, nous poussons cet outil au-delà de ses limites physiques en y intégrant des boucles logiques artificielles, des variables complexes et des fonctions de chaînage qui le transforment en un langage de programmation illisible et non typé.
Le mirage de la simplicité déclarative
Au début d'un projet, le choix du YAML ou du HCL de Terraform semble évident car il permet de décrire l'état cible de l'infrastructure de manière statique. Cependant, dès que l'infrastructure grandit, le besoin de dynamicité apparaît et c'est là que le piège se referme. On se retrouve à utiliser des fonctions de templating complexes comme templatefile() ou des boucles de type for_each imbriquées pour générer dynamiquement des blocs de configuration réseau.
Cette complexité accidentelle transforme vos fichiers déclaratifs en un code impératif déguisé, dépourvu de toutes les protections modernes des vrais langages. Vous n'avez pas de compilateur pour détecter une erreur de type avant l'exécution, pas d'autocomplétion native efficace dans votre IDE, et aucun moyen simple de réutiliser le code sans dupliquer des blocs entiers, violant ainsi le principe fondamental du DRY (Don't Repeat Yourself).
La complexité accidentelle des structures répétitives
Prenons un exemple concret : la création de sous-réseaux multi-az avec des règles de routage spécifiques. En YAML statique, cela se traduit souvent par des centaines de lignes redondantes où la moindre modification de masque CIDR nécessite un copier-coller massif sujet aux erreurs humaines. Les équipes tentent de contourner cela avec des préprocesseurs ou des moteurs de templates externes, ajoutant une couche d'outillage supplémentaire sur une pile technique déjà surchargée.
Le piège des préprocesseurs
L'utilisation de générateurs de YAML tiers crée une double complexité : vous devez maintenir le code du générateur ET déboguer le YAML généré qui ne correspond plus directement à vos fichiers sources.
La révolte des développeurs : L'avènement des CDKs
Le typage statique au secours des infrastructures
Face à cette impasse, une transition majeure s'opère vers l'utilisation de véritables langages de programmation via les frameworks de type CDK (Cloud Development Kit) et Pulumi. En écrivant votre infrastructure en TypeScript, Python ou Go, vous bénéficiez immédiatement de la puissance des compilateurs et des outils d'analyse statique de code. Une erreur de configuration sur un paramètre de base de données est détectée directement dans votre éditeur de code, avant même d'initier le moindre déploiement.
Le code suivant illustre comment l'utilisation de Pulumi avec TypeScript permet de définir des ressources de manière modulaire, typée et réutilisable, éliminant ainsi les risques d'erreurs de syntaxe propres aux structures imbriquées en YAML.
import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";
// Définition d'un composant d'infrastructure réutilisable et fortement typé
interface NetworkArgs {
vpcCidr: string;
publicSubnets: string[];
}
class StandardNetwork extends pulumi.ComponentResource {
public readonly vpcId: pulumi.Output<string>;
constructor(name: string, args: NetworkArgs, opts?: pulumi.ComponentResourceOptions) {
super("custom:network:StandardNetwork", name, {}, opts);
const vpc = new aws.ec2.Vpc(`${name}-vpc`, {
cidrBlock: args.vpcCidr,
enableDnsSupport: true,
enableDnsHostnames: true,
}, { parent: this });
args.publicSubnets.forEach((cidr, index) => {
new aws.ec2.Subnet(`${name}-public-sub-${index}`, {
vpcId: vpc.id,
cidrBlock: cidr,
mapPublicIpOnLaunch: true,
}, { parent: this });
});
this.vpcId = vpc.id;
this.registerOutputs({ vpcId: vpc.id });
}
}
Le match : Terraform HCL vs Pulumi TypeScript
Le choix entre l'approche déclarative classique et l'approche par programmation dépend fortement de la maturité de vos équipes. Le tableau suivant met en évidence les compromis réels que vous devez accepter lors de cette transition technologique.
- Validation au build : Impossible en YAML/HCL (détection uniquement au moment du plan/apply), alors qu'elle est native et immédiate avec un compilateur TypeScript ou Go.
- Tests unitaires : Limités à des validations de schémas tiers en YAML, contre l'utilisation de frameworks standards comme Jest ou Mocha en CDK.
- Courbe d'apprentissage : Faible pour les profils système avec le YAML, mais élevée pour l'écriture de code orienté objet propre en CDK.
- Gestion de l'état : Identique dans les deux mondes, mais les risques de dérive d'état (**State Drift**) sont plus complexes à appréhender lorsque le code de génération est dynamique.
Les pièges cachés des langages de programmation en IaC
Le cauchemar du débogage en production
Si l'utilisation de vrais langages de programmation apporte une flexibilité inégalée, elle introduit également des risques inédits pour les équipes opérationnelles. Lorsque votre code d'infrastructure utilise des appels d'API externes ou des conditions asynchrones au moment de la génération du graphe de ressources, le comportement du déploiement devient non déterministe. J'ai vu des pipelines de production échouer de manière aléatoire parce qu'un script de déploiement dépendait d'une bibliothèque tierce mise à jour à la volée.
Le schéma ci-dessus illustre le cycle de défaillance classique d'un déploiement basé sur un code impératif mal maîtrisé. Bien que la phase de compilation locale et de tests unitaires se déroule avec succès, la génération du plan d'infrastructure peut échouer lors de l'exécution dans le pipeline d'intégration continue si des dépendances externes ou des états asynchrones introduisent des divergences imprévues avec les API cloud réelles.
Le journal d'erreur suivant montre un cas réel d'échec de déploiement dû à une promesse non résolue lors de la configuration dynamique d'un groupe de sécurité réseau.
Type: aws:ec2:SecurityGroup
Name: web-sg
Status: failed
Info:
error: Program run completed with an unhandled exception:
Error: Calling getSubnetOutput() inside a resource constructor without awaiting the promise.
at /infra/node_modules/@pulumi/aws/ec2/getSubnet.js:12:32
at processTicksAndRejections (node:internal/process/task_queues:95:5)
Résultat:
Pipeline terminated with exit code 1. Infrastructure state is partially updated. Manual recovery required.
Quand la liberté du développeur détruit la standardisation
Le plus grand danger des CDKs réside dans la perte de lisibilité pour les ingénieurs système. Si chaque développeur conçoit ses propres abstractions logicielles avec des concepts de programmation orientée objet complexes, l'infrastructure devient une boîte noire. Un administrateur système senior doit pouvoir comprendre instantanément l'architecture déployée sans devoir naviguer à travers trois niveaux d'héritage de classes TypeScript.
Trouver l'équilibre entre rigueur et flexibilité
La règle d'or de l'abstraction raisonnable
Pour éviter de tomber dans l'un ou l'autre de ces extrêmes, les organisations modernes adoptent une approche hybride. Le YAML doit rester l'interface de configuration finale pour les déploiements applicatifs quotidiens, tandis que les langages de programmation réels comme TypeScript ou Go doivent être réservés exclusivement à la création de modules de base réutilisables et hautement standardisés par l'équipe plateforme.
Cette séparation claire des responsabilités permet de bénéficier du typage fort et de la puissance de test des CDKs pour concevoir les fondations de votre cloud, tout en offrant aux développeurs applicatifs des fichiers de configuration YAML simples, statiques et sans logique complexe à manipuler au quotidien pour leurs déploiements via des outils de **GitOps**.
Espace commentaire
Écrire un commentaire
Rejoignez la discussion
Vous devez être connecté pour poster un message.
20 commentaires
Le danger c'est l'encapsulation. Si tu masques tout derrière une classe, personne ne sait ce qui est créé réellement dans le cloud.
terraform showau moins, ça montre la réalité.C'est une opinion tranchée mais compréhensible. Cependant, l'autocomplétion IDE et le typage statique sont des gains de productivité réels que personne ne peut nier.
Je bosse en ops depuis 15 ans, j'ai vu passer les templates, le cloud-init, le YAML... Le CDK, c'est juste la promesse de pouvoir coder son infra, mais ça reste une illusion de productivité.
Le problème c'est pas le YAML, c'est l'absence de tests. Si vous écriviez des tests pour votre YAML, vous n'auriez pas besoin de passer à TypeScript.
Le HCL est top, mais dès que tu as besoin de logique métier (ex: conditionner la création d'un bucket selon l'environnement), tu te retrouves avec des
countou desfor_eachillisibles. C'est là que le CDK prend tout son sens.Pour moi, le HCL reste le meilleur compromis. C'est du déclaratif typé, sans la complexité inutile d'un compilateur TypeScript. Ne réinventez pas la roue.
J'ai vu des projets où le code CDK est devenu si complexe qu'ils ont fini par générer du YAML à partir du code... On a bouclé la boucle de l'absurde.
Je suis d'accord pour les déploiements applicatifs. L'idée est de réserver le CDK à la plateforme, pas aux devs qui déploient leurs microservices tous les jours.
Totalement d'accord. Le GitOps avec du YAML simple reste la seule méthode viable pour éviter que l'infra ne devienne un monolithe incontrôlable.
Le problème de fond c'est que les devs veulent traiter l'infrastructure comme une application. Sauf que l'infra, ça se répare pas avec un
git revertrapide quand ça pète à 3h du mat.Excellent exemple. C'est exactement l'équilibre dont je parle : utiliser la puissance du langage pour la structure, mais garder les valeurs critiques en constantes ou fichiers de config externes.
Le vrai souci c'est la dérive. En YAML déclaratif, l'état est prévisible. En CDK, si tu mets des boucles
forpartout, tu ne sais plus ce qui est réellement déployé. Voici ce qu'on fait pour limiter la casse :La lisibilité est un argument solide, mais le coût de maintenance est caché. Combien de temps passes-tu à vérifier manuellement que tes 10 environnements sont synchronisés ? Le code permet de garantir cette standardisation par conception.
Le YAML a au moins le mérite de la lisibilité universelle. N'importe quel ops peut lire un
deployment.yaml. Avec vos abstractions en TypeScript, c'est devenu de la magie noire.On a testé Pulumi, on est revenus en arrière. Le débogage des promesses non résolues comme tu le montres avec ton erreur
getSubnetOutputest un enfer pour une équipe ops qui n'a pas fait de JS depuis 5 ans.C'est un risque réel, je l'ai mentionné. La solution n'est pas de bannir les langages, mais d'encapsuler la logique dans des modules testés. Si tu ne fais pas de tests unitaires sur ton infra, tu joues au casino en prod.
Le typage statique c'est bien beau, mais le jour où ton pipeline plante à cause d'une mise à jour de dépendance npm dans ton code d'infra, tu vas regretter la stabilité du HCL.
C'est exactement ça. Le problème du CDK, c'est que tu donnes le contrôle total à des devs qui ne comprennent pas forcément les implications réseau. On finit avec des abstractions partout où personne ne peut débugger le
terraform planfinal.Je ne dis pas que le YAML est inutile, je dis qu'il atteint ses limites dès qu'on veut de la réutilisabilité. Quand tu dois copier-coller 50 fois un bloc
aws_subnet, tu finis par créer des erreurs humaines inévitables.Encore un article qui prône le passage aux CDK juste pour éviter d'apprendre à structurer ses fichiers HCL. Si tu as 4000 lignes dans un seul fichier, c'est pas le YAML le problème, c'est ton architecture.