Retour au projet dans le portfolio

Terraform Drift Detection — comparaison de plans entre deux branches

Job Jenkins qui exécute scripts/check-drift.sh pour comparer le plan Terraform d'une branche de référence et celui d'une branche à valider, couche par couche et stage par stage. Objectif : prouver qu'un refactor produit exactement le même plan d'infrastructure — donc aucune ressource créée, détruite ou modifiée par accident.

Schéma générique : instances, dépôts, canaux et modules sont désignés par leur fonction. L'architecture est présentée à titre illustratif, indépendamment de toute organisation.

Étape du script Artefact comparé · service externe Secret ou décision Couche validée Écart détecté Agent Jenkins
01 — déclenchement, préparation du workspace, exécution

Pipeline Jenkins

Tout se passe sur un agent éphémère : les deux branches y sont ramenées côte à côte, les secrets injectés puis supprimés, et chaque stage est traité couche par couche avant que Slack ne reçoive le verdict.

paramètres du job FEATURE_BRANCH REF_BRANCH STAGES ENV_ID VERBOSE / KEEP_PLANS / SKIP_INIT branche à valider référence (integration) integration,test ou all requis — échec si vide Job Jenkins Terraform Drift Detection agent jenkins préparation du workspace Checkout branche à valider git fetch branche de référence Fichier de secrets généré puis injecté exécution du script de comparaison Agent SSH par stage Credentials AWS Credentials Scaleway Credentials du coffre Une clé SSH distincte par stage. Les identifiants ne sortent jamais du wrapper de credentials. ./check-drift.sh --ref-branch REF --stages LIST --env-id ID --secrets PATH pour chaque stage, trois couches 1 Outillage du stage plan comparé 2 Liste des envs apply, choix de l'ID 3 Stacks d'environnement generate puis plan Détail de la comparaison : schéma 2 ci-dessous. GitHub dépôt d'infrastructure checkout + git fetch REF_BRANCH clone complet, nettoyage avant checkout résultat du build Slack notification systématique vert / rouge / gris selon l'issue environnement du job AWS_DEFAULT_REGION = eu-west-1 SCW_DEFAULT_ZONE = fr-par-2 TFENV_TERRAFORM_VERSION = 1.13.4 TF_PLUGIN_CACHE_DIR (partagé) timeout : 3 heures builds concurrents désactivés (le build précédent est interrompu) post-exécution Suppression du fichier de secrets Nettoyage complet du workspace Notification Slack quel que soit le résultat Exécuté même en cas d'échec ou d'interruption.
Le fichier de secrets n'existe que le temps du build : il est généré dans le workspace, injecté dans Terraform, puis supprimé par le bloc de post-exécution — qui s'exécute même si le job échoue.
02 — génération des deux plans, normalisation, verdict

Comparaison d'une couche

Les deux branches produisent chacune un plan JSON sur le même state, puis jq retire tout ce qui n'est pas déterministe. Ce qui reste est l'intention d'infrastructure : c'est la seule chose comparée, et la comparaison est stricte.

couches concernées 1 Outillage du stage 2 Liste des envs 3 Stacks d'environnement une stack par environnement Les couches 1 et 3 passent par le cycle ci-contre. La couche 2 ne compare rien : elle applique, expose la liste des environnements et fixe ENV_ID. branche de référence git checkout REF_BRANCH terraform init (cache partagé) terraform plan -out terraform show -json → ref.json Couche 3 : make generate exécuté avant le plan. branche à valider git checkout FEATURE_BRANCH terraform init (cache partagé) terraform plan -out terraform show -json → feat.json Même environnement et même state ciblés des deux côtés. normalisation des deux plans (jq) .resource_changes | sort_by(.address) map(del(.deposed)) .change |= del(.before_sensitive, .after_sensitive, .after_unknown) .before / .after |= del(.latest_restorable_time) walk(del(.Authorization)) Retire les champs non déterministes : ce qui reste est l'intention d'infrastructure. ref.json identique à feat.json ? comparaison stricte, chaîne à chaîne oui [ OK ] Couche validée Aucun écart de plan entre les branches. Compteur de couches validées incrémenté. Le refactor est neutre pour l'infrastructure. non [ FAIL ] Écart détecté Rapport par adresse de ressource : ressources présentes d'un seul côté, et diff du bloc .change pour les adresses communes dont le plan diverge. Bilan par stage : couches validées, en échec, ignorées → code de sortie du script → statut du build Jenkins → notification Slack.
La normalisation est ce qui rend le verdict exploitable : sans elle, des champs non déterministes feraient échouer la comparaison à chaque exécution.

Tous les schémas d'architecture