Skip to main content
GenioCT

Bicep vs Terraform, pourquoi Terraform est notre choix par défaut (et quand Bicep l'emporte)

Par GenioCT | | 9 min de lecture
Azure Terraform Bicep DevOps

Dans cet article

Bicep est Azure-native et étroitement intégré, tandis que Terraform offre une portée multi-cloud et des déploiements cross-resource-group.

La plupart des articles comparatifs listent les fonctionnalités côte à côte et vous disent de “choisir le bon outil pour le bon besoin.” Cet article n’est pas de ceux-là.

Nous utilisons Bicep et Terraform pour le même client enterprise, sur la même plateforme Azure, depuis plus d’un an maintenant. Pas un proof of concept, pas une démo de blog : des landing zones en production avec Bicep, de l’infrastructure produit avec Terraform, les deux ciblant les mêmes subscriptions. En janvier 2024, nous avons organisé un workshop IaC interne où toute l’équipe plateforme a passé en revue les deux solutions côte à côte, comparé forces et faiblesses en direct, et tiré des conclusions qui nous ont surpris. (Le volet humain de ce workshop a donné lieu à son propre article sur la gestion des désaccords techniques.)

Voici ce que nous pensons réellement.

Notre choix par défaut, c’est Terraform. Voici pourquoi.

Terraform n’est pas parfait. Nous aborderons ses problèmes. Mais quand nous démarrons un nouveau projet d’infrastructure, c’est Terraform que nous choisissons, et voici les raisons.

Les Data Blocks changent tout

La plupart des articles comparatifs ne mentionnent même pas ce point. Les data blocks de Terraform vous permettent de lire l’infrastructure existante comme des objets de première classe dans votre code :

data "azurerm_resource_group" "rg" {
  for_each = toset(local.rg_names)
  name     = each.value
}

resource "azurerm_virtual_network" "this" {
  for_each            = local.vnets
  resource_group_name = each.value.resource_group

  # Inherit tags from the resource group automatically
  tags = merge(
    data.azurerm_resource_group.rg[each.value.resource_group].tags,
    try(each.value.tags, {}),
    local.tags
  )
}

Vous pouvez interroger un Key Vault, lire ses access policies, récupérer un subnet ID depuis une autre subscription, extraire les tags d’un resource group, le tout sans rien hardcoder. Avec Bicep, vous utilisez le mot-clé existing pour une partie de ces cas, mais il est limité au scope du déploiement en cours et n’atteint pas la flexibilité des data sources Terraform à travers les providers et les subscriptions.

Lors de notre workshop, un membre de l’équipe a qualifié les data blocks de “gros, gros avantage” de Terraform. Après un an de travail quotidien avec les deux outils, cet avis n’a fait que se renforcer.

Vous n’êtes pas limité à un Resource Group

Les déploiements Bicep sont scopés. Vous choisissez un scope de déploiement (resource group, subscription, management group) et tout dans ce déploiement cible ce scope. En pratique, la plupart des déploiements Bicep ciblent un seul resource group.

Terraform n’a pas cette limitation. Un seul stack Terraform peut créer des resource groups, déployer des ressources dans plusieurs resource groups, configurer du peering cross-subscription et gérer des DNS zones, le tout en un seul plan. Cela compte dès que votre infrastructure dépasse les limites d’un resource group, ce qui arrive plus vite que vous ne le pensez.

Avec Bicep, si vous avez besoin de ressources dans deux resource groups différents, il vous faut deux déploiements, et vous devez gérer l’ordonnancement des dépendances vous-même via votre pipeline. Avec Terraform, vous exprimez la dépendance dans le code et terraform apply fait le reste.

L’écosystème compte, même sur un seul cloud

Oui, Terraform fonctionne sur AWS, GCP et Azure. Mais même si vous êtes 100% Azure, le catalogue de providers compte. Nous gérons le DNS sur Cloudflare, le monitoring dans Datadog, les ressources CI/CD dans Azure DevOps et les manifestes Kubernetes, le tout depuis Terraform. Bicep ne peut toucher à rien de tout cela.

Là où Bicep gagne vraiment

Terraform est notre choix par défaut, mais Bicep a mérité sa place sur la même plateforme. Le rejeter serait malhonnête.

Configuration as Code avec JsonNet

Nos landing zones Bicep utilisent JsonNet comme couche de configuration par-dessus. Un fichier de config par type de ressource, un fichier pour tous les environnements. Vous voulez ajouter un environnement de test ? Ajoutez une entrée dans la config, pas un nouveau fichier. L’équipe Bicep ne duplique rien.

Avec Terraform, nous devions initialement créer des fichiers de config séparés par environnement. C’est une vraie faiblesse : pour six environnements, cela fait six fichiers au contenu quasi identique. Terragrunt résout une partie du problème, et notre approche YAML-driven l’a encore réduit, mais Bicep avec JsonNet avait résolu cela dès le premier jour.

Les fichiers de config vides sont une fonctionnalité (et un risque)

Un pattern qui nous a impressionnés dans la solution Bicep : certaines ressources se déploient avec un fichier de configuration vide. Le stack resource group, par exemple, ne nécessite aucun paramètre parce que tous les defaults sont dérivés des paramètres communs (subscription, convention de nommage, tags). Vous commitez un fichier vide et vous obtenez un resource group correctement configuré.

Élégant, mais cela masque l’intention. Si vous regardez un fichier de config vide sans connaître le code derrière, vous n’avez aucune idée de ce que vous allez obtenir. Il n’y a pas de source of truth unique visible dans la config. La connaissance réside dans les modules Bicep. Pour l’équipe qui l’a écrit, c’est parfait. Pour l’équipe qui en hérite, c’est un manque de documentation.

Support Azure des ressources dès le jour zéro

Bicep compile vers des ARM templates. Quand Microsoft publie une nouvelle version d’API le mardi, Bicep peut l’utiliser le mardi même. Le provider AzureRM de Terraform met des semaines, voire des mois, à rattraper son retard. Pour les équipes qui adoptent les tout derniers services Azure, ce décalage est réel.

Vous pouvez contourner cela avec le provider azapi, mais cela revient à écrire des définitions de ressources brutes au format ARM dans Terraform, ce qui annule l’intérêt de l’abstraction du provider. Nous avons utilisé azapi pour Azure Functions Flex Consumption parce que le provider AzureRM ne le supportait tout simplement pas encore. Cela fonctionne, mais ce n’est pas agréable.

Stabilité pour les Landing Zones

Nos landing zones Bicep sont passées par l’implémentation, les tests et la production. Elles déploient plusieurs landing zones avec succès et sont stables depuis plus d’un an. Quand quelque chose fonctionne et a fait ses preuves, “réécrivons-le en Terraform” n’est pas un argument convaincant. Nous avons eu exactement cette discussion lors du workshop, et la réponse était claire : garder Bicep pour la couche plateforme.

Du Terraform mal écrit est pire que du bon Bicep

Soyons directs. Terraform vous donne plus de puissance, mais la puissance sans discipline crée un désordre plus difficile à débugger que tout ce que Bicep peut produire.

Dans notre setup, la partie la plus complexe est la couche de transformation locals.tf, où la configuration YAML est traduite en flat maps que le for_each de Terraform attend. Cela nécessite d’aplatir des structures imbriquées, de fusionner des defaults, de collecter des noms de resource groups pour les data lookups. Comme l’a dit un membre de l’équipe lors du workshop : “il faut faire du massaging pour préparer tous les différents fichiers de config à être ingérés dans Terraform.”

# This is where Terraform complexity lives
locals {
  subnets = {
    for s in flatten([
      for v in values(local.vnets) : [
        for sn in try(v.subnets, []) : merge(sn, {
          vnet_name      = v.name
          resource_group = v.resource_group
        })
      ]
    ]) : "${s.vnet_name}/${s.name}" => s
  }
}

Si votre équipe écrit cela correctement, c’est puissant et maintenable. Sinon, vous obtenez des expressions for imbriquées que personne ne peut lire, des state files impossibles à démêler et un plan output qui demande 20 minutes de concentration pour être compris. À ce stade, un module Bicep propre avec des defaults sensés aurait été le meilleur choix.

Terraform récompense le bon engineering et punit le mauvais engineering plus sévèrement que Bicep. Le scope plus restreint de Bicep et son modèle de déploiement plus simple font office de garde-fous. Terraform n’a pas de garde-fous, sauf si vous les construisez vous-même.

Le modèle dual-track qui fonctionne vraiment

Après le workshop, l’équipe s’est mise d’accord sur un modèle que nous appliquons avec succès depuis :

Bicep gère la couche plateforme : landing zones, resource groups, networking de base, policy assignments. Ceux-ci sont déployés une fois (ou très rarement modifiés), ils sont scopés aux resource groups, et la stabilité de Bicep ainsi que le modèle de configuration JsonNet fonctionnent parfaitement ici.

La couche produit utilise Terraform : infrastructure applicative, networking spécifique au produit, WAF policies, Application Gateways. Tout ce qui est déployé et modifié fréquemment à travers les environnements, là où le scope cross-resource-group et les data blocks prennent tout leur sens.

Bicep possède les fondations, Terraform construit par-dessus. Le pipeline exécute Bicep en premier, puis Terraform. Pas d’interférence, une frontière nette.

“Choisissez un seul outil” est la mauvaise approche. L’infrastructure plateforme et l’infrastructure produit ont des fréquences de changement, des exigences de scope et des modèles d’ownership d’équipe différents. Utiliser un seul outil pour les deux force un des deux côtés à se battre contre les defaults de l’outil.

Ce que nous vous dirions autour d’un café

Si vous nous forciez à choisir un seul outil pour tout, ce serait Terraform. Les data blocks, les déploiements cross-scope, des centaines de providers et le workflow plan/apply, tout cela se cumule avec le temps. Plus vous gérez d’infrastructure, plus la portée de Terraform se rentabilise.

Mais si votre équipe est petite, que votre infrastructure est 100% Azure et que vous déployez des environnements relativement statiques, Bicep est plus simple et vous livrerez plus vite. N’introduisez pas la complexité de Terraform pour le principe du “standard de l’industrie.” Un codebase Bicep bien structuré avec JsonNet vous servira mieux qu’un codebase Terraform mal structuré avec des fichiers de config éparpillés partout.

Oubliez “Bicep ou Terraform ?” Posez-vous plutôt la question : “mon équipe a-t-elle la discipline pour écrire du bon Terraform ?” Si oui, Terraform. Si vous n’êtes pas sûr, commencez par Bicep et ajoutez Terraform quand vous toucherez ses limites, parce que vous les toucherez. Le scope resource group, les dépendances cross-subscription, les ressources non-Azure : voilà les moments où Bicep n’a plus de réponses et où Terraform justifie sa complexité.

Notre article sur le catalogue Terraform piloté par YAML détaille les patterns de la couche produit. Pour la documentation officielle : Terraform on Azure best practices, Bicep vs ARM templates, AzureRM Provider.

Prêt à automatiser votre infrastructure ?

De l'Infrastructure as Code aux pipelines CI/CD, nous aidons les équipes à livrer plus vite avec confiance et moins de tâches manuelles.

Engagement type : 2 à 6 semaines selon le périmètre.
Discuter de vos objectifs d'automatisation
Partager cet article

Commencez par un Platform Health Check

Pas sûr par où commencer ? Une revue rapide d'architecture vous donne une image claire. Sans engagement.