
Le vrai sujet d’une infrastructure IaC n’est pas seulement “creer des ressources”, mais les faire evoluer sans casser leur historique. Vous renommez un module, vous passez une ressource vers for_each, vous reprenez une ressource existante creee hors du state, ou vous voulez retirer un objet de la configuration sans le detruire. Sans aide explicite, OpenTofu voit souvent ces changements comme un vieux bloc a detruire et un nouveau bloc a creer.
OpenTofu propose plusieurs outils pour rendre ces transitions declaratives : import, moved et removed. Le plus important est de leur donner le bon role. import sert a raccrocher un objet existant a votre state. moved sert a dire qu’un objet a change d’adresse logique. removed sert a dire qu’un objet doit sortir de la configuration, avec ou sans destruction selon votre intention. Cette page vous montre comment les combiner proprement sur des depots qui vivent longtemps.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- choisir entre commande d’import et import block ;
- utiliser
movedpour renommer ou redistribuer des ressources ; - utiliser
removedpour oublier un objet sans le detruire ; - faire evoluer des ressources ou modules avec
countetfor_each; - garder un historique de refactor clair pour les mainteneurs suivants.
Pourquoi ces blocs existent
Section intitulée « Pourquoi ces blocs existent »OpenTofu raisonne principalement avec les adresses des objets dans le state. Si vous changez une adresse sans explication, il ne peut pas deviner s’il s’agit :
- du meme objet deplace ;
- d’un objet a importer ;
- d’un objet a oublier ;
- ou d’un objet a recreer.
Les blocs de refactor existent donc pour transformer une intention implicite en intention declarative et lisible.
1 - import : reprendre un objet deja existant
Section intitulée « 1 - import : reprendre un objet deja existant »OpenTofu dispose de deux facons principales d’importer :
- la commande
tofu import; - le
importblock dans la configuration.
L’approche la plus interessante pour les equipes et la CI est souvent le import block, car elle est visible dans le code et passe par le cycle normal plan -> apply.
Exemple minimal avec import block
Section intitulée « Exemple minimal avec import block »import { to = aws_instance.example id = "i-abcd1234"}
resource "aws_instance" "example" { ami = "ami-12345678" instance_type = "t3.micro"}Le point important est le suivant : il faut deja une configuration de ressource. OpenTofu n’accepte pas un import sans savoir dans quel bloc la ressource devra vivre.
Quand preferer import block a tofu import
Section intitulée « Quand preferer import block a tofu import »| Situation | Bon choix |
|---|---|
| operation ponctuelle en local | tofu import |
| revue en equipe | import block |
| pipeline CI/CD | import block |
| imports multiples et historisation du changement | import block |
Exemple avec module et for_each
Section intitulée « Exemple avec module et for_each »OpenTofu permet aussi :
import { to = module.instances.aws_instance.example id = "i-abcd1234"}ou encore :
import { to = aws_instance.example["blue"] id = "i-abcd1234"}2 - moved : dire qu’un objet a change d’adresse
Section intitulée « 2 - moved : dire qu’un objet a change d’adresse »Le bloc moved sert a expliquer qu’un objet deja connu du state doit etre considere comme le meme objet, mais a une nouvelle adresse logique.
Syntaxe minimale :
moved { from = aws_instance.old_name to = aws_instance.new_name}Renommer une ressource
Section intitulée « Renommer une ressource »resource "aws_instance" "web" { ami = "ami-12345678" instance_type = "t3.micro"}
moved { from = aws_instance.app to = aws_instance.web}OpenTofu comprend alors que aws_instance.app n’est pas a detruire, mais a re-interpreter comme aws_instance.web.
Passer d’une ressource simple a for_each
Section intitulée « Passer d’une ressource simple a for_each »locals { instances = { blue = { instance_type = "t3.micro" } }}
resource "aws_instance" "web" { for_each = local.instances ami = "ami-12345678" instance_type = each.value.instance_type}
moved { from = aws_instance.web to = aws_instance.web["blue"]}Le cas d’usage est tres frequent : vous partez d’une ressource unique, puis vous generalisez avec for_each.
Renommer un appel de module
Section intitulée « Renommer un appel de module »module "network_v2" { source = "../modules/network"}
moved { from = module.network to = module.network_v2}Ici encore, l’objectif n’est pas de creer un nouveau module, mais de conserver le lien historique entre les adresses.
3 - moved pour des refactors plus lourds
Section intitulée « 3 - moved pour des refactors plus lourds »Le bloc moved devient encore plus utile quand vous :
- transformez un
countenfor_each; - decoupez un gros module en plusieurs petits modules ;
- enchainez plusieurs renommages dans le temps.
Exemple de chaine de moves
Section intitulée « Exemple de chaine de moves »moved { from = aws_instance.a to = aws_instance.b}
moved { from = aws_instance.b to = aws_instance.c}Cette ecriture garde un historique d’upgrade plus robuste qu’un seul move ecrasant les etapes intermediaires.
Pourquoi il ne faut pas supprimer trop vite les moved
Section intitulée « Pourquoi il ne faut pas supprimer trop vite les moved »Retirer un moved trop tot est souvent une breaking change. Un utilisateur qui saute une version intermediaire n’aura plus l’information necessaire pour relier l’ancienne adresse a la nouvelle.
Le bon reflexe est donc de conserver les moved tant que vous n’avez pas la certitude que tous les consommateurs ont applique la migration voulue.
4 - removed : sortir un objet de la configuration sans toujours le detruire
Section intitulée « 4 - removed : sortir un objet de la configuration sans toujours le detruire »Le bloc removed sert a dire qu’un objet doit quitter la configuration. C’est utile si vous voulez oublier une ressource ou un module dans le state sans la destruction correspondante.
Exemple pour une ressource
Section intitulée « Exemple pour une ressource »removed { from = aws_instance.web
lifecycle { destroy = false }}Avec destroy = false, OpenTofu retirera l’objet du state sans demander sa destruction dans le systeme distant.
Exemple pour un module
Section intitulée « Exemple pour un module »removed { from = module.legacy_network
lifecycle { destroy = false }}Pour un module, l’idee est la meme. Vous oubliez le module dans la configuration et dans le state, sans toucher aux objets geres a distance.
Ce qu’il faut retenir sur removed
Section intitulée « Ce qu’il faut retenir sur removed »lifecyclen’est pas strictement obligatoire, mais il est fortement recommande ;destroy = falsesert a oublier sans detruire ;destroy = truedocumente explicitement une suppression volontaire ;- les
removedpointant vers des modules n’acceptent pas de provisioners ; - pour une ressource, un
removedpeut inclure un provisioner si la destruction est voulue.
5 - Scenario type de refactor propre
Section intitulée « 5 - Scenario type de refactor propre »Voici un enchainement sain sur une configuration existante :
-
Importer les objets qui existent hors state avec
importoutofu import. -
Stabiliser la configuration avec un
planpropre. -
Refactorer ensuite les adresses avec
moved. -
Sortir enfin certains objets de la configuration avec
removedsi l’objectif est de les oublier sans les detruire. -
Conserver les
movedutiles aussi longtemps que le chemin d’upgrade doit rester compatible.
Ce sequence evite le piege classique qui consiste a faire un gros refactor structurel avant meme d’avoir raccroche correctement les objets au state.
6 - Ne pas oublier la deprecation des variables et outputs
Section intitulée « 6 - Ne pas oublier la deprecation des variables et outputs »Les refactors ne concernent pas seulement les ressources. OpenTofu supporte aussi des messages de deprecation sur les variables et outputs d’un module, ce qui aide a accompagner les consommateurs vers une nouvelle interface sans tout casser d’un coup.
Cette approche ne remplace pas moved, mais elle complete une strategie de migration progressive pour les modules tres utilises.
Depannage
Section intitulée « Depannage »| Symptome | Cause probable | Solution |
|---|---|---|
| un renommage provoque une recreation | absence de moved ou mauvais adressage | ajouter un moved correct puis relire le plan |
| import block refuse de planifier | resource block absent ou insuffisant | definir la ressource cible avant l’import |
| plusieurs adresses gerent le meme objet | import fait deux fois vers des adresses differentes | choisir une seule adresse canonique et nettoyer le state |
| un objet sort du state alors qu’il devait etre detruit | removed avec destroy = false utilise par erreur | corriger l’intention de destruction et relire le plan |
| un vieux consommateur casse a la mise a jour | moved retire trop tot | restaurer le move ou documenter une migration intermediaire |
A retenir
Section intitulée « A retenir »importsert a reprendre un objet existant dans le state.movedsert a conserver un objet tout en changeant son adresse logique.removedsert a sortir un objet de la configuration, avec ou sans destruction selonlifecycle.destroy.- Les refactors propres se lisent d’abord dans le plan, pas au moment du
applysurprise. - Garder l’historique des migrations dans le code aide les prochains mainteneurs autant que l’outillage lui-meme.