linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/kb/State/tf-state-import

kb/state ── State ── intermediate

Импорт: захват существующего ресурса под Terraform

Импорт = «этот ресурс уже создан в облаке, начни им управлять». Старый способ: `terraform import <адрес> <cloud_id>`, ресурс в HCL надо написать руками. Новый (TF 1.5+): `import` блок прямо в HCL, plan показывает что будет, apply закрепляет. Импорт **не пишет HCL**, это твоя работа.

view as markdownaka: terraform-import, terraform-state-import

Когда нужен 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:

ResourceID format
aws_s3_bucketимя бакета: my-bucket
aws_iam_roleимя роли: my-role
aws_security_groupsg-id: sg-1234abcd
aws_route_table_associationсоставной: subnet_id/rtb_id
aws_vpcvpc-id: vpc-1234abcd
aws_instanceinstance-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 <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-out Terraform создаёт файл рядом, не интегрирует. Положит generated.tf в текущей директории. Дальше, твоё дело: правишь, дробишь по файлам, удаляешь то что не нужно.

  • Secret-данные после import попадают в state. Если импортируешь aws_db_instance, пароль БД будет в state в открытом виде. Это свойство импорта, не баг. См. tf-state про защиту 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 <ADDRESS>

После import проверить какие реальные атрибуты были захвачены.

§ см. также

  • tf-stateState: память Terraform о созданномState. JSON-файл terraform.tfstate, где Terraform записывает, что он создал в облаке. Без него Terraform не знал бы, какой бакет «его», а какой чужой. Содержит ID ресурсов, все атрибуты, и часто секреты. Самая чувствительная часть проекта.
  • tf-state-manipulationstate mv, state rm, state pull/push: ручные операции`terraform state mv` переименовывает адрес ресурса в state (без destroy/recreate). `terraform state rm` убирает ресурс из state (но не из облака). `terraform state pull/push`, скачать/залить state как файл. Все четыре, резкие операции, делать через backup и понимая зачем. Для декларативных альтернатив есть [[tf-moved-block]] и [[tf-removed-block]].
  • tf-moved-blockmoved блок: переименование без destroy`moved { from = ..., to = ... }` в HCL декларативно говорит Terraform: «этот ресурс раньше был по одному адресу, теперь по другому, в облаке тот же». Plan покажет «move», не «destroy + create». Появился в TF 1.1. Замена ручному `terraform state mv`, оставляет след в git, повторяется у всех в команде, видно в diff.
  • aws-providerAWS Provider: настройки и где Terraform берёт ключиAWS-провайдер ищет credentials в нескольких местах подряд: env-переменные, ~/.aws/credentials, IAM-роль инстанса. Чаще всего достаточно `aws configure` локально или роль на EC2, больше ничего не настраивать.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки