Jusqu’à Terraform 1.15, un bloc import ne pouvait vivre que dans le module racine. Si tu voulais reprendre la main sur un groupe de ressources créé à la main et géré ensuite par un module, tu devais écrire depuis la racine des adresses comme module.network.azurerm_virtual_network.main, et le module lui-même ne savait rien de cette adoption. Terraform 1.16, publié le 26 août 2026 et suivi de la 1.16.1 le 2 septembre, lève cette limite.
Ce n’est pas la seule nouveauté utile. Voici ce qui change vraiment pour un usage quotidien sur Azure, ce qui mérite d’être testé avant d’y croire, et le seul point de vigilance de la montée de version.
Les blocs import acceptés dans les modules
Les notes de version de la 1.16.0 l’annoncent en une ligne (#38352) : les blocs import sont désormais supportés à l’intérieur des modules. Concrètement, un module peut porter lui-même l’adoption de ressources existantes :
# modules/resource-group/main.tf
resource "azurerm_resource_group" "this" {
name = var.name
location = var.location
}
import {
to = azurerm_resource_group.this
id = "/subscriptions/${var.subscription_id}/resourceGroups/${var.name}"
}
Le même lot corrige un bug gênant : les blocs import respectent maintenant les noms locaux de providers (#38338). Si tu utilises des alias du type azurerm.hub pour cibler un autre abonnement, c’était une source d’erreurs.
Mon avis : c’est un vrai gain pour les projets de reprise d’existant (landing zone, migration d’un parc fait au portail), mais ne laisse pas de bloc import en dur dans un module partagé entre plusieurs équipes. Chaque appelant hériterait d’une tentative d’import qui n’a de sens que pour toi. Garde ces blocs dans un module d’adoption dédié, ou retire-les une fois l’import fait. Teste aussi le comportement avec un module appelé en count ou for_each sur un terraform plan avant de généraliser.
lifecycle { destroy = false } : ce que prevent_destroy ne faisait pas
Nouveauté de la 1.16 (#38784), le bloc lifecycle d’une ressource accepte maintenant destroy = false. La documentation du méta-argument lifecycle le décrit ainsi : la ressource est retirée de l’état sans que l’infrastructure réelle soit détruite.
resource "azurerm_storage_account" "legacy_logs" {
name = "stlegacylogs001"
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
account_tier = "Standard"
account_replication_type = "GRS"
lifecycle {
destroy = false
}
}
La différence avec prevent_destroy est importante. prevent_destroy = true fait échouer le plan dès qu’une destruction est prévue, mais la doc rappelle qu’il ne protège rien si tu supprimes la configuration de la ressource. destroy = false fait autre chose : Terraform « oublie » la ressource au lieu de la supprimer. C’est exactement ce que tu veux quand une ressource passe sous la gestion d’une autre équipe ou d’une autre stack.
Un bémol : la documentation reste laconique sur les cas limites, en particulier le remplacement forcé (un changement d’attribut qui impose une recréation). Valide le comportement sur ton cas avec un terraform plan en environnement de test avant de compter dessus en production. Pour une ressource que tu ne dois jamais perdre, garde un verrou Azure (CanNotDelete) en plus : c’est la seule protection qui ne dépend pas de l’outil.
Le bloc store de terraform_data
Les valeurs éphémères (arrivées en 1.10) ont un défaut assumé : elles ne survivent pas entre le plan et l’apply. Le nouveau bloc store de terraform_data (#38298) permet de les conserver. L’exemple de la documentation de terraform_data :
ephemeral "random_password" "test" {
length = 16
}
resource "terraform_data" "test" {
store {
input = ephemeral.random_password.test.result
sensitive = true
version = 1
}
}
output "password" {
sensitive = true
value = terraform_data.test.store.sensitive_output
}
L’argument version est la subtilité à retenir : tant que tu ne l’incrémentes pas, une nouvelle valeur d’input ne remplace pas la sortie stockée. Ça t’évite de régénérer un mot de passe à chaque plan.
Attention au revers : stocker une valeur, c’est l’écrire dans l’état, même marquée sensitive. Les valeurs éphémères ont été créées justement pour ne plus y mettre de secrets. Utilise store seulement quand tu en as vraiment besoin, et seulement avec un backend chiffré et des droits d’accès serrés sur le state (pour un backend azurerm, un compte de stockage avec RBAC Entra et sans clé d’accès partagée).
Les petites améliorations qui changent le quotidien
terraform state show -jsonetterraform workspace list -jsonproduisent enfin une sortie exploitable par un script, sans parser du texte.terraform graph -format=mermaidsort le graphe de dépendances en Mermaid : tu peux le coller tel quel dans un README GitHub ou un wiki Azure DevOps, qui savent l’afficher.terraform console -scope=module.networkévalue tes expressions dans le contexte d’un module. Pratique pour déboguer unlocalscompliqué sans tout remonter à la racine.contains()sait tester la valeurnull, etmerge()ne plante plus quand on lui passe des objetsnull.terraform testliste maintenant les ressources laissées en place quandskip_cleanupest activé.- Les namespaces de providers avec underscore sont acceptés (#38894), ce qui débloque certains registres privés.
Avant de monter de version
La seule note de mise à niveau concerne les provisioners : bastion_host_key est désormais réellement appliqué (#38318). Si tu utilises encore des provisioners remote-exec ou file via un bastion SSH, vérifie que la clé configurée est la bonne. Jusqu’ici, une valeur fausse passait inaperçue. Après la mise à jour, la connexion échouera.
Pour le reste, la montée depuis 1.15 se fait sans réécriture. Prends directement le dernier patch (1.16.1 à ce jour) et contrains la version dans tes modules racines :
terraform {
required_version = ">= 1.16.1, < 1.17.0"
}
La 1.17 est déjà en alpha (build 1.17.0-alpha20260827), donc ne t’étonne pas de voir passer de nouvelles fonctions dans les prochains mois. Si tu es en train de te demander s’il faut rester sur Terraform ou passer à OpenTofu, j’ai fait le point dans OpenTofu vs Terraform en 2026.
FAQ
Mes configurations 1.15 fonctionnent-elles telles quelles ?
Oui, à l’exception du cas bastion_host_key décrit plus haut. Fais quand même un terraform plan sur chaque stack après la mise à jour et vérifie qu’il ne propose aucun changement.
Faut-il retirer un bloc import une fois la ressource importée ?
Un bloc import dont la cible est déjà dans l’état ne fait plus rien, donc le garder ne casse rien. Le retirer rend le code plus lisible et évite qu’un futur lecteur se demande quelle ressource est encore « en adoption ».
destroy = false ou bloc removed ?
Le bloc removed (depuis 1.7) sert quand tu supprimes la ressource de ta configuration. destroy = false se pose sur la ressource elle-même, tant qu’elle est encore déclarée. Pour un transfert ponctuel vers une autre stack, removed reste le plus explicite.
Ces nouveautés concernent-elles le provider AzureRM ?
Non, elles sont dans le cœur de Terraform et marchent avec tous les providers. Côté AzureRM, la grosse évolution de l’année reste la version 5.0, dont j’ai détaillé la migration.
Par où commencer
Mets à jour Terraform sur ton poste et ton runner CI, lance un plan sur ta stack la plus simple, puis cherche dans ton code les terraform state rm lancés à la main dans des scripts ou des runbooks : ce sont les premiers candidats à remplacer par destroy = false ou un bloc removed, versionnés et relus comme le reste.

Laisser un commentaire