# Импорт: захват существующего ресурса под Terraform _State · TerraformLab Knowledge Base_ **TL;DR:** Импорт = «этот ресурс уже создан в облаке, начни им управлять». Старый способ: `terraform import <адрес> `, ресурс в HCL надо написать руками. Новый (TF 1.5+): `import` блок прямо в HCL, plan показывает что будет, apply закрепляет. Импорт **не пишет HCL**, это твоя работа. ## Когда нужен import Терраформ-проект не родился пустым. Бывает: - **Legacy инфраструктура.** Бакеты, IAM-роли, VPC были созданы руками или другим инструментом. Хочешь начать управлять terraform. Нужно «захватить». - **Конфликт «уже создано».** В одном проекте сделали `apply`, в другом забыли state. Тот же ресурс пытаются создать дважды, облако ругается. Либо `state rm` старого + `import` в новый, либо разобраться кто хозяин. - **Восстановление state.** State потерян. Ресурсы в облаке остались. Import каждого. Это единственный путь. Import **не пишет HCL за тебя**. Он только связывает «вот этот ресурс в HCL = вот этот ID в облаке». Сам ресурс в HCL описать должен ты. ## Старый способ: `terraform import` CLI-команда, работает со всеми версиями. ```bash # 1. Опиши ресурс в HCL, пока без атрибутов из облака cat > main.tf <<'EOF' resource "aws_s3_bucket" "legacy" { bucket = "my-legacy-bucket-2018" } EOF # 2. Запусти import terraform import aws_s3_bucket.legacy my-legacy-bucket-2018 ``` Результат: - State теперь содержит `aws_s3_bucket.legacy` с **реальными** атрибутами из облака. - `terraform plan` сравнит HCL и state. Любое расхождение покажет как diff. ### ID зависит от типа ресурса Каждый AWS resource имеет свой формат import ID: | Resource | ID format | |---|---| | `aws_s3_bucket` | имя бакета: `my-bucket` | | `aws_iam_role` | имя роли: `my-role` | | `aws_security_group` | sg-id: `sg-1234abcd` | | `aws_route_table_association` | составной: `subnet_id/rtb_id` | | `aws_vpc` | vpc-id: `vpc-1234abcd` | | `aws_instance` | instance-id: `i-1234abcd` | Документация: в Registry-странице каждого resource внизу секция «Import». Если не знаешь ID, `aws s3 ls`, `aws iam list-roles`, `aws ec2 describe-*`. ## Новый способ: `import` блок (TF 1.5+) Декларативный, оставляет след в HCL, видно в plan. ```hcl # main.tf import { to = aws_s3_bucket.legacy id = "my-legacy-bucket-2018" } resource "aws_s3_bucket" "legacy" { bucket = "my-legacy-bucket-2018" } ``` Запустить: ```bash terraform plan ``` Покажет: ``` Terraform will perform the following actions: # aws_s3_bucket.legacy will be imported id = "my-legacy-bucket-2018" ... ``` ```bash terraform apply ``` После apply: - State содержит ресурс. - HCL содержит блок `import` (можно удалить, больше не нужен) и `resource`. Преимущества над CLI-командой: - **Видно в diff.** PR с `import` блоком показывает: «мы захватываем этот ресурс». - **План перед apply.** CLI-команда `terraform import` делает дело сразу. `import` блок, нет: `plan` сначала покажет, потом `apply`. - **Не на пустом ресурсе.** Если ты ошибся в HCL (например, бакет уже есть, ты хочешь импортировать, но в HCL пишешь имя другого): plan покажет конфликт. - **Поддерживает for_each.** `import` блок с `for_each` (TF 1.7+) позволяет захватить N ресурсов одним блоком. ### `import` блок с for_each ```hcl variable "legacy_buckets" { type = set(string) default = ["bucket-a", "bucket-b", "bucket-c"] } import { for_each = var.legacy_buckets to = aws_s3_bucket.legacy[each.key] id = each.value } resource "aws_s3_bucket" "legacy" { for_each = var.legacy_buckets bucket = each.value } ``` Один блок. N импортов. Особенно полезно при миграции с другой IaC-системы (Pulumi, CloudFormation), где список ресурсов известен. ## Генерация HCL: `-generate-config-out` Большой ресурс (например, `aws_iam_policy` с десятками statements) писать HCL руками, мучение. С TF 1.5+ можно попросить Terraform сгенерить: ```hcl # main.tf. БЕЗ resource блока, только import import { to = aws_iam_policy.legacy id = "arn:aws:iam::123456789012:policy/legacy" } ``` ```bash terraform plan -generate-config-out=generated.tf ``` В `generated.tf` появится сгенерированный `resource "aws_iam_policy" "legacy" { ... }` со всеми attributes. **Прочти его глазами**: там бывают: - Computed-only поля (provider их выставит сам, их можно убрать). - Lifecycle-блоки, которых ты не хочешь. - Несуществующие в новых версиях AWS-провайдера arguments. Сгенеренный HCL, **черновик**, не финальная версия. Доработка обязательна. ## Что после import 1. `terraform plan`, должен быть **чистым** (`No changes`). 2. Если показывает diff, значит атрибуты в HCL отличаются от реальных. Подгоняй HCL под облако (или **намеренно** ставишь HCL, тогда apply приведёт облако к HCL, но это рискованно для прода). 3. `import` блоки можешь удалить, после успешного apply они не нужны. Многие команды оставляют их в HCL как «документацию что было захвачено». ## Подводные камни - **Import не пишет HCL атрибутов.** Только связывает state и облако. HCL, твоя работа. Без HCL `apply` будет пытаться разрушить ресурс (он в state, но нет в HCL). - **Module-ресурсы импортируются по полному адресу.** `terraform import module.app.aws_s3_bucket.this `. С `import` блоком в HCL модуля можно объявить блок внутри модуля, `to = aws_s3_bucket.this`, terraform разрешит из контекста. - **`count`/`for_each` ресурсы, указывай индекс.** `aws_iam_user.user["alice"]`, не `aws_iam_user.user`. Если ошибся, импортируешь не туда, и план покажет destroy+create. - **Не все ресурсы импортируются.** AWS-провайдер поддерживает import у большинства, но есть exception: некоторые `aws_lambda_alias`, `aws_ssm_parameter_*` могут не иметь import handler. Документация по каждому ресурсу, внизу страницы «Import» либо «This resource cannot be imported». - **После `-generate-config-out` Terraform создаёт файл рядом, не интегрирует.** Положит `generated.tf` в текущей директории. Дальше, твоё дело: правишь, дробишь по файлам, удаляешь то что не нужно. - **Secret-данные после import попадают в state.** Если импортируешь `aws_db_instance`, пароль БД будет в state в открытом виде. Это свойство импорта, не баг. См. [tf-state](/terraform/kb/tf-state.md) про защиту state. - **Импорт не «магически переносит» теги, lifecycle и пр.** Если у тебя в HCL `lifecycle { prevent_destroy = true }`, в облачный ресурс LifecyclePolicy не уйдёт. Lifecycle, это terraform-метаданные. ## Команды ```bash terraform import aws_s3_bucket.legacy my-bucket-2018 ``` Старый CLI-способ. Сразу пишет в state, plan не показывает. ```bash terraform plan ``` С import-блоком в HCL: покажет что будет импортировано до apply. ```bash terraform plan -generate-config-out=generated.tf ``` TF 1.5+: сгенерить черновик resource блока. Дорабатывать обязательно. ```bash terraform state show
``` После import проверить какие реальные атрибуты были захвачены. ## См. также - [State: память Terraform о созданном](/terraform/kb/tf-state.md) - [state mv, state rm, state pull/push: ручные операции](/terraform/kb/tf-state-manipulation.md) - [moved блок: переименование без destroy](/terraform/kb/tf-moved-block.md) - [AWS Provider: настройки и где Terraform берёт ключи](/terraform/kb/aws-provider.md)