Terraform Interview Questions and Answers for DevOps and Platform Engineers

What Interviewers Are Testing

Strong Terraform answers connect the language to operational consequences. An interviewer usually cares less about memorized syntax than whether you can protect state, explain a surprising plan, design reviewable changes, and recover without guessing.

Use the Terraform theory course to build the underlying model, then use Terraform Labs to practice the CLI workflows these questions assume.

---

1. What is Terraform state, and why is it required?

State is Terraform's record of bindings between resource instances in configuration and remote provider objects, plus metadata and cached attributes needed to plan efficiently. For example, it records that fakecloud_database.orders manages provider ID db-81a2.

Configuration alone cannot reliably answer which real object a resource address owns. Teams should store state in a remote backend with access control, encryption, versioning, and locking where supported. State is sensitive even when outputs are marked sensitive, because raw snapshots can contain secret values.

2. What happens during terraform plan?

Terraform loads configuration and prior state, asks providers to refresh managed objects, builds a dependency graph, and calculates actions required to make remote objects match configuration. A plan can therefore reveal drift unrelated to the edited file.

A saved plan created with terraform plan -out=tfplan is preferable in CI because approval and apply can refer to the same artifact. Protect it: plan files can contain sensitive data and backend context. If remote objects change after planning, apply may fail or providers may detect conflicts; a saved plan is not a timeless guarantee.

3. How do implicit and explicit dependencies differ?

An expression such as subnet_id = module.network.subnet_id creates an implicit graph edge. Terraform can order operations and track the specific data dependency. depends_on adds an explicit dependency when behavior depends on another object but no attribute reference expresses it.

Prefer references. Overusing depends_on, especially on whole modules, makes plans conservative and can turn more values into unknowns. Use it for genuine hidden relationships, and leave a comment explaining why the dependency is not visible in an expression.

4. count or for_each?

Use count for conditional zero-or-one resources or fungible numbered instances. Use for_each for independently named objects. Removing an item from the middle of a list used by count shifts numeric identities; stable map keys avoid that churn.

The key is state identity, not personal syntax preference. When migrating existing instances, use moved blocks to map old index addresses to new keyed addresses without replacement.

5. How would you adopt an existing production resource?

Inventory the object, verify no state already owns it, write a resource block at its final module address, and add a declarative import block or run terraform import ADDRESS ID. Review the import plan and reject unexpected replacement or destruction.

After import, compare state and a refreshed normal plan. Iteratively represent intentional live settings in HCL until the plan is empty. Separate adoption from policy changes so reviewers can distinguish "manage what exists" from "change production."

6. How do you detect and resolve drift?

A normal refreshed plan detects differences between remote objects and configuration. In automation, terraform plan -detailed-exitcode returns 0 for no changes, 2 for changes, and 1 for an error.

Resolution requires an ownership decision: restore reality to declared configuration, or update configuration to accept an intentional external change. apply -refresh-only updates state to observed reality but does not rewrite HCL, so it is not a universal drift fix. Avoid refresh=false or broad ignore_changes merely to hide a diff.

7. When would you use CLI workspaces?

CLI workspaces are multiple state instances for one configuration and backend. They fit structurally identical preview or test stacks inside one trust boundary. They are not appropriate for system decomposition or environments needing separate credentials and access controls.

For conventional dev/staging/prod outside HCP Terraform, separate root directories calling shared modules usually make backend, credentials, variables, and CI policy more explicit. Also clarify that HCP Terraform workspaces are not the same abstraction as CLI workspaces.

8. How would you design a reusable module?

A good module has a cohesive purpose, typed inputs with descriptions and validation, narrow outputs, sensible defaults, pinned compatible provider requirements, and no embedded environment credentials. It exposes policy choices without forwarding every provider argument mechanically.

Avoid a giant module that creates networking, compute, databases, DNS, and monitoring for every possible team. Smaller composition boundaries reduce blast radius and make upgrades easier. Version shared modules and document breaking migrations with moved blocks where addresses change.

9. How do you keep Terraform secrets safe?

Do not hardcode credentials or commit tfvars containing secrets. Use short-lived workload identity or environment-based provider authentication. Encrypt remote state, restrict state and plan access, retain audited versions, and mask CI output.

Marking a variable or output sensitive prevents routine display, not storage. Secrets may still exist in state and plan files. When possible, store references to a secrets manager rather than secret material, and use provider features that avoid returning plaintext.

10. A state lock blocks an urgent deploy. What do you do?

Read the lock metadata, identify the owning run, and determine whether its Terraform process is still alive. Wait with -lock-timeout if legitimate contention may finish. If the owner is provably gone, back up state, confirm the exact backend/workspace, obtain peer review, and run terraform force-unlock LOCK_ID.

Never reach first for -lock=false; it disables the safety mechanism while another writer may exist. After unlocking, create a fresh refreshed plan because the interrupted run may have partially changed remote infrastructure.

11. How do you refactor resources into a module safely?

Moving the HCL changes addresses, so Terraform may propose destroy and create even though the remote object should remain. Add moved blocks mapping each old address to its new module address, then verify the plan reports moves without infrastructure actions.

Commit migration declarations with the refactor and keep them until all relevant states have crossed the migration. For a one-state repair, terraform state mv is available, but it is less reviewable and repeatable.

12. What belongs in a safe Terraform CI/CD pipeline?

Use immutable Terraform and provider versions, commit the dependency lock file, authenticate with short-lived least-privilege credentials, and run formatting, validation, security/policy checks, and a refreshed saved plan. Publish the plan for review, require approval for sensitive environments, serialize apply per state, and apply the reviewed artifact.

Production state and plan artifacts need encrypted restricted storage. Avoid routine -target, automatic force-unlock, unreviewed auto-apply, and long-lived cloud keys. Schedule drift detection separately and alert rather than automatically reconciling unknown changes.

13. When is -target appropriate?

-target is an exceptional recovery and troubleshooting tool, not a normal deployment strategy. It asks Terraform to focus on an address and its dependencies, so the result may not represent all changes required by the complete configuration.

Use it to recover from a specific broken dependency or bootstrap edge case, inspect the warning, then run a full plan immediately afterward. If every deployment needs targeting, split the configuration into smaller state boundaries instead of relying on partial graph execution.

14. How do lifecycle rules improve safety, and where do they fail?

create_before_destroy can preserve capacity during replacement if names and quota allow coexistence. prevent_destroy blocks planned destruction while the resource block remains configured. ignore_changes assigns selected attributes to another owner. replace_triggered_by ties replacement to another managed change.

These are not substitutes for provider-native deletion protection, backups, permissions, or architecture. A strong answer describes the boundary: for example, prevent_destroy cannot stop a console deletion and disappears if the whole block is removed.

How to Practice Your Answers

For scenario questions, structure the answer as evidence, decision, action, and verification. Say which state and credentials you would confirm, what output you expect, what would make you stop, and how a final full plan proves recovery. That operational reasoning is what separates someone who has read HCL from someone trusted to run it in production.