linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Knowledge base
  • Cheat sheet
  • Capstone
  • Interview prep
home/terraform/lessons/tf-garden-03-import-mismatch

lesson ── terraform-garden ── ~16 мин ── 4 шагов

Troubleshooting Garden: import points at nothing

Someone added two import blocks to adopt existing resources and stop creating duplicates. The cloud holds those resources under one set of names, but import.id was typed with mistakes: "logs" vs "loggs", "ci-role" vs "ci-role-old". The plan fails with "Cannot import non-existent remote object".

Find both mismatches, fix them, and prove the plan runs with no destroy.

▶ интерактивный sandbox

Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.

запустить sandbox →

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

Шаги

  1. 01

    Run the plan, catch the import error

    bash
    cd /home/student/tf-garden
    terraform init
    terraform plan 2>&1 | tee /tmp/import.log

    Two error lines should show up, something like:

    Error: Cannot import non-existent remote object
      with aws_s3_bucket.logs,
      on main.tf line ...

    And a matching one for aws_iam_role.ci. This means the imported IDs garden-import-loggs / garden-ci-role-old are not present in the cloud.

    ✓ Both errors are visible. Now check what is actually in the cloud.

  2. 02

    Compare against reality: what is in LocalStack

    bash
    aws --endpoint-url=http://localstack:4566 s3 ls | tee /tmp/s3.log
    aws --endpoint-url=http://localstack:4566 iam list-roles \
      --query 'Roles[].RoleName' --output text | tee /tmp/iam.log

    This shows:

    • S3: garden-import-logs (without the second g).
    • IAM: garden-ci-role (without -old).

    These are your real ids. The typo is in the HCL, the cloud is correct.

    ✓ The real ids are found. Now fix the HCL.

  3. 03

    Fix the id in the import blocks

    Open main.tf and replace these lines:

    hcl
    import {
      to = aws_s3_bucket.logs
      id = "garden-import-logs"     # was: garden-import-loggs
    }

    and

    hcl
    import {
      to = aws_iam_role.ci
      id = "garden-ci-role"         # was: garden-ci-role-old
    }

    One symmetric way with sed:

    bash
    sed -i 's/garden-import-loggs/garden-import-logs/' main.tf
    sed -i 's/garden-ci-role-old/garden-ci-role/' main.tf
    подсказка

    The S3 typo is an extra g. The IAM typo is an extra -old suffix.

    ✓ Typos are gone. Now check the plan.

  4. 04

    The plan should show an import with no destroy

    bash
    terraform plan -out=plan.tfplan
    terraform show plan.tfplan | head -40

    The plan should now contain these lines:

    # aws_s3_bucket.logs will be imported
    # aws_iam_role.ci will be imported

    There should be no # ... will be destroyed. A destroy would mean the HCL describes the resource differently and the provider wants to recreate it. Here the HCL matches the cloud, so you should get a clean import.

    Apply (optional):

    bash
    terraform apply plan.tfplan

    ✓ Bucket and role are in state, with no destroy. The import block did its job.

    The same thing on OpenTofu

    OpenTofu has supported import blocks since 1.6. The behavior and the messages are identical. The one difference is that -generate-config-out is more stable on OT, because they patched a few early bugs with unicode names sooner. See tf-opentofu-parity.

    • → terraform import
    • → OpenTofu parity

Что ты узнал

Import adopts whatever you point at with id. If that id does not exist, the provider returns a 404 and Terraform complains about a "non-existent remote object". The cure is to check the id by hand through the provider CLI (aws s3 ls, aws iam list-roles), and better still, to wrap a guard around it with a data source.

команды

  • aws --endpoint-url=http://localstack:4566 s3 lscheck the S3 bucket names before import
  • aws --endpoint-url=http://localstack:4566 iam list-roles --query 'Roles[].RoleName'check the IAM role names
  • terraform plan -generate-config-out=imported.tfgenerate HCL for the resource you are importing (TF 1.5+)

концепции

  • · import.id is the address inside the provider (an S3 bucket name, an IAM role name, an EC2 instance id)
  • · Plan-on-import runs before apply, so the error is caught with no consequences
  • · A data-source guard in front of the import block is the pattern for legacy migrations

← предыдущий

Outputs: returning values to the outside

следующий →

for_each over a module: N instances from one block

Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies