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
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/lessons/tf-intermediate-07-state-manipulation

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

state mv, state rm: operations on state

State is a file of records that say "resource address in HCL maps to a concrete object in the cloud". Sometimes the address has to change (a rename, a move into a module) while the cloud resource stays put. The CLI terraform state mv/rm does exactly that.

In this lesson you will create a bucket, rename it in the state, confirm the bucket was not recreated, then drop the resource from state without a destroy.

Since TF 1.1+ the declarative moved block is the better choice for most cases, and it gets its own lesson next. For now we cover the CLI, because sometimes it is the only thing that works.

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

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

запустить sandbox →

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

Шаги

  1. 01

    Create a bucket under management

    bash
    cd /home/student/tf-state-ops
    cat > main.tf <<'EOF'
    resource "random_id" "suffix" {
      byte_length = 4
    }
    resource "aws_s3_bucket" "logs" {
      bucket = "linuxlab-state-ops-${random_id.suffix.hex}"
    }
    EOF
    terraform init
    terraform apply -auto-approve
    terraform state list

    It should print:

    aws_s3_bucket.logs
    random_id.suffix

    ✓ The bucket is created under the address aws_s3_bucket.logs.

  2. 02

    Back up state before the operations

    bash
    terraform state pull > /tmp/state-backup.json
    ls -la /tmp/state-backup.json
    jq '.serial, .lineage' /tmp/state-backup.json

    This is your safety net. If something goes wrong, terraform state push /tmp/state-backup.json puts everything back.

    With an S3 backend plus versioning this duplicates the automatic protection, but the habit costs nothing.

    ✓ Backup done. Now: state mv.

  3. 03

    Rename a resource in state

    We want to rename aws_s3_bucket.logs to aws_s3_bucket.log_storage.

    bash
    terraform state mv aws_s3_bucket.logs aws_s3_bucket.log_storage

    Output:

    Move "aws_s3_bucket.logs" to "aws_s3_bucket.log_storage"
    Successfully moved 1 object(s).

    Now you must fix the HCL, otherwise the plan shows a destroy on log_storage and a create on logs:

    bash
    sed -i 's/"aws_s3_bucket" "logs"/"aws_s3_bucket" "log_storage"/' main.tf

    Check:

    bash
    grep -n "aws_s3_bucket" main.tf
    terraform state list
    terraform plan

    State and HCL agree. Plan: No changes.

    ✓ The address is renamed, and the cloud bucket was not recreated.

  4. 04

    Try state rm in dry-run mode

    We want to drop the resource from management, but not delete the bucket in the cloud.

    First a dry-run:

    bash
    terraform state rm -dry-run aws_s3_bucket.log_storage

    It shows:

    Would remove:
      aws_s3_bucket.log_storage

    No changes. This is your sanity check. Always dry-run before the real rm. See tf-state-manipulation.

    ✓ The dry-run worked. Now: the real rm.

  5. 05

    Drop the resource from state

    bash
    terraform state rm aws_s3_bucket.log_storage

    Output:

    Removed aws_s3_bucket.log_storage
    Successfully removed 1 resource instance(s).

    Check:

    bash
    terraform state list

    aws_s3_bucket.log_storage is gone. In the cloud, take a look:

    bash
    aws --endpoint-url=http://localstack:4566 s3 ls

    The bucket is still there. State "forgot" it, but it really exists. That bucket is now ownerless as far as terraform is concerned.

    If you run terraform apply now without editing the HCL, Terraform sees "log_storage is in HCL, not in state" and tries to create it in the cloud. It fails with "bucket already exists".

    This is a normal state while preparing for import (the next lesson). To fix it right now, remove the resource from the HCL:

    bash
    # remove the aws_s3_bucket.log_storage resource block
    cat > main.tf <<'EOF'
    resource "random_id" "suffix" {
      byte_length = 4
    }
    EOF

    ✓ State let go of the resource, and the cloud bucket stayed. That is state rm.

    The same thing on OpenTofu

    OpenTofu keeps the CLI and state compatible with Terraform for the commands in this step: migration usually goes through mv .terraform .terraform.bak; tofu init -upgrade. On a first switch, though, back up the state and do a run on a feature branch, the differences cluster in the newer features (variables in backend, state encryption, OCI registry-backed modules). See tf-opentofu-parity for the full matrix.

    • → OpenTofu parity
  6. 06

    Restore from the backup

    If you wanted things back the way they were:

    bash
    terraform state push /tmp/state-backup.json

    Terraform checks lineage and serial:

    • Lineage in the backup == current, OK.
    • Serial in the backup < current, it asks for confirmation (this is a potential rollback).
    • Lineage differs, it refuses. This guards against an accidental swap.

    -force skips the checks, but you usually do not need it.

    bash
    terraform state list

    You see all the resources from the backup. This is your safety net.

    In real work with an S3 backend the backup happens automatically (versioning). Restore is done with aws s3 cp s3://.../tfstate?versionId=....

    ✓ Restore is possible. A backup before these operations is basic hygiene, not paranoia.

    When to use CLI state mv/rm, and when the moved/removed block

    The main rule: declarative beats imperative.

    CaseWhat to use
    Rename (one person, in the current sprint)state mv is faster, but moved is better for the PR
    Rename (in a team, in the repo)the moved block, it repeats for everyone
    Drop from state without destroythe removed block (TF 1.7+): visible in the diff
    Emergency "get this resource out of the way while I figure it out"state rm
    Merging state from different rootsstate mv with -state-out
    Fixing broken state from a dumpstate push (the only way)

    Rule: in a production repo the CLI is a last resort. Try the declarative blocks first. The CLI leaves the operation with no trace in git, and six months later nobody remembers why bucket.logs became bucket.log_storage.

    • → moved block
    • → removed block

Что ты узнал

terraform state mv <SRC> <DST> changes an address in state without touching the cloud. terraform state rm <ADDR> drops a record from state while the cloud resource stays. Before either one, run terraform state pull > backup.json.

команды

  • terraform state listwhat is in state: your starting point
  • terraform state pull > backup.jsona backup before a dangerous operation
  • terraform state mv SRC DSTrename an address
  • terraform state rm -dry-run ADDRwhat rm would do, with no changes
  • terraform state push backup.jsonrestore state from a dump

концепции

  • · state mv changes state, not HCL: keeping HCL in sync is your manual work
  • · state rm does not delete the cloud resource: it only breaks the link
  • · A state backup before any dangerous operation is mandatory

← предыдущий

Data sources: reading what already exists

следующий →

Plan as an artifact, between PR and apply

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