In this article
Before you start
This page is one step of the DevOps and cloud learning path.
Why interviewers ask this
A wrong idea about Terraform shows up as a deleted resource or as two engineers overwriting each other's changes, so these questions check the model behind the commands. They are reported by candidates in DevOps and cloud interviews at large tech companies, often as short "what is" and "why" prompts.
A weak answer lists commands: "init, plan, apply." A strong answer explains what each command reads and writes: the configuration, the state and the real infrastructure. It says why plan is safe to run, why apply respects ordering, and why a team needs shared, locked state. A Staff-level answer adds what goes wrong: secrets in the state file, drift after a manual change, and a depends_on added where none was needed.
Terraform and infrastructure as code from first principles
- Infrastructure as code (IaC)
- Infrastructure as code (IaC) is the practice of describing servers, networks and other cloud resources in text files that a tool reads and applies, so an environment can be reviewed, versioned and rebuilt in the same way as software.
- Terraform state
- Terraform state is a record, kept by default in a local file named
terraform.tfstate, that binds each resource declared in the configuration to the real object it created, plus metadata, so Terraform can tell what already exists and what must change. - Remote backend
- A remote backend is a shared data store, such as Amazon S3, Azure Blob Storage or Google Cloud Storage, where Terraform keeps its state so that a whole team uses one copy. Fully featured backends also support locking.
In plain words
Think of a building plan, a site inventory and an inspector. The plan says what the building should contain. The inventory lists what is really built. The inspector compares the two and writes a to-do list: add this wall, change that door, remove this window. The analogy breaks in one place: Terraform also carries out the to-do list itself.
In one precise sentence, Terraform keeps the desired picture in configuration files, the known picture in the state, and the real picture in the cloud, and every run reconciles them.
Each of the three pictures can be wrong on its own. The configuration may describe something nobody built yet. The state may be out of date if someone changed a resource by hand. The cloud holds the truth, but only a read of it reveals that. Terraform's job is to read all three and decide what to do.
Step 1: Configuration declares the result you want
A Terraform configuration is a set of text files that declare the resources you want, not the commands that create them. A declarative definition is one that states the result you want, not the steps to reach it. Microsoft Learn says a definition file "describes the components and configuration that an environment requires, but not necessarily how to achieve that configuration."
Input: your files. Output: a desired picture. Because the file states an end result, running it again on an unchanged environment should change nothing; a tool that behaves this way is called idempotent. You do type commands, but the configuration stays declarative, so "Terraform is imperative because I run commands" is a misconception. A first command, terraform init, "prepares your workspace so Terraform can apply your configuration". A provider is a plugin that maps resource blocks to the application programming interface (API) of a cloud or service, and terraform init downloads the providers and modules a configuration uses (HashiCorp). You now know where the desired picture lives; the next step is the known picture.
Step 2: State maps each resource block to a real object
Terraform state records which real object belongs to which resource block. HashiCorp's documentation says Terraform "uses your workspace's state to map real world resources to your configuration", keeps metadata and improves performance for large infrastructures. Its primary purpose is to store bindings between objects in a remote system and the resource instances declared in your configuration.
Without that record, Terraform could see a block named aws_instance.web and a running machine, but could not know they are the same thing. By default each workspace keeps the state in a local file named terraform.tfstate, with a backup of the previous state in terraform.tfstate.backup (HashiCorp). The file is plain text, and the documentation says not to edit it directly. Input: the objects Terraform created. Output: a durable map. State is therefore not an optional cache; every decision in the next step depends on it.
Step 3: Plan compares the configuration with the state
A Terraform plan is a preview of the changes a run would make, and it changes no infrastructure. The plan compares the configuration with the state before apply changes anything. HashiCorp's plan tutorial says Terraform creates the plan by comparing your configuration to the state of your infrastructure, and that it lists changes that create, update or destroy resources.
Before any operation, Terraform also does a refresh to update the state with the real infrastructure (HashiCorp). That is why a plan can reveal drift, which is a difference between the real infrastructure and what the configuration or state expects, usually after someone changes a resource by hand. Input: configuration plus state plus a read of reality. Output: a list of actions. In automation, HashiCorp's sequence is init, plan, human review, apply, and terraform plan -out=tfplan -input=false saves a plan for that review (HashiCorp); a continuous integration and delivery pipeline is where these steps are usually run.
Step 4: Apply runs the plan in dependency order
Terraform apply performs the approved plan in dependency order. HashiCorp's introduction says that on approval Terraform performs the proposed operations in the correct order, respecting any resource dependencies, and gives an example: if you change a network and the number of machines inside it, Terraform recreates the network before scaling the machines.
Terraform finds that order itself. A dependency graph is a map of which resource must exist before another can be created. Terraform builds one by studying the resource attributes used in expressions; a subnet that uses the network's id depends on the network. When Terraform cannot infer a link, you add an explicit one with the depends_on argument. Resources with no link between them are created in parallel, by default up to 10 at a time (apply command reference). Input: an approved plan. Output: changed infrastructure and an updated state.
Step 5: Remote state and locking protect a team
A team needs one shared copy of the state, and a lock around it. By default local state limits the ability to collaborate and risks losing the state if the file is lost. With remote state, Terraform writes the data to a shared store; HashiCorp lists its hosted service, Consul, Amazon S3, Azure Blob Storage and Google Cloud Storage among the options.
State locking is a backend feature that lets one run hold the state while it works, so a second run waits or fails. A backend with locking stops two runs from changing the same state at once, but the documentation says only fully featured backends offer it. On Amazon S3, setting use_lockfile = true creates a lock object named <key>.tflock next to the state, and that object must be in the access controls (HashiCorp S3 backend). In an illustrative simulation of two overlapping runs, an unlocked state records one of the two new resources and a locked one records both.1
Step-by-step description
- Configuration declares the result you want.
- State maps blocks to real objects.
- Plan reads both and previews the difference.
- Apply runs in order and updates state.
- Shared state with a lock protects a team.
- The plan compares the configuration with the state before apply changes anything.
- A backend with locking stops two runs from changing the same state at once.
Knowing it is half the job. Saying it is the other half.
Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.
Start Free Trial →Worked example
An illustrative configuration has four resources: a virtual private cloud (VPC), which is a private network inside a cloud account, a subnet, a security group and an instance. The subnet and the security group set their vpc_id from the network's id, and the instance uses both, so Terraform infers every link from attribute references.1
The virtual private cloud is created first, the subnet and security group follow together, and the instance comes last. In the illustrative example those three waves hold one, two and one resource, and destroying runs the waves in reverse.1
Text description of the diagram
- The virtual private cloud is created first, the subnet and security group follow together, and the instance comes last.
- Resources with no link between them are created in parallel.
| Run (illustrative) | Summary line (apply output; plan output when nothing changes) | What it shows |
|---|---|---|
| First apply | Resources: 4 added, 0 changed, 0 destroyed | everything is new |
| Plan with no edits | No changes. Your infrastructure matches the configuration. | the run is idempotent |
| Change a tag on the instance | Resources: 0 added, 1 changed, 0 destroyed | an in-place update |
| Change the network's address range, forcing replacement | Resources: 4 added, 0 changed, 4 destroyed | dependents are rebuilt |
The summary format "N added, N changed, N destroyed" follows the sample output in HashiCorp's core workflow, which shows one resource added. The four-resource counts are illustrative and computed in the script, not taken from a real account.1 The last row assumes the provider marks the network change as forcing replacement, which you must check in each provider's documentation.
| Fact | Value |
|---|---|
| Default local state file | terraform.tfstate |
| Backup of the previous state | terraform.tfstate.backup |
| S3 backend setting for native locking | use_lockfile = true |
| S3 lock object | <key>.tflock |
Trade-offs and edge cases
Terraform trades convenience for care: shared state, ordering and secrets each need a decision.
Local state versus remote state
Local state is a file on one machine; remote state is shared and can be locked. Locking is a feature of fully featured backends, not of remote storage alone. Local state needs no extra configuration, which suits a first experiment, but it limits collaboration and risks losing the state if the file is lost (HashiCorp).
State can contain sensitive values, such as database passwords (HashiCorp), so keep it out of version control and out of any store without locking and secure access control. Git, covered in the Git commands guide, holds the configuration; the state belongs in a backend.
Text description of the diagram
- Local state is a file on one machine; remote state is shared and can be locked.
- Locking is a feature of fully featured backends, not of remote storage alone.
When depends_on is needed
The depends_on argument is for a dependency Terraform cannot see from attribute references. HashiCorp describes it as the tool for the occasional case where Terraform cannot infer a link, and Google Cloud's guidance recommends it only as the last resort for a hidden dependency.
Adding it "to be safe" hides the real graph and can slow runs, because linked resources lose the parallel creation that independent ones get. Prefer referencing an attribute, which creates a real dependency and documents it.
Modules and drift
A module is a collection of resources that Terraform manages together (HashiCorp). The configuration in a workspace's root directory is the root module, and modules called with module blocks are child modules. Modules can load from the local file system, a registry or version control repositories, and version constraints work only for registry modules.
Drift needs a different answer: plan refreshes first and shows the difference, and you then decide whether to update the configuration or let apply undo the manual change. Terraform often provisions the cluster that Kubernetes then runs. An image built elsewhere, as in what a Docker image is, is an input to a resource, not something Terraform builds.
How to answer it in an interview
Explain how Terraform works and why state matters.
Terraform is an infrastructure as code tool. I write declarative configuration describing the resources I want. Terraform keeps a state file that maps each resource block to a real object. On plan it refreshes the state, compares it with my configuration and lists what it will create, update or destroy. On apply it makes those changes in dependency order, creating independent resources in parallel. For a team I keep state in a remote backend with locking so two runs cannot collide, and I keep it out of Git because it can hold secrets.
The spoken structure: say what it is, say what the state maps, walk through plan and apply, then name the team setup.
Common mistakes
- "State is just a cache." (wrong) It stores the bindings between real objects and resource blocks. (right) "State is how Terraform knows which object belongs to which block."
- "Plan changes the infrastructure." (wrong) Plan only previews. (right) "Plan compares configuration with state and lists changes; apply makes them."
- "Commit
terraform.tfstateto Git." (wrong) It can contain sensitive values and Git has no state locking. (right) "State goes in a remote backend with access control." - "Edit the state file by hand." (wrong) The documentation says not to. (right) "Use the
terraform statesubcommands, and only as a last resort." - "Remote state alone stops concurrent runs." (wrong) Locking is a backend feature. (right) "I choose a backend that supports locking and turn it on."
Follow-up questions
What is the difference between declarative and imperative infrastructure as code?
A declarative file states the result you want and the tool works out the steps; an imperative script lists the steps. Microsoft Learn says there is no standard syntax for declarative IaC, so the idea matters more than the language.
Why should you not commit terraform.tfstate to Git?
State can contain sensitive values, such as database passwords, and Git offers no state locking or state-aware access control. Commit the configuration; keep state in a remote backend.
What is state locking, and how does it work with Amazon S3?
Locking lets one run hold the state while others wait. On Amazon S3 you set use_lockfile = true and Terraform writes a .tflock object beside the state key; an older pattern used a separate DynamoDB table, so check the backend page for your version. For the wider platform, see Amazon Web Services interview questions for DevOps.
What does terraform import do?
It brings a resource that already exists under Terraform management by recording it in the state, as described on HashiCorp's import page. You still write a matching resource block, because the state binds an object to a block.
Workspaces or separate state per environment?
Terraform keeps a separate state for each workspace (HashiCorp), so one configuration can serve several environments. Many teams still prefer a separate configuration and backend per environment, because workspaces share the same backend and access controls; this is general practice, not a rule.
What if a resource was changed by hand?
Plan refreshes the state against the real infrastructure first (HashiCorp), so it shows the drift. You then either update the configuration to accept the change, or apply to change the resource back.
- Terraform compares declarative configuration with its state and applies only the difference.
- State binds each resource block to a real object; it is not an optional cache.
- Plan previews changes after a refresh; apply performs them in dependency order.
- Shared, locked remote state suits teams; keep state out of Git.
- Use
depends_ononly when Terraform cannot infer a dependency.
Sources
- State | Terraform — HashiCorp Developer, 26 Oct 2026
- State: Remote Storage | Terraform — HashiCorp Developer, 26 Oct 2026
- What is Terraform? and the core workflow — HashiCorp Developer, 26 Oct 2026
- Create a Terraform plan and automate Terraform — HashiCorp Developer, 26 Oct 2026
- Automate Terraform — HashiCorp Developer, 26 Oct 2026
- The core Terraform workflow — HashiCorp Developer, 26 Oct 2026
- Create resource dependencies — HashiCorp Developer, 26 Oct 2026
- Modules overview — HashiCorp Developer, 26 Oct 2026
- Backend Type: s3 — HashiCorp Developer, 26 Oct 2026
- What is infrastructure as code (IaC)? — Microsoft Learn, 26 Oct 2026
- Terraform dependency management best practices — Google Cloud documentation, 26 Oct 2026
- Providers — HashiCorp Developer, 26 Oct 2026
- Command: apply — HashiCorp Developer, 26 Oct 2026
- Manage sensitive data — HashiCorp Developer, 26 Oct 2026
- Import existing resources — HashiCorp Developer, 26 Oct 2026