Aller au contenu
Infrastructure as Code medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Terraform AWS — Import, moved et drift

16 min de lecture

logo terraform

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.

  • 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 moved sans 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

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.

  • Terraform >= 1.11 installé
  • AWS CLI configuré
  • Compréhension de base des ressources EC2, des outputs et du state Terraform

Créez deux répertoires séparés pour distinguer l’infrastructure d’origine et la configuration qui va la reprendre :

Fenêtre de terminal
mkdir -p ~/terraform-aws-import/{seed,manage}
cd ~/terraform-aws-import/seed
RépertoireRô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.

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"
Fenêtre de terminal
terraform init
terraform validate
terraform apply -auto-approve

Notez 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.

Fenêtre de terminal
terraform state rm aws_instance.seed_vm

Cette 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 :

Fenêtre de terminal
aws ec2 describe-instances \
--instance-ids i-xxxxxxxxxxxxxxxxx \
--query 'Reservations[0].Instances[0].[InstanceId,State.Name,Tags]' \
--output table

Remplacez 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/ :

Fenêtre de terminal
cd ~/terraform-aws-import/manage

Le 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 :

Fenêtre de terminal
terraform init
terraform import aws_instance.imported_vm i-0abc123def4567890

Après l’import, exécutez un plan :

Fenêtre de terminal
terraform plan

Si la configuration correspond bien à la ressource réelle, vous devez obtenir un résultat proche de :

No changes. Your infrastructure matches the configuration.

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 :

Fenêtre de terminal
terraform plan

Vous 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.

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.

Fenêtre de terminal
aws ec2 create-tags \
--resources i-0abc123def4567890 \
--tags Key=Name,Value=manual-change

Puis demandez à Terraform de comparer le state et l’infrastructure réelle sans préparer une modification d’infrastructure :

Fenêtre de terminal
terraform plan -refresh-only

Sortie 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é.

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.

Fenêtre de terminal
terraform apply -auto-approve

Vérifiez ensuite que le plan est redevenu neutre :

Fenêtre de terminal
terraform plan

Vous devez obtenir à nouveau :

No changes. Your infrastructure matches the configuration.
ConceptQuestion à laquelle il répondAction
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

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.

Faites les choses dans cet ordre :

  1. importer la ressource ;
  2. obtenir un plan sans changement ;
  3. seulement après, renommer avec moved.

Cela vous évite de cumuler plusieurs sources d’erreur en même temps.

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.

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.

SymptômeCause probableSolution
Cannot import non-existent remote objectL’ID EC2 est incorrect ou l’instance n’existe plusVérifier instance_id avec aws ec2 describe-instances
Le premier terraform plan après import veut remplacer l’instanceLa configuration ne reflète pas la ressource réelleAligner l’AMI, le type et les tags avant d’importer
Moved object still exists ou erreur de référenceLe bloc moved pointe vers une ancienne adresse incorrecteVérifier précisément from et to
terraform plan -refresh-only ne montre rienLe drift n’a pas réellement été introduitRelire les tags AWS avec aws ec2 describe-tags
terraform destroy dans seed/ ne détruit rienLa ressource a été retirée du state avec terraform state rmDétruire depuis manage/, qui possède maintenant le state

La ressource est maintenant gérée par manage/. Détruisez-la depuis ce répertoire :

Fenêtre de terminal
cd ~/terraform-aws-import/manage
terraform destroy -auto-approve
rm -rf .terraform .terraform.lock.hcl terraform.tfstate terraform.tfstate.backup
cd ~/terraform-aws-import/seed
rm -rf .terraform .terraform.lock.hcl terraform.tfstate terraform.tfstate.backup
  1. terraform import rattache une ressource existante au state ; il ne génère pas votre code.
  2. Une configuration réaliste est nécessaire avant l’import, sinon Terraform voudra corriger ou remplacer la ressource.
  3. moved permet de renommer une adresse Terraform sans recréer l’objet AWS.
  4. Le drift correspond à un écart entre le code, le state et l’infrastructure réelle.
  5. terraform plan -refresh-only sert à observer cet écart proprement avant de décider quoi en faire.
  6. Le répertoire qui possède le state courant est aussi celui qui doit détruire la ressource à la fin.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn