Terraform Interview Questions: State, Modules, and Real-World Scenarios
Most Terraform interview guides will walk you through `terraform init`, `plan`, and `apply` — the stuff you'd find in a 10-minute getting-started tutorial. That's not what gets you hired at…
Terraform Interview Questions: State, Modules, and Real-World Scenarios
Most Terraform interview guides will walk you through terraform init, plan, and apply — the stuff you'd find in a 10-minute getting-started tutorial. That's not what gets you hired at cloud-native companies. What they actually test is your understanding of *why* things work the way they do: state management under pressure, module boundaries, what happens when reality drifts from your config, and how you'd handle messy real-world situations.
Here's what senior-level Terraform interviews actually look like, and how to answer them well.
Why State Is the Heart of Every Terraform Question
Terraform's state file is where most interview questions live. It's the source of truth mapping your config to real infrastructure. If you can't talk confidently about state, you'll struggle with almost every scenario question.
The classic follow-up after "explain Terraform state" is: what happens when two engineers run terraform apply at the same time?
The answer is state corruption — unless you've configured state locking. With a remote backend like S3 + DynamoDB, Terraform acquires a lock before writing:
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/network/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}The DynamoDB table uses a LockID primary key. When Terraform starts, it writes a lock entry. When it finishes, it deletes it. If a process dies mid-apply, you get a stale lock — and you'll need to run terraform force-unlock manually after confirming no one else is running.
Tip for interviews: Don't just say "use remote state." Explain the locking mechanism, what can go wrong, and how you'd recover. That's what separates a senior answer from a junior one.
Module Design: The Questions That Trip People Up
Modules come up in almost every senior interview, and the question isn't usually "what is a module?" It's more like: *how do you decide what goes in a module vs. what stays in root config?*
A good rule of thumb: modules should encapsulate a logical unit of infrastructure that you'd want to reuse or version independently. A VPC module, an EKS cluster module, a database module — these make sense. A module that creates one S3 bucket probably doesn't.
Here's what a clean module call looks like:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0" name = "prod-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
}
Interview curveball: what's the risk of using version = "~> 5.0" vs pinning to "5.1.2"?
The ~> constraint allows minor version bumps. That's usually fine for well-maintained public modules, but in production you probably want exact pins and a deliberate upgrade process. Unpinned versions mean a terraform init -upgrade could silently pull in breaking changes.
Another common module question: how do you pass outputs from one module to another?
module "vpc" {
source = "./modules/vpc"
}module "eks" {
source = "./modules/eks"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
}
Terraform builds a dependency graph from these references. The eks module won't run until vpc completes. If you need to force an ordering that isn't captured by data references, you can use depends_on — but be careful, because it applies to the *entire* module and can cause unnecessary re-evaluations.
Handling Drift: The Scenario Question Everyone Gets Wrong
Drift happens when someone manually changes infrastructure outside of Terraform. The interviewer wants to know: how do you detect it, and what do you do about it?
Detection is straightforward:
terraform plan -refresh-onlyThis refreshes state against real infrastructure and shows you what's changed without proposing any resource modifications. It's the safest way to see drift without accidentally queuing up destructive changes.
What you do next depends on context:
terraform apply, which will revert the manual change.lifecycle { ignore_changes = [...] } to tell Terraform to stop fighting you on specific attributes.resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro" lifecycle {
ignore_changes = [ami]
}
}
This is useful when, say, an autoscaling process updates AMIs and you don't want Terraform to revert them on every apply.
Import Workflows: Bringing Existing Resources Under Control
terraform import comes up constantly in real-world scenarios, and interviewers love asking about it because it reveals whether you've actually done it in anger.
The workflow is:
terraform import to associate it with the stateterraform plan to see what attributes Terraform wants to changeterraform import aws_security_group.web sg-0a1b2c3d4e5fThe gotcha: Terraform import only updates state — it doesn't generate config for you (though terraform plan -generate-config-out in Terraform 1.5+ helps with this). If your config doesn't match the real resource, your next apply will try to modify or even destroy it.
In interviews, mention that you'd always do a dry-run plan immediately after importing, and you wouldn't proceed until the plan showed no changes.
Provider Versioning and the Dependency Graph
Two more areas that come up at senior levels:
Provider version conflicts happen when modules require different versions of the same provider. Terraform resolves this through version constraints in the required_providers block:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0, < 6.0"
}
}
}If two modules have incompatible constraints, terraform init will fail. The fix is usually to align constraints or update the module that's lagging behind.
The dependency graph is what Terraform uses to determine execution order. You can visualize it:
terraform graph | dot -Tsvg > graph.svgUnderstanding the graph matters when you're debugging why a resource is being modified unexpectedly, or when you need to explain why a change to one resource triggers a cascade of updates.
Actionable Next Steps
If you've got a Terraform interview coming up, here's how to prepare:
refresh-only plans.check blocks, import blocks, and config generation are fresh enough that knowing them signals you're current.The engineers who ace these interviews aren't necessarily the ones who've memorized the most commands. They're the ones who can reason through failure scenarios, explain tradeoffs, and talk about what they'd do when things go sideways. That's what you're preparing for.