Когда нужен import
Терраформ-проект не родился пустым. Бывает:
- Legacy инфраструктура. Бакеты, IAM-роли, VPC были созданы руками или другим инструментом. Хочешь начать управлять terraform. Нужно «захватить».
- Конфликт «уже создано». В одном проекте сделали
apply, в другом забыли state. Тот же ресурс пытаются создать дважды, облако ругается. Либоstate rmстарого +importв новый, либо разобраться кто хозяин. - Восстановление state. State потерян. Ресурсы в облаке остались. Import каждого. Это единственный путь.
Import не пишет HCL за тебя. Он только связывает «вот этот ресурс в HCL = вот этот ID в облаке». Сам ресурс в HCL описать должен ты.
Старый способ: terraform import
CLI-команда, работает со всеми версиями.
# 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.
# main.tf
import {to = aws_s3_bucket.legacy
id = "my-legacy-bucket-2018"
}
resource "aws_s3_bucket" "legacy" {bucket = "my-legacy-bucket-2018"
}
Запустить:
terraform plan
Покажет:
Terraform will perform the following actions:
# aws_s3_bucket.legacy will be imported
id = "my-legacy-bucket-2018"
...
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
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
сгенерить:
# main.tf. БЕЗ resource блока, только import
import {to = aws_iam_policy.legacy
id = "arn:aws:iam::123456789012:policy/legacy"
}
terraform plan -generate-config-out=generated.tf
В generated.tf появится сгенерированный resource "aws_iam_policy" "legacy" { ... } со всеми attributes. Прочти его глазами: там
бывают:
- Computed-only поля (provider их выставит сам, их можно убрать).
- Lifecycle-блоки, которых ты не хочешь.
- Несуществующие в новых версиях AWS-провайдера arguments.
Сгенеренный HCL, черновик, не финальная версия. Доработка обязательна.
Что после import
terraform plan, должен быть чистым (No changes).- Если показывает diff, значит атрибуты в HCL отличаются от реальных. Подгоняй HCL под облако (или намеренно ставишь HCL, тогда apply приведёт облако к HCL, но это рискованно для прода).
importблоки можешь удалить, после успешного apply они не нужны. Многие команды оставляют их в HCL как «документацию что было захвачено».
Подводные камни
-
Import не пишет HCL атрибутов. Только связывает state и облако. HCL, твоя работа. Без HCL
applyбудет пытаться разрушить ресурс (он в state, но нет в HCL). -
Module-ресурсы импортируются по полному адресу.
terraform import module.app.aws_s3_bucket.this <ID>. С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-outTerraform создаёт файл рядом, не интегрирует. Положитgenerated.tfв текущей директории. Дальше, твоё дело: правишь, дробишь по файлам, удаляешь то что не нужно. -
Secret-данные после import попадают в state. Если импортируешь
aws_db_instance, пароль БД будет в state в открытом виде. Это свойство импорта, не баг. См. tf-state про защиту state. -
Импорт не «магически переносит» теги, lifecycle и пр. Если у тебя в HCL
lifecycle { prevent_destroy = true }, в облачный ресурс LifecyclePolicy не уйдёт. Lifecycle, это terraform-метаданные.