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/lessons/tf-intermediate-08-import-block

lesson ── terraform-intermediate ── ~15 мин ── 5 шагов

Декларативный import: захват существующего ресурса

Бакет уже есть в облаке, его создали руками (или это 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 минут, без регистрации.

запустить sandbox →

stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    Создай бакет вне Terraform

    Эмулируем «legacy»: бакет создан руками через AWS CLI.

    bash
    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. Будем захватывать.

  2. 02

    Опиши ресурс и import блок

    В /home/student/tf-import/main.tf:

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

    bash
    terraform init

    ✓ Import блок описан. Plan покажет что будет.

  3. 03

    Plan показывает import заранее

    bash
    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 закрепит.

  4. 04

    Apply: бакет в state

    bash
    terraform apply -auto-approve

    После apply:

    bash
    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 должен быть чистым.

    bash
    terraform plan -detailed-exitcode
    echo "exit: $?"

    Exit 0 означает «No changes». HCL соответствует реальности. Если 2, есть diff, значит атрибуты в HCL не точно совпадают с реальными. Подгонять придётся.

    ✓ Бакет захвачен под Terraform-управление. Plan чистый.

    То же самое на OpenTofu

    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 для полной матрицы.

    • → OpenTofu parity
  5. 05

    Удали import блок (он отработал)

    После apply импорт-блок свою роль сыграл. Можно убрать:

    bash
    # вырезать 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-блок удалён, бакет всё равно под управлением. Захват завершён.

    Bulk-import через for_each (TF 1.7+)

    Десять legacy-бакетов с одинаковой структурой, один блок:

    hcl
    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

    Большой ресурс описать руками, мучение. Команда:

    bash
    terraform plan -generate-config-out=generated.tf

    Создаст файл generated.tf с черновиком resource-блока, все attributes выставлены из облака. Это черновик, нужно:

    • Убрать computed-only поля.
    • Удалить deprecated arguments.
    • Подогнать структуру под свой стиль.

    Не финальный артефакт, но экономит время.

    • → Импорт целиком
    • → Паттерны рефакторинга

Что ты узнал

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 блока

концепции

  • · import блок НЕ пишет HCL за тебя: это связь state↔облако, но resource блок ты пишешь сам
  • · -generate-config-out помогает черновиком, но требует ручной доработки
  • · После apply import-блок можно удалить из HCL: он отработал свою роль

← предыдущий

count vs for_each: массовое создание

следующий →

GitHub Actions, полный pipeline через act

Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки