
Le cas réel n’est pas “je pars de zéro”, mais souvent “quelqu’un a déjà créé quelque chose”. Une instance EC2 existe déjà dans AWS, vous voulez maintenant la gérer proprement avec Terraform sans la détruire, puis refactoriser votre code sans provoquer de recréation. Enfin, vous devez vérifier qu’aucune modification manuelle n’a éloigné l’infrastructure du code. C’est exactement le trio import, moved et drift detection.
Ce guide suit un enchaînement volontairement progressif. Vous allez d’abord créer une instance dans un projet de départ, puis demander à Terraform de l’oublier sans la détruire. Cette étape simule une ressource déjà présente dans AWS mais absente de votre state courant. Vous l’importerez ensuite dans une nouvelle configuration, vous renommerez son adresse avec un bloc moved, puis vous introduirez un drift via l’AWS CLI pour observer comment Terraform le détecte.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Importer une ressource existante avec
terraform import - Comprendre pourquoi une configuration minimale ne suffit pas après un import
- Refactoriser un nom de ressource avec
movedsans recréer l’infrastructure - Détecter un drift avec
terraform plan -refresh-only - Corriger le drift soit en revenant au code, soit en acceptant le nouvel état
Pourquoi ce workflow existe
Section intitulée « Pourquoi ce workflow existe »terraform import sert à raccrocher une ressource existante à votre state Terraform. Pensez-y comme à l’opération qui ajoute une ligne manquante dans l’inventaire. La ressource existe déjà dans AWS ; Terraform apprend simplement à la suivre.
Le bloc moved répond à un autre problème : votre code évolue. Vous renommez une ressource, vous la déplacez dans un module, vous réorganisez un fichier. Sans indication explicite, Terraform peut croire qu’il faut détruire l’ancienne ressource et en créer une nouvelle. moved dit à Terraform : “c’est le même objet, seule son adresse logique a changé”.
Enfin, le drift apparaît quand quelqu’un modifie l’infrastructure hors de Terraform. terraform plan -refresh-only permet de comparer l’état réel et le state enregistré sans pousser immédiatement une modification sur l’infrastructure.
Prérequis
Section intitulée « Prérequis »- Terraform >= 1.11 installé
- AWS CLI configuré
- Compréhension de base des ressources EC2, des outputs et du state Terraform
Préparation
Section intitulée « Préparation »Créez deux répertoires séparés pour distinguer l’infrastructure d’origine et la configuration qui va la reprendre :
mkdir -p ~/terraform-aws-import/{seed,manage}cd ~/terraform-aws-import/seed| Répertoire | Rôle |
|---|---|
seed/ | Crée l’instance de départ |
manage/ | Importe l’instance et la gère ensuite |
Cette séparation rend le workflow plus lisible. Vous voyez exactement quand la ressource change de “propriétaire” côté state Terraform.
Étape 1 — Créer une instance de départ
Section intitulée « Étape 1 — Créer une instance de départ »Pour obtenir un exemple reproductible, vous allez d’abord créer une EC2 avec Terraform. Ensuite, vous la retirerez du state du projet seed/ sans la supprimer dans AWS. C’est cette opération qui simulera une ressource déjà existante hors de votre state courant.
Créez versions.tf :
terraform { required_version = ">= 1.11.0"
required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } }}
provider "aws" { region = var.aws_region}Créez variables.tf :
variable "aws_region" { description = "AWS region" type = string default = "us-east-1"}
variable "instance_name" { description = "Name tag for the seed EC2 instance" type = string default = "terraform-import-seed"}
variable "instance_type" { description = "EC2 instance type" type = string default = "t2.micro"}Créez main.tf :
data "aws_ami" "ubuntu" { most_recent = true owners = ["099720109477"]
filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] }
filter { name = "virtualization-type" values = ["hvm"] }}
resource "aws_instance" "seed_vm" { ami = data.aws_ami.ubuntu.id instance_type = var.instance_type
tags = { Name = var.instance_name }}Créez outputs.tf :
output "instance_id" { description = "Instance ID of the seed VM" value = aws_instance.seed_vm.id}
output "instance_ami_id" { description = "AMI ID used by the seed VM" value = aws_instance.seed_vm.ami}
output "instance_type" { description = "Instance type used by the seed VM" value = aws_instance.seed_vm.instance_type}
output "instance_name" { description = "Name tag of the seed VM" value = var.instance_name}Créez terraform.tfvars :
aws_region = "us-east-1"instance_name = "terraform-import-seed"instance_type = "t2.micro"Vérification
Section intitulée « Vérification »terraform initterraform validateterraform apply -auto-approveNotez les outputs affichés. Vous allez les réutiliser dans la configuration manage/.
Étape 2 — Simuler une ressource existante hors state
Section intitulée « Étape 2 — Simuler une ressource existante hors state »Vous allez maintenant retirer cette instance du state du projet seed/ sans toucher à l’infrastructure AWS.
terraform state rm aws_instance.seed_vmCette commande est importante à comprendre :
- elle ne détruit pas l’instance EC2 ;
- elle supprime seulement la ligne correspondante dans le state local ;
- Terraform n’associe plus cette ressource à la configuration
seed/.
Vérifiez que l’instance existe toujours dans AWS :
aws ec2 describe-instances \ --instance-ids i-xxxxxxxxxxxxxxxxx \ --query 'Reservations[0].Instances[0].[InstanceId,State.Name,Tags]' \ --output tableRemplacez i-xxxxxxxxxxxxxxxxx par l’instance_id affiché à l’étape précédente.
Étape 3 — Importer l’instance dans une nouvelle configuration
Section intitulée « Étape 3 — Importer l’instance dans une nouvelle configuration »Passez dans manage/ :
cd ~/terraform-aws-import/manageLe point clé ici est le suivant : un import ne génère pas le code à votre place. Vous devez fournir une configuration suffisamment proche de la réalité, sinon le premier terraform plan voudra immédiatement remplacer la ressource.
Créez versions.tf :
terraform { required_version = ">= 1.11.0"
required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } }}
provider "aws" { region = var.aws_region}Créez variables.tf :
variable "aws_region" { description = "AWS region" type = string default = "us-east-1"}
variable "instance_id" { description = "Existing EC2 instance ID to import" type = string}
variable "instance_ami_id" { description = "AMI ID already used by the existing instance" type = string}
variable "instance_type" { description = "Instance type already used by the existing instance" type = string}
variable "instance_name" { description = "Name tag already used by the existing instance" type = string}Créez main.tf :
resource "aws_instance" "imported_vm" { ami = var.instance_ami_id instance_type = var.instance_type
tags = { Name = var.instance_name }}Créez outputs.tf :
output "imported_instance_id" { description = "Imported EC2 instance ID" value = aws_instance.imported_vm.id}
output "imported_instance_name" { description = "Imported EC2 instance Name tag" value = aws_instance.imported_vm.tags.Name}Créez terraform.tfvars avec les valeurs récupérées dans seed/ :
aws_region = "us-east-1"instance_id = "i-0abc123def4567890"instance_ami_id = "ami-00de3875b03809ec5"instance_type = "t2.micro"instance_name = "terraform-import-seed"Initialisez, puis importez :
terraform initterraform import aws_instance.imported_vm i-0abc123def4567890Après l’import, exécutez un plan :
terraform planSi la configuration correspond bien à la ressource réelle, vous devez obtenir un résultat proche de :
No changes. Your infrastructure matches the configuration.Étape 4 — Renommer la ressource avec moved
Section intitulée « Étape 4 — Renommer la ressource avec moved »Vous allez maintenant améliorer le nom logique de la ressource. imported_vm est correct pour un lab, mais pas très parlant dans un dépôt réel.
Remplacez main.tf par ceci :
resource "aws_instance" "production_web_server" { ami = var.instance_ami_id instance_type = var.instance_type
tags = { Name = var.instance_name }}
moved { from = aws_instance.imported_vm to = aws_instance.production_web_server}Mettez à jour outputs.tf :
output "imported_instance_id" { description = "Imported EC2 instance ID" value = aws_instance.production_web_server.id}
output "imported_instance_name" { description = "Imported EC2 instance Name tag" value = aws_instance.production_web_server.tags.Name}Relancez le plan :
terraform planVous devez retrouver l’idée suivante : Terraform comprend qu’il s’agit du même objet AWS et qu’il faut simplement déplacer son adresse dans le state.
Étape 5 — Introduire un drift volontaire
Section intitulée « Étape 5 — Introduire un drift volontaire »Un drift signifie que la réalité AWS ne correspond plus au code Terraform. Pour le provoquer de manière simple et réversible, modifiez seulement le tag Name avec l’AWS CLI.
aws ec2 create-tags \ --resources i-0abc123def4567890 \ --tags Key=Name,Value=manual-changePuis demandez à Terraform de comparer le state et l’infrastructure réelle sans préparer une modification d’infrastructure :
terraform plan -refresh-onlySortie attendue :
~ resource "aws_instance" "production_web_server" { tags = { "Name" = "manual-change" -> "terraform-import-seed" } }Le point important ici est que -refresh-only met l’accent sur la synchronisation de l’état. Vous voyez d’abord ce qui a changé, avant de décider si vous acceptez la dérive ou si vous remettez l’infrastructure en conformité.
Étape 6 — Corriger le drift
Section intitulée « Étape 6 — Corriger le drift »Pour ce guide, choisissez la stratégie la plus simple : le code reste la source de vérité. Vous allez donc remettre le tag AWS à la valeur décrite dans Terraform.
terraform apply -auto-approveVérifiez ensuite que le plan est redevenu neutre :
terraform planVous devez obtenir à nouveau :
No changes. Your infrastructure matches the configuration.Anatomie
Section intitulée « Anatomie »Import vs moved vs drift
Section intitulée « Import vs moved vs drift »| Concept | Question à laquelle il répond | Action |
|---|---|---|
| Import | ”Comment rattacher une ressource existante à Terraform ?” | terraform import |
| moved | ”Comment renommer une ressource sans la recréer ?“ | bloc moved {} |
| Drift | ”Comment savoir si quelqu’un a modifié l’infra hors Terraform ?” | terraform plan -refresh-only |
Pourquoi une ressource vide est insuffisante
Section intitulée « Pourquoi une ressource vide est insuffisante »Un import ne change pas l’infrastructure, mais Terraform a quand même besoin d’une configuration cohérente. Si votre bloc resource "aws_instance" est vide ou incomplet, Terraform risque de proposer une recréation au premier plan parce qu’il ne peut pas comparer correctement la configuration et l’objet AWS existant.
Bonnes pratiques
Section intitulée « Bonnes pratiques »1. Importer d’abord, refactoriser ensuite
Section intitulée « 1. Importer d’abord, refactoriser ensuite »Faites les choses dans cet ordre :
- importer la ressource ;
- obtenir un plan sans changement ;
- seulement après, renommer avec
moved.
Cela vous évite de cumuler plusieurs sources d’erreur en même temps.
2. Conserver les attributs structurants
Section intitulée « 2. Conserver les attributs structurants »Pour une EC2, les premiers attributs à aligner sont généralement l’AMI, le type d’instance et les tags principaux. Sans eux, Terraform peut considérer que la ressource doit être remplacée.
3. Provoquer un drift réversible pendant l’apprentissage
Section intitulée « 3. Provoquer un drift réversible pendant l’apprentissage »Modifier un tag est idéal : c’est visible, peu risqué et facile à remettre en conformité. Évitez de jouer avec des paramètres qui forcent un redéploiement complet tant que vous apprenez.
4. Documenter la raison d’un moved
Section intitulée « 4. Documenter la raison d’un moved »Quand vous laissez un bloc moved dans le code, faites-le correspondre à un vrai changement de structure : renommage d’une ressource, déplacement dans un module, réorganisation d’un fichier. Ce n’est pas un gadget ; c’est une trace de migration de state.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
Cannot import non-existent remote object | L’ID EC2 est incorrect ou l’instance n’existe plus | Vérifier instance_id avec aws ec2 describe-instances |
Le premier terraform plan après import veut remplacer l’instance | La configuration ne reflète pas la ressource réelle | Aligner l’AMI, le type et les tags avant d’importer |
Moved object still exists ou erreur de référence | Le bloc moved pointe vers une ancienne adresse incorrecte | Vérifier précisément from et to |
terraform plan -refresh-only ne montre rien | Le drift n’a pas réellement été introduit | Relire les tags AWS avec aws ec2 describe-tags |
terraform destroy dans seed/ ne détruit rien | La ressource a été retirée du state avec terraform state rm | Détruire depuis manage/, qui possède maintenant le state |
Nettoyage
Section intitulée « Nettoyage »La ressource est maintenant gérée par manage/. Détruisez-la depuis ce répertoire :
cd ~/terraform-aws-import/manageterraform destroy -auto-approverm -rf .terraform .terraform.lock.hcl terraform.tfstate terraform.tfstate.backup
cd ~/terraform-aws-import/seedrm -rf .terraform .terraform.lock.hcl terraform.tfstate terraform.tfstate.backupÀ retenir
Section intitulée « À retenir »terraform importrattache une ressource existante au state ; il ne génère pas votre code.- Une configuration réaliste est nécessaire avant l’import, sinon Terraform voudra corriger ou remplacer la ressource.
movedpermet de renommer une adresse Terraform sans recréer l’objet AWS.- Le drift correspond à un écart entre le code, le state et l’infrastructure réelle.
terraform plan -refresh-onlysert à observer cet écart proprement avant de décider quoi en faire.- Le répertoire qui possède le state courant est aussi celui qui doit détruire la ressource à la fin.