Hedronite · Cert Lesson · Cert-Prep / HashiCorp · Mon 2026-09-14

Terraform Associate: import blocks and moved blocks — GCS resource IDs

The exam asks how belief catches up to the world, and how addresses move when modules reshape the code.

Lesson Class: Cert-Prep (Terraform Associate 003 / Pro-depth)
Paired Ops: GCS import + moved module refactor
Paired Dev: Python plan JSON import/move/no-op census
Paired Go: GCS import-candidate census
Grounding: Lab 03 · Lab 11 · Brikman Ch.5 pp.296-297
Import
CLI and import blocks bind IDs to addresses.
Moved
Declarative address rewrite for modules.
IDs
GCS buckets import by name; read the docs.
Import enrolls. Moved renames. No-op proves the refactor.

<!-- hal:authoritative:yaml -->

The exam asks how belief catches up to the world, and how addresses move when modules reshape the code.

§I — Frame

09-11 Cert practiced partial backends and migration flags. 09-08 Cert practiced validation, checks, and terraform test. 08-03 Cert surveyed the full state-manipulation toolbox: CLI import, state mv, -replace, refresh-only, moved, removed. Keep that survey.

Today narrows to two instruments with a GCP concrete referent: the declarative import block and the moved block, applied to google_storage_bucket. Ops runs the Lab 03/11 path. You answer the Associate and Pro-depth questions that path implies.

§II — Objective map

Why import exists. Brikman Valid Plans Can Fail: plan only consults state. Out-of-band objects are invisible. Import creates the association between a configuration address and a remote ID.

CLI form (still tested).

terraform import google_storage_bucket.logs acme-app-logs-prod

First argument: address. Second: provider-specific ID. Resource docs specify the ID format. For GCS buckets, the name is the usual ID.

Declarative form (modern / Pro-depth).

import {
  to = google_storage_bucket.logs
  id = "acme-app-logs-prod"
}

Same binding, reviewable in code. Import does not author the resource block. The block must already describe the object you intend to manage.

**Moved vs state mv.**

moved {
  from = google_storage_bucket.logs
  to   = module.logs.google_storage_bucket.this
}

terraform state mv rewrites one local (or remote) ledger interactively or in automation. A moved block ships the rewrite with the module so every consumer inherits the rename. Published modules prefer moved. Exam stems that rename addresses without moved or state mv expect destroy/create.

Success criterion. Lab 11: final plan is a no-op. Import success alone is insufficient if attributes still drift.

§III — GCS ID discipline (cloud medium)

Associate stems often use AWS IDs (i-…, bucket name, IAM user name). Pro-depth and cloud-medium practice require switching vendors without panic.

ResourceTypical import ID
google_storage_bucketbucket name
aws_s3_bucketbucket name
azurerm_storage_accountresource ID path (subscription/resourceGroups/...)

Read the Import section for the resource type on the exam. Guessing the ID format is a common miss. Ops today uses the GCS name form so the muscle memory is not AWS-only.

§IV — Practice questions

Question 1
A GCS bucket was created in the console. You added resource "google_storage_bucket" "logs" matching its name. Which command or block attaches state without recreating the bucket?
tap to reveal

<details class="q-card"><summary>Reveal</summary> <p><code>terraform import google_storage_bucket.logs BUCKET_NAME</code> or an <code>import</code> block with the same <code>to</code> and <code>id</code>. Import never creates the remote object.</p> </details>

Question 2
You rename google_storage_bucket.logs to module.logs.google_storage_bucket.this in code only. The next plan shows destroy and create. What declarative fix preserves the object?
tap to reveal

<details class="q-card"><summary>Reveal</summary> <p>A <code>moved</code> block from the old address to the new address. Alternatively <code>terraform state mv</code> for a one-off ledger edit.</p> </details>

Question 3
Import succeeded. Plan still wants to change uniform_bucket_level_access. What failed?
tap to reveal

<details class="q-card"><summary>Reveal</summary> <p>Configuration drift: code and live attributes disagree. Align the resource block (or intentionally apply the change). Lab success is no-op, not merely import exit zero.</p> </details>

Question 4
Partial backend config chose network/dev.tfstate. You import a prod bucket into that working directory. What went wrong?
tap to reveal

<details class="q-card"><summary>Reveal</summary> <p>Wrong ledger. Init selects the state object; import writes into whatever state is active. Reconfigure to the correct key first (09-11 Cert adjacency).</p> </details>

Question 5
Why do published modules ship moved blocks instead of README instructions to run state mv?
tap to reveal

<details class="q-card"><summary>Reveal</summary> <p><code>moved</code> travels with the module version. Every consumer applies the address rewrite automatically. <code>state mv</code> is a per-workspace operator action and does not scale.</p> </details>

§V — Failure modes the exam loves

Import without a resource block. Error. Write configuration first.

**state rm while the block remains.** Next plan tries to create. Pair removal of belief with removal of code, or use a removed block (08-03 / Lab 26; out of today's narrow spine).

Confusing import with refresh-only. Refresh-only updates state from the world for objects already tracked. Import enrolls a new address.

Confusing moved with replace. -replace forces recreate of a tracked object. moved only changes the address.

§VI — Closing

Know CLI import and import blocks. Know moved versus state mv. Know that IDs are resource-specific and that GCS buckets use names. Demand the no-op plan. Backend selection remains prior art from 09-11.

Related