Sägetstrasse 18, 3123 Belp, Switzerland +41 79 173 36 84 info@ict.technology

      Terraform @ Scale - Partie 1d : Pièges et bonnes pratiques dans des environnements multi-tenant

      Les Remote States sont un outil puissant pour transmettre des informations de manière contrôlée entre équipes et locataires (tenants). Dans les environnements cloud complexes impliquant plusieurs domaines de responsabilité, ils permettent de gagner en transparence, en réutilisabilité et en scalabilité. Toutefois, ils présentent également des risques : des états erronés, des problèmes d’accès ou des dépendances non résolues peuvent compromettre la stabilité de l’ensemble de l’infrastructure. Cet article montre comment éviter ces écueils et comment poser les bases d’une infrastructure automatisée fiable grâce à des structures claires et des pratiques éprouvées.

      State-Locking dans des environnements multi-tenant

      Dans un environnement où plusieurs équipes travaillent simultanément sur différentes parties de l’infrastructure, le verrouillage de l’état (State-Locking) est indispensable. Sans ce verrouillage, il est possible que plusieurs exécutions de Terraform tentent de modifier le même state en parallèle, risquant de se l’écraser mutuellement. Et cela se termine presque toujours en désastre. Il est donc impératif de disposer d’un mécanisme qui réserve un fichier d’état (statefile) de manière exclusive à un utilisateur jusqu’à la fin de ses opérations d’écriture, après quoi le fichier est de nouveau accessible aux autres processus.

      Bonnes pratiques pour le State-Locking

      • Choisissez un backend doté d’un mécanisme de verrouillage robuste :

        Comme expliqué dans la dernière partie de cette série d’articles, en dehors de l’utilisation d’un fichier local dans un environnement mono-utilisateur, seuls l’utilisation de Consul comme backend distant pour les fichiers d’état ainsi que Terraform Cloud et Enterprise sont supportés et certifiés officiellement. Terraform prend également en charge d’autres backends comme HTTPS ou S3, mais ceux-ci ne sont pas couverts par les contrats de support, leur utilisation se fait donc à vos risques et périls, et tous ne prennent pas en charge un verrouillage robuste et fiable. En revanche, Consul et Terraform Enterprise offrent des mécanismes de verrouillage fiables au niveau entreprise.

        Lorsqu’on utilise Consul, Terraform crée automatiquement une session pour protéger l’état pendant les processus de planification et d’application.


        terraform {
          backend "consul" {
            address = "consul.example.com:8500"
            path    = "terraform/customer-a"
            lock    = true  # Activation explicite du verrouillage
          }
        }

        Cette fonction de verrouillage ne nécessite aucune configuration manuelle de sessions ou d’autres mécanismes – Terraform s’en charge automatiquement.

      • Identifier et résoudre les verrous orphelins :

        Les verrous orphelins apparaissent généralement lorsque des processus Terraform s’interrompent de manière inattendue (par exemple suite à un échec CI/CD, une coupure réseau ou une interruption manuelle par un utilisateur).

        Pour nettoyer automatiquement ces verrous orphelins, vous devriez mettre en place un processus de nettoyage préventif. Un simple contrôle avant chaque exécution Terraform suffit dans la pratique :


         

        consul lock -delete "terraform/customer-a" || true


        Cette commande supprime les verrous existants avant le démarrage d’un nouveau processus Terraform - efficacement et sans surcharge administrative inutile.

      • Configurer des délais d’expiration automatiques pour les verrous : Dans la configuration Consul (consul.hcl), vous pouvez définir des durées de validité pour les sessions de verrouillage. Cela garantit que les verrous persistants sont automatiquement levés.


        Valeurs recommandées pour des environnements de production :


        session_ttl_min = "15m"
        session_ttl_max = "1h"

        Vous évitez ainsi que des verrous orphelins ne bloquent durablement des ressources.

      • Tenir compte des conflits de verrouillage dans les pipelines CI/CD :

        Dans les environnements CI/CD, des conflits peuvent survenir lorsque plusieurs pipelines essaient de placer un verrou simultanément.

        Un modèle éprouvé est ici un mécanisme de répétition avec une stratégie de backoff :


        for i in {1..5}; do
          terraform apply && break
          echo "Conflit de verrou détecté. Nouvelle tentative dans $((i * 10)) secondes..."
          sleep $((i * 10))
        done

        Ce mécanisme assure que votre déploiement reste stable, même si des verrous temporaires bloquent momentanément l’accès. Voici une explication du fonctionnement du script :

        1. Une boucle s’exécute cinq fois : (for i in {1..5})
        2. À chaque itération, il tente d’exécuter terraform apply
        3. Si terraform apply réussit (&&), la boucle se termine via break
        4. Si terraform apply échoue, le script attend un certain temps (sleep) avant de réessayer
        5. Le temps d’attente augmente à chaque tentative ($((i * 10))) : 10, 20, 30, 40 et 50 secondes


        Ainsi, le déploiement via Terraform est automatiquement relancé en cas de problèmes temporaires comme des erreurs réseau, des limites d’API ou des conflits de ressources qui feraient échouer une tentative ponctuelle. 

        Un système de verrouillage robuste réduit le risque d’erreurs et empêche les exécutions Terraform de se bloquer mutuellement. Avec les mesures décrites, vous protégez efficacement votre environnement contre les verrous orphelins et les retards inutiles.

       

      Versionnage du State

      Un versionnage solide est indispensable. En cas de problème, vous pouvez ainsi revenir facilement à des versions précédentes. Terraform Cloud/Enterprise propose cette fonctionnalité par défaut ; avec Consul, vous devez en revanche mettre en œuvre des mécanismes de sauvegarde supplémentaires (snapshots).

      Gestion des dépendances entre States et prévention des cycles

      Les dépendances entre différents States peuvent rapidement devenir complexes et, dans le pire des cas, entraîner des dépendances circulaires qui rendent impossible la mise à jour de l’infrastructure.

      Comme les dépendances Remote State dans terraform_remote_state sont statiques, les configurations dépendantes doivent être mises à jour manuellement dès que des outputs pertinents sont modifiés.

      Bonnes pratiques pour éviter les cycles :

      • Structure hiérarchique des dépendances : Ressources globalesRessources régionalesRessources spécifiques aux tenantsRessources spécifiques aux applications. Cette dépendance unidirectionnelle claire empêche la formation de cycles.
      • Utiliser des références indirectes : Si une référence directe crée un cycle, transmettez l’information via un niveau intermédiaire :
        # Au lieu d’une référence directe de A → C et C → A :
        # A → B → C (avec B comme médiateur)
        # Dans la configuration B :
        output "information_from_a" {
        value = data.terraform_remote_state.a.outputs.needed_value
        }




        # Dans la configuration C :
        data "terraform_remote_state" "b" {
        # ...
        }
        local {
        value_from_a = data.terraform_remote_state.b.outputs.information_from_a
        }

      • Utiliser des Data Sources à la place de Remote State : Lorsque cela est possible, privilégiez les Data Sources natives. Elles sont plus dynamiques et réduisent les dépendances entre States.

        # Au lieu de :
        data "terraform_remote_state" "network" {
          # ...
        }
        
        # Mieux, si possible :
        data "oci_core_subnet" "app_subnet" {
          subnet_id = "ocid1.subnet.oc1..."
        }

        Cependant : utilisez les Data Sources avec discernement et évitez de les employer dans des boucles for_each ou autres, car elles déclenchent un appel API à chaque exécution, ce qui nuit à la scalabilité. L’utilisation de Data Sources à l’intérieur de modules de base est donc dans de nombreux cas déconseillée. De plus, les modules appelés par des modules racine ne devraient pas non plus accéder aux fichiers d’état. Il est donc recommandé d’utiliser les Data Sources également au niveau des modules racine. 

      Réduction des informations dans le State pour améliorer les performances

      Plus votre fichier d’état est volumineux, plus l’exécution de chaque terraform plan sera lente. Terraform lit l’intégralité du State, ce qui peut considérablement ralentir les opérations dans des environnements complexes.

      Bonnes pratiques pour l’optimisation du State :

      • Partitionnement granulaire du State : Une bonne règle empirique : un State ne devrait pas contenir plus de 100 à 250 ressources, afin de garantir des temps de planification acceptables. Appliquez le principe de Boucles d’or (nous en reparlerons dans un prochain article de cette série).
      • Attention aux outputs complexes : Limitez les outputs à ce qui est essentiel et strictement nécessaire :

        # À éviter :
        output "entire_vcn" {
          value = oci_core_vcn.main
        }
        
        # Mieux :
        output "vcn_essential_info" {
          value = {
            id         = oci_core_vcn.main.id
            cidr_block = oci_core_vcn.main.cidr_block
          }
        }

      • Évitez les outputs sensibles lorsque ce n’est pas nécessaire : Ils augmentent la taille du State et Terraform y stocke des métadonnées supplémentaires. De plus, les outputs sensibles ne peuvent pas être utilisés dans d’autres modules - il est donc souvent préférable de les omettre.

      Débogage des dépendances complexes entre States

      Le débogage des problèmes liés aux Remote States peut être délicat. Voici quelques conseils utiles :

      • Activer les logs détaillés :

        export TF_LOG=DEBUG
        export TF_LOG_PATH=./terraform.log

      • Intégrer une validation du State dans votre CI/CD :

        terraform state pull | jq '.outputs.network_config.value | has("vcn_id")'

      • Utiliser terraform console :

        $ terraform console
        > data.terraform_remote_state.network.outputs.subnet_ids

      Aliasing de Provider pour scénarios complexes

      Lorsqu’on travaille avec plusieurs comptes (ou plusieurs régions dans un même compte), l’aliasing de Provider simplifie considérablement la configuration :


      provider "oci" {
        alias  = "global"
        region = "eu-frankfurt-1"
      }
      
      provider "oci" {
        alias  = "customer_a"
        region = "eu-amsterdam-1"
      }
      
      module "customer_a_instance" {
        source     = "./modules/instance"
        provider   = oci.customer_a
        subnet_id  = module.global.outputs.subnet_id
      }

      Conclusion

      La maîtrise de ces bonnes pratiques vous aide à gérer une infrastructure multi-tenant de manière stable et efficace. Avec l’expérience, vous constaterez que ces modèles ne permettent pas seulement d’éviter des problèmes, mais qu’ils améliorent aussi la collaboration entre équipes et rehaussent la qualité globale de votre infrastructure.