Ansible Vault et pipeline Terraform→Ansible : boucler la boucle IaC
Cet article fait suite à la session précédente sur les modules Terraform et le backend Azure. L’infrastructure tourne, le tfstate est distant et chiffré, les modules sont découpés proprement. Il manquait la partie configuration : une fois la VM provisionnée, qui la configure et comment les secrets passent sans apparaître en clair dans les logs ?
C’est l’objet de cette session : Ansible Vault pour les secrets, et un script bash pour orchestrer le tout de bout en bout.
Le problème de départ
Un détail agaçant s’est glissé entre les deux sessions. J’avais supprimé la VM via az group delete en urgence (le terraform destroy avait timeout), ce qui a laissé le state Terraform dans un état incohérent : plus de VM dans Azure, mais le tfstate croyait encore qu’elle existait.
En plus de ça, le blob de state était verrouillé : un run précédent avait planté sans libérer le lock.
Avant tout terraform apply, il a fallu :
|
|
La commande refresh-only est ce que faisait l’ancien terraform refresh (maintenant déprécié). Elle interroge Azure, compare avec le tfstate, et met à jour le state local sans déployer quoi que ce soit. C’est la façon propre de résorber un drift sans casser l’infrastructure existante.
Ansible Vault : pourquoi et comment
Un playbook Ansible qui configure un serveur a souvent besoin de secrets : mot de passe base de données, token API, clé d’application. Les mettre en clair dans un fichier vars.yml versionné sur GitHub est un problème de sécurité classique, le genre de chose qu’on retrouve dans les audits de dépôts Git.
Ansible Vault résout ça simplement : il chiffre un fichier YAML entier en AES-256. Le fichier peut aller sur GitHub, il ne contient rien de lisible sans la clé.
La structure que j’ai mise en place :
Ansible/
├── group_vars/all/
│ ├── vars.yml # Variables claires (app_name, db_user)
│ └── vault.yml # Variables chiffrées (vault_app_secret, vault_db_password)
├── .vault_pass # Mot de passe vault (chmod 600, dans .gitignore)
└── ansible.cfg # vault_password_file = .vault_pass
La convention vault_ en préfixe sur les variables chiffrées n’est pas obligatoire mais elle est très utile : en lisant un playbook, on sait immédiatement d’où vient chaque variable et si elle est sensible.
La ligne clé dans ansible.cfg :
|
|
Ça permet de lancer les playbooks sans saisir le mot de passe à chaque fois. Ansible le lit dans ce fichier. En production, ce fichier serait remplacé par une référence à un vault externe (HashiCorp Vault, Azure Key Vault, ou AWS Secrets Manager selon le contexte).
Ce qui a coincé
Un bug subtil d’ansible-core 2.20 avec localhost en connection: local : le chargement automatique de group_vars/all/ ne se fait pas pour le groupe local. Les variables déclarées dans vars.yml n’étaient pas visibles dans le playbook.
La solution propre : déclarer explicitement les fichiers dans le playbook avec vars_files:.
|
|
Ce n’est pas idéal (ça casse le principe de chargement automatique), mais c’est documenté comme comportement connu pour les playbooks qui tournent sur localhost hors inventaire dynamique. Un autre bug s’est glissé là-dedans : des fichiers corrompus à cause d’un copier-coller sans retour à la ligne. localhostapp_name: "mon-app-lab" sur une seule ligne, difficile à déboguer au premier coup d’œil.
Le pipeline bout en bout
Une fois Ansible Vault en place, j’ai écrit un script bash qui enchaîne tout sans intervention manuelle :
|
|
Quelques détails qui comptent dans ce script :
set -euo pipefail en ligne 2 : c’est une triple protection. -e arrête le script à la première erreur (sans ça, une commande en échec est silencieusement ignorée). -u génère une erreur si une variable non définie est utilisée. -o pipefail propage l’erreur dans les pipes. Sans ces trois options, un script bash peut rater silencieusement et continuer.
$(terraform output -raw vm_public_ip) : la notation $() est une substitution de commande : elle exécute la commande et capture sa sortie dans une variable. terraform output -raw retourne la valeur brute sans guillemets ni formatage, ce qui est exactement ce qu’on veut pour une adresse IP.
(cd Ansible && ansible-playbook ...) : le () crée un sous-shell. Le changement de répertoire est confiné à ce sous-shell ; le répertoire courant du script parent reste intact après. C’est plus propre que cd && ... && cd -.
Le sleep 40 est la faiblesse du script : on attend empiriquement que SSH soit disponible. En CI/CD professionnel, on bouclerait avec ssh-keyscan jusqu’à obtenir une connexion, ou on utiliserait un readiness check natif Ansible.
Le handoff Terraform → Ansible
Le point d’articulation entre les deux outils, c’est cette ligne :
|
|
Terraform sort l’IP, bash la capture, sed met à jour l’inventaire Ansible. Ce couplage par fichier plat fonctionne en homelab. En entreprise, on préférerait un inventaire dynamique : le plugin azure_rm d’Ansible interroge directement l’API Azure et découvre les VMs par tag. Plus de fichier hosts.ini à maintenir, plus de sed fragile.
Ce que ça donne selon le contexte
Ce pipeline en script bash illustre bien comment les patterns d’orchestration évoluent selon l’échelle.
Dans un homelab, le bash suffit. Le script s’exécute depuis le poste de travail, les credentials sont chargés via un source init-session.sh, et on a un cycle complet en quelques minutes.
En PME, ce script serait remplacé par un workflow GitHub Actions ou GitLab CI : déclenchement automatique sur push, secrets stockés dans les variables d’environnement du CI, logs archivés, notification d’échec. Le code ne change pas beaucoup, c’est l’orchestrateur qui change.
En grande entreprise, l’architecture se sépare davantage. Terraform Enterprise (ou OpenTofu avec un runner dédié) gère l’infrastructure avec approbations, drift detection automatique, et run history. Ansible est remplacé par Ansible Automation Platform (anciennement Tower/AWX) avec ses propres credentials vaultés, ses inventaires dynamiques, et son RBAC. Le pipeline bash devient un webhook entre deux plateformes.
La logique reste la même. Ce qui change, c’est le niveau d’audit trail, de gouvernance, et d’automatisation de la résilience.
Ce que j’en retiens
Ansible Vault est simple à mettre en place et couvre le cas d’usage principal : ne pas avoir de secrets en clair dans un repo. La vraie question dans un contexte professionnel n’est pas “comment chiffrer” mais “qui détient la clé et comment elle est distribuée”. C’est là que ça rejoint les problématiques de gestion des identités et des accès qu’on retrouve dans les référentiels type ISO 27001 ou NIS2.
Le pipeline bout en bout m’a surtout montré à quel point le handoff entre outils est souvent le point fragile d’une chaîne IaC. Terraform et Ansible ne se parlent pas nativement : c’est au script ou à l’orchestrateur de faire la jonction. Dans mon background BI, j’appelais ça un ETL : extraire une donnée (l’IP), la transformer (la mettre dans le bon format pour hosts.ini), la charger (l’écrire dans le fichier). La logique est identique, les enjeux de fiabilité aussi.
Le premier run du pipeline complet a fonctionné du premier coup : 9 ressources créées, hardening complet, démo Vault, 9 ressources détruites. Ça tient surtout au fait que chaque étape avait été testée séparément avant. Lancer un pipeline “all-in-one” sans avoir validé chaque maillon individuellement, c’est la garantie de déboguer dans un état incertain.
Configs disponibles sur GitHub. Le repo inclut le README avec les prérequis, les variables à configurer, et l’ordre d’exécution.