lesson ── terraform-intermediate ── ~15 мин ── 5 шагов
Бакет уже есть в облаке, его создали руками (или это legacy из 2019).
Хочется управлять им через Terraform. Старый способ, terraform import
CLI. Новый (TF 1.5+): декларативный import блок прямо в HCL: plan
покажет что будет, apply закрепит.
В этом уроке создашь бакет «руками» через AWS CLI, потом захватишь его в Terraform-state через import-блок.
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
Эмулируем «legacy»: бакет создан руками через AWS CLI.
cd /home/student/tf-import
aws --endpoint-url=http://localstack:4566 s3 mb s3://linuxlab-legacy-data
aws --endpoint-url=http://localstack:4566 s3api put-bucket-tagging \
--bucket linuxlab-legacy-data \
--tagging 'TagSet=[{Key=Origin,Value=manual},{Key=Year,Value=2019}]'aws --endpoint-url=http://localstack:4566 s3 ls
aws --endpoint-url=http://localstack:4566 s3api get-bucket-tagging --bucket linuxlab-legacy-data
Бакет существует. Terraform о нём ничего не знает.
✓ Бакет создан без Terraform. Будем захватывать.
В /home/student/tf-import/main.tf:
resource "aws_s3_bucket" "legacy" {bucket = "linuxlab-legacy-data"
tags = {Origin = "manual"
Year = "2019"
}
}
import {to = aws_s3_bucket.legacy
id = "linuxlab-legacy-data"
}
Разбор:
resource блок описывает желаемое состояние ресурса в HCL.
Атрибуты должны соответствовать реальным значениям бакета.
Если будет несовпадение, plan покажет diff после import.import { to, id }, инструкция terraform: «возьми ресурс с этим
cloud-ID и привяжи к этому адресу в state».id для S3, это имя бакета. Каждый AWS resource имеет свой
формат ID. См. Документацию provider'а.Init:
terraform init
✓ Import блок описан. Plan покажет что будет.
terraform plan
В выводе:
Terraform will perform the following actions:
# aws_s3_bucket.legacy will be imported
resource "aws_s3_bucket" "legacy" {bucket = "linuxlab-legacy-data"
...
}
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
Главное: 1 to import, не «to add». Это и есть отличие import блока от обычной декларации.
Если бы плана не было, было бы непонятно что произойдёт. Старый
CLI terraform import делает сразу, без plan'а.
✓ Plan показал импорт. Apply закрепит.
terraform apply -auto-approve
После apply:
terraform state list
terraform state show aws_s3_bucket.legacy
aws_s3_bucket.legacy в state. state show покажет все реальные
атрибуты бакета: имя, region, tags, ARN, hosted_zone_id и т.д. Всё что AWS отдаёт.
Главный тест: plan после import должен быть чистым.
terraform plan -detailed-exitcode
echo "exit: $?"
Exit 0 означает «No changes». HCL соответствует реальности. Если
2, есть diff, значит атрибуты в HCL не точно совпадают с реальными.
Подгонять придётся.
✓ Бакет захвачен под Terraform-управление. Plan чистый.
OpenTofu держит CLI и state совместимыми с Terraform по командам
этого шага: миграция обычно проходит через mv .terraform .terraform.bak; tofu init -upgrade. Но при первом переходе
сделай backup state и прогон на feature-branch - расхождения
концентрируются в новых фичах (variables в backend,
state-encryption, OCI registry-backed модули). См.
tf-opentofu-parity для полной матрицы.
После apply импорт-блок свою роль сыграл. Можно убрать:
# вырезать import блок, оставить только resource
cat > main.tf <<'EOF'
resource "aws_s3_bucket" "legacy" {bucket = "linuxlab-legacy-data"
tags = {Origin = "manual"
Year = "2019"
}
}
EOF
terraform plan
Plan: No changes. Бакет под управлением, импорт-блок не нужен.
Многие команды оставляют import блоки в коде как документацию
«когда-то этот ресурс был импортирован». Безопасно держать, после
apply Terraform их игнорирует.
✓ Import-блок удалён, бакет всё равно под управлением. Захват завершён.
Десять legacy-бакетов с одинаковой структурой, один блок:
variable "legacy_buckets" {type = set(string)
default = ["data-2019", "data-2020", "data-2021"]
}
resource "aws_s3_bucket" "legacy" {for_each = var.legacy_buckets
bucket = each.value
}
import {for_each = var.legacy_buckets
to = aws_s3_bucket.legacy[each.key]
id = each.value
}
Один HCL. N импортов. Особенно полезно при миграции с другого IaC (Pulumi, CloudFormation): есть список ресурсов, делаешь bulk-захват.
-generate-config-outБольшой ресурс описать руками, мучение. Команда:
terraform plan -generate-config-out=generated.tf
Создаст файл generated.tf с черновиком resource-блока, все
attributes выставлены из облака. Это черновик, нужно:
Не финальный артефакт, но экономит время.
import { to = ADDR, id = "CLOUD_ID" } в HCL связывает существующий
облачный ресурс с адресом в state. Plan показывает «will be imported».
Apply закрепляет. После, блок можно удалить. С TF 1.7+ работает for_each
в import-блоке для bulk-операций.
команды
aws --endpoint-url=$LS s3 lsпосмотреть что есть в облаке для импортаterraform planс import-блоком: покажет 'will be imported'terraform plan -generate-config-out=generated.tfсгенерить черновик resource блокаконцепции