Terraform Interview Questions: Remote State & tfstate Management

Reviewed by Mark Dickie · Last updated

10 questions on this page you can answer and see marked on the spot. Go to the first one

Terraform remote state is the practice of storing your project's terraform.tfstate file in a shared, externally managed backend rather than on local disk, so that state persists across team members and CI runs. For an interview on this topic, you should know how backends like S3, Azure Blob, and Terraform Cloud differ in locking and consistency; what happens to state during terraform apply and terraform refresh; and how terraform state subcommands move, remove, or import resources. Expect questions about state locking mechanics, the risks of a corrupted or out-of-date state file, and the difference between terraform refresh and terraform apply -refresh-only.

ConceptWhat to know for the interview
Backend typesStandard backends (S3, GCS, Azure) store state remotely; enhanced backends (Terraform Cloud, local) also run operations.
State lockingA lock prevents concurrent writes; S3 uses DynamoDB, GCS uses object versioning + a lock file, Terraform Cloud locks at the workspace level.
terraform state list / showInspect resources tracked in state without modifying anything.
terraform state mv / rmRename or drop a resource from state without destroying the cloud object.
terraform importPull an existing cloud resource into state so Terraform can manage it going forward.
terraform refreshReconciles state with real infrastructure; in Terraform 0.15+ prefer terraform apply -refresh-only to avoid the standalone refresh command.

What does a Terraform interview test about remote state?

  1. Which backend you would choose for a team of 20 engineers and why locking matters at that scale.
  2. What happens to the state lock if the CI runner dies mid-apply and how you recover from a stuck lock.
  3. The difference between the state file stored in S3 and the state data Terraform loads into memory during a plan.
  4. Why committing terraform.tfstate to Git is considered harmful even for a small project.
  5. How terraform state push can repair a backend after a local run or a failed migration.

Key facts

  • Tarmac's Terraform interview questions cover 24 questions at difficulty 1–5 of 5.
  • Tarmac tracked 1,687 job postings asking for Terraform in September 2026.
  • Roles asking for Terraform advertise a median base salary of £80,000, across 613 job postings as of September 2026.
  • Tarmac last reviewed these Terraform interview questions on 13 October 2026.

At a glance

Questions24
Difficulty1–5 of 5
FormatsTrue / false, Short answer, Fill in the blank, Multiple choice, Multiple answer, Design exercise

What you'll review

  1. remote state

Practice questions

Try one before you open the answer. Pick an option and press Check; it's marked on the spot.

True or false: storing Terraform state in a remote backend lets multiple team members share a single state file, and the backend can lock state during writes to prevent concurrent modifications.#

Options

Show answer

True. A remote Terraform backend stores state in a shared location so a whole team works from one state file. Backends that support locking hold a lock during writes, so two concurrent runs cannot modify the state at the same time and corrupt it.

Why:

A remote backend keeps the state file in a shared, centralized location (for example an S3 bucket or an Azure Storage account), so every team member reads and writes the same state. Backends that support locking acquire a lock before a write, so two concurrent Terraform runs cannot modify the state at the same time. Both halves of the statement are true, so the answer is true.

What is a core benefit of storing Terraform state in a remote backend (such as S3, GCS, or Terraform Cloud) instead of a local terraform.tfstate file?#

Show answer

A remote backend lets every team member share the same state file, so everyone works from one consistent view of the infrastructure. Many remote backends also support state locking, which helps prevent two people from applying changes at the same time.

Why:

A remote backend stores the state file in a shared location that all team members and CI pipelines can read and write, so they all see the same record of what Terraform manages. A local state file exists only on one machine, which leads to divergent copies and conflicting changes. Many remote backends also provide locking, which is a further benefit of the same setup.

Most Terraform remote state backends support _____, which prevents two Terraform operations from writing to the same state file at the same time.#

Show answer

Most Terraform remote state backends support state locking, which prevents two Terraform operations from writing to the same state file at the same time.

Why:

The mechanism is state locking. Before a Terraform operation that modifies state begins, the backend acquires a lock; if another process already holds the lock, the run waits or fails. This prevents concurrent writes that could corrupt the state file or cause race conditions between team members.

In Terraform, what is the primary purpose of configuring a remote state backend (e.g., an S3 bucket with DynamoDB locking or an Azure Storage account) instead of relying on the default local terraform.tfstate file?#

Options

Show answer

A Terraform remote state backend lets the whole team share one authoritative state file and, when the backend supports locking, prevents concurrent writes from corrupting it. It does not automatically guarantee encryption, remove the need for plan, or store your module source code — those are separate concerns.

Why:

A remote state backend stores the state file in a shared, central location (such as S3, GCS, or Azure Blob) so that every team member works against the same authoritative state. Backends that support locking (via DynamoDB, Azure lease, etc.) also prevent two runs from writing simultaneously. Encryption at rest depends on the specific backend and bucket/container configuration — it is not automatic for every backend. Remote state does not remove the need for terraform plan, and it stores state data, not module source code.

Which statement about Terraform remote state is correct?#

Options

Show answer

A single Terraform root module can declare only one backend block. That is why teams that need data from another backend use terraform_remote_state data sources instead. Remote backends are preferred over version control because state files may contain secrets and version control does not provide state locking.

Why:

A root module can declare only one backend block, which determines where that configuration's state is stored. Teams that need to read state from another backend use terraform_remote_state data sources, which do not conflict with the single backend declaration. The other statements are wrong: state files can contain passwords, tokens, and access keys, so they should not be published or committed to version control, and version control provides no locking to prevent concurrent operations from corrupting state.

True or false: storing Terraform state files in a version control system such as Git is considered an anti-pattern, because state files can contain sensitive data and version control systems provide no built-in state locking to prevent concurrent modifications.#

Options

Show answer

Storing Terraform state in Git or another version control system is an anti-pattern. State files can hold secrets like passwords and access keys, and version control offers no state locking, so concurrent applies can corrupt state. Remote backends such as S3 with DynamoDB or Terraform Cloud avoid both problems.

Why:

True. The Terraform documentation recommends against storing state in version control. State files can contain sensitive values such as passwords, tokens, and access keys, which should not be exposed in a repository. Version control also has no built-in state locking, so two people running terraform apply at the same time could corrupt the state. Remote backends such as S3 with DynamoDB locking, Azure Blob Storage, GCS, or Terraform Cloud address both problems.

A team stores Terraform state in a local file committed to a Git repository, and several engineers run terraform apply from their own machines. Which problem does migrating to a remote backend (for example, S3 with DynamoDB) solve that local state cannot?#

Options

Show answer

Migrating to a remote backend such as S3 with DynamoDB solves concurrent-write corruption. Local state has no locking, so two engineers running terraform apply at once can overwrite each other's state updates. Remote backends add a lock, such as a DynamoDB table for the S3 backend, so only one write proceeds at a time.

Why:

Local state files have no locking mechanism, so two engineers running terraform apply at the same time can produce a lost-update race where the last writer's state overwrites the other's changes. A remote backend adds a lock (for the S3 backend, a DynamoDB table) so that only one write operation proceeds at a time. The other choices are wrong: local state does store resource attributes and IDs, plan can read local state, and a backend only stores state; it does not create resources.

True or false: the Terraform S3 backend provides state locking out of the box, so storing state in an S3 bucket with no additional lock configuration prevents concurrent terraform apply runs from corrupting the state.#

Options

Show answer

False. The Terraform S3 backend does not lock state out of the box. Locking must be enabled: with Terraform 1.10 and later, set use_lockfile = true to use a lock file in the bucket; older versions used a DynamoDB table via dynamodb_table. Without locking, concurrent applies can overwrite each other's state.

Why:

The S3 backend does not lock state by default. Locking has to be enabled explicitly. In Terraform 1.10 and later, setting use_lockfile = true makes the backend write a lock file to the bucket. Older versions required a separate DynamoDB table, configured with the dynamodb_table argument, to acquire and release locks. Without either mechanism, two concurrent runs can read the same state and overwrite each other's writes, so the statement is false.

You have an aws_s3_bucket resource named my_bucket tracked in Terraform state. You rename the resource block identifier from aws_s3_bucket.my_bucket to aws_s3_bucket.website_bucket in the configuration, without running terraform state mv. What does Terraform plan to do on the next terraform plan, and why?#

Show answer

Terraform plans to destroy the old resource at aws_s3_bucket.my_bucket and create a new resource at aws_s3_bucket.website_bucket. State is matched to configuration by resource address, so the old address no longer appears in the configuration and is marked for destruction, while the new address appears for the first time and is marked for creation. On apply, the bucket tracked by the old address would be destroyed (unless a prevent_destroy lifecycle guard blocks it) and a new bucket created under the new address. Running terraform state mv before planning would rewrite the state entry to the new address and avoid the destroy-and-create.

Why:

Terraform reconciles state with configuration by resource address, not by the physical object. Renaming the resource block changes the address, so the old address (aws_s3_bucket.my_bucket) has a state entry but no matching configuration and is planned for destruction. The new address (aws_s3_bucket.website_bucket) has configuration but no state entry and is planned for creation. Only terraform state mv rewrites the state entry to the new address so Terraform sees one continuing resource instead of a destroy plus a create.

In Terraform v1.x, the S3 backend is configured with a backend "s3" block inside the terraform block. Which of the following arguments are marked Required in the S3 backend schema, meaning they must be set explicitly and cannot be sourced from environment variables or defaults? Select all that apply.#

Options

Pick every one that applies.

Show answer

In Terraform v1.x, the S3 backend marks only bucket and key as Required. region can come from environment variables or provider defaults, and dynamodb_table only enables state locking, so both are Optional. workspace_key_prefix has a default value and is also Optional.

Why:

In the current Terraform S3 backend documentation (v1.x), only the bucket name and the object path for the state file are marked Required. The bucket argument names the S3 bucket and the key argument sets the path of the state file inside it. The region can be resolved from the AWS_DEFAULT_REGION or AWS_REGION environment variables or other provider defaults, so it is Optional. The DynamoDB table only enables state locking and is Optional. The workspace key prefix has a default value (env:) and is also Optional.

To configure remote state in Terraform, you declare a _____ block of type s3 inside the terraform configuration block. For the S3 backend, the three arguments _____, _____, and _____ are required at minimum to specify the bucket name, the state file path, and the AWS region, respectively. Use the Terraform argument names.#

Show answer

To configure remote state in Terraform, you declare a backend block of type s3 inside the terraform configuration block. For the S3 backend, the three arguments bucket, key, and region are required at minimum to specify the bucket name, the state file path, and the AWS region, respectively. Use the Terraform argument names.

Why:

The backend block is nested inside the terraform block, and its label selects the backend type, here s3. For the S3 backend, bucket names the S3 bucket, key is the object key (path) of the state file inside that bucket, and region identifies the AWS region where the bucket resides. These three arguments are the minimum required to point Terraform at a remote state object.

You are using an S3 remote backend configured with a DynamoDB table for state locking. A teammate runs terraform apply and it is still in progress. You then run terraform apply against the same workspace. What is the expected behavior of your command?#

Options

Show answer

Terraform fails to acquire the state lock and returns an error, so your terraform apply does not proceed. When a backend supports locking (e.g., S3 with a DynamoDB lock table), every state read/write acquires an exclusive lock. A concurrent operation finds the lock already held and fails fast rather than queuing or merging. You can inspect the lock with the reported lock ID and, if it is genuinely stale, use terraform force-unlock to release it.

Why:

When a remote backend supports locking (S3 + DynamoDB is the classic example), Terraform acquires a lock on the state file before any read/write operation such as apply, plan, or refresh. If a second process tries to acquire the lock while it is already held, Terraform does not queue or merge — it immediately fails with a lock-acquisition error and prints the lock ID so you can investigate or, if the lock is genuinely stale, run terraform force-unlock. The claim that your apply runs concurrently and Terraform merges both change sets is wrong because Terraform never bypasses or auto-merges state locks. The claim that your apply automatically queues behind the first is wrong because Terraform does not implement a wait/queue mechanism; it fails fast rather than waiting.

Two Terraform configurations manage different infrastructure stacks in the same AWS account. Configuration A stores its state in an S3 backend and declares several output blocks. Configuration B reads Configuration A using a terraform_remote_state data source. Which statement correctly describes what Configuration B can access from Configuration A's state?#

Options

Show answer

The terraform_remote_state data source in Terraform exposes only the root-module output values recorded in another configuration's state, and it does so read-only. It does not give access to managed resources, their full attributes, or any write capability. Managing those resources requires importing them into your own state or sharing the module that defines them.

Why:

The terraform_remote_state data source reads the state file of another configuration, but the only values it exposes for consumption are the root-module outputs recorded in that state, and it provides them read-only. It does not hand over the managed resources or their full attribute sets, and it grants no ability to change those resources. Input variables and local values are not stored in the state file, so they are not reachable either. To manage a resource from Configuration B, you would need to import it into B's state or share the module that defines it.

In a Terraform AWS backend configured to store state in S3 with a DynamoDB table for state locking, explain the specific role of the DynamoDB table and what problem it solves. Also name the Terraform command used to manually release a stuck lock.#

Show answer

The DynamoDB table holds a distributed lock that Terraform acquires before any state-modifying operation such as terraform apply or terraform destroy. This prevents concurrent runs, from multiple team members or CI pipelines, from writing to the same state file at once, which could corrupt the state or produce conflicting infrastructure changes. If a run is interrupted and the lock is left behind, terraform force-unlock <lock-id>, using the lock ID shown in the lock error message, releases it manually.

Why:

With an S3 backend and a DynamoDB table configured for locking, Terraform writes a lock entry to the table before any state-modifying operation, so only one process can change the state file at a time. Without this, two concurrent applies could overwrite each other's state updates, leading to corruption, lost track of resources, or inconsistent infrastructure. If a process is killed or loses connectivity, the lock can remain orphaned, and terraform force-unlock with the lock ID from the error message removes it manually.

A team manages a shared VPC and networking stack in Configuration A (state in S3 + DynamoDB). A separate application team needs to reference the VPC ID, subnet IDs, and security group IDs from that stack in their own Configuration B. Each configuration keeps its own state. What is the recommended approach to expose and consume those values across configurations? Name the Terraform constructs involved on each side.#

Show answer

In Configuration A, declare output blocks for the VPC ID, subnet IDs, and security group IDs. In Configuration B, add a terraform_remote_state data source that points at Configuration A's S3 backend, using Configuration A's bucket, state key, and region (the DynamoDB lock table is not needed to read outputs). Then reference the values as data.terraform_remote_state.networking.outputs.vpc_id, and similarly for the subnet and security group outputs. This keeps the two state files independent and exposes only the explicitly declared outputs as read-only data.

Why:

The canonical pattern for cross-configuration value sharing in Terraform is the terraform_remote_state data source. Configuration A declares the needed values as output blocks, and Configuration B reads them through a terraform_remote_state data source that points at Configuration A's state location (bucket, key, region). Each configuration keeps its own state lifecycle, and only the declared outputs are exposed as read-only data.

Your networking team manages a VPC in a separate Terraform configuration that uses an S3 remote backend at key network/terraform.tfstate. The networking configuration declares output "vpc_id" with the VPC's ID. Your application team's configuration needs that VPC ID to provision subnets in the same VPC. Write the data block that reads the networking team's remote state (using the S3 backend, with the bucket, key, and region as placeholders) and the expression that references the VPC ID. Explain why the data source is needed instead of querying the VPC directly.#

Show answer

data "terraform_remote_state" "network" { backend = "s3" config = { bucket = "<networking-state-bucket>" key = "network/terraform.tfstate" region = "<region>" } }

Reference the VPC ID as data.terraform_remote_state.network.outputs.vpc_id. The terraform_remote_state data source reads the outputs recorded in the networking configuration's state file, so the application configuration gets the value the networking team published. Each output is nested under the outputs attribute, so omitting .outputs produces an invalid path.

Why:

The terraform_remote_state data source reads the outputs map from another configuration's state file. Its backend argument names the remote backend ("s3") and its config argument carries the bucket, key, and region that match the networking configuration's backend. The VPC ID is then read as data.terraform_remote_state.network.outputs.vpc_id, because every output is exposed under the outputs attribute. Querying an aws_vpc data source would instead look up live AWS infrastructure by filter, not read the networking team's published output.

Your organization is adopting Terraform to manage AWS infrastructure across three environments (dev, staging, production) and five teams (networking, database, compute, security, observability). Each team owns separate Terraform configurations and provisions its own resources. Design a remote state management strategy that addresses state file isolation, state locking, encryption at rest, least-privilege access control, and cross-team output sharing. Describe your backend configuration approach, how you would organize state files, and how teams would safely reference each other's outputs.#

Show answer

Use an S3 backend in a dedicated, versioned, access-logged state bucket, with a lock table for concurrency control.

Isolation: give every environment-team pair its own state file through the key path, for example envs/prod/networking/terraform.tfstate and envs/dev/database/terraform.tfstate. Backend blocks do not accept variables, so each team's configuration hard-codes its own key, or the key is supplied per environment with terraform init -backend-config=envs/prod.hcl. Separate files mean a plan or apply by one team never touches another team's state.

Locking: configure dynamodb_table = "tf-state-locks" (partition key LockID, type String) so Terraform writes a lock row before any state-changing operation. On Terraform 1.10 and later, use_lockfile = true offers S3-native locking as an alternative; either way a concurrent apply on the same state fails instead of corrupting it.

Encryption: set encrypt = true in the backend, and for production set kms_key_id to a customer-managed KMS key so key policy controls who can decrypt state. Plain encrypt = true uses S3 SSE-S3 by default, so the KMS key is what adds granular control. Enable bucket versioning and block public access as well.

Least privilege: give each team's IAM role s3:GetObject, s3:PutObject and s3:DeleteObject only on its own key prefix (arn:aws:s3:::acme-tf-state/envs/*/networking/* for the networking role), plus s3:ListBucket restricted with an s3:prefix condition. Scope DynamoDB GetItem, PutItem and DeleteItem to the lock table, using a dynamodb:LeadingKeys condition so each team can only lock its own state path. Grant kms:Decrypt and kms:Encrypt on the CMK only to the roles that need them.

Cross-team sharing: the consumer reads the producer's state with the terraform_remote_state data source, for example

data "terraform_remote_state" "network" {
  backend = "s3"
  config = {
    bucket = "acme-tf-state"
    key    = "envs/prod/networking/terraform.tfstate"
    region = "us-east-1"
  }
}

then references data.terraform_remote_state.network.outputs.vpc_id. The networking team exposes only intended values through output blocks, and consumers are granted read access to that one state object. Be explicit about the caveat: this data source reads the whole state file, so granting it exposes every value in that state including secrets. For sensitive or high-volume sharing, the producer should instead publish the values to a store such as SSM Parameter Store, and consumers should read them with the matching data source, keeping the state file itself private.

Why:

A sound design answer must cover all five pillars the question names: isolation via separate per-environment and per-team state keys, locking via a DynamoDB lock table (or S3 native locking on Terraform 1.10+), encryption at rest via encrypt plus a customer-managed KMS key, least-privilege IAM scoped to key prefixes and lock keys, and cross-team sharing via the terraform_remote_state data source reading only declared outputs. The strongest answers also note that backend blocks cannot interpolate variables and that terraform_remote_state exposes the entire state file, so sensitive values need a separate publishing path.

Your team currently keeps terraform.tfstate on each engineer's laptop. Explain why you'd move to a remote backend (e.g. S3, Terraform Cloud) and what two problems it solves for a team.#

Show answer

A local state file means each engineer has their own copy, so the state diverges and is never authoritative; it also isn't backed up and can be lost or committed to git with secrets in it. A remote backend stores a single shared state in a central, durable location so the whole team plans and applies against the same source of truth. Crucially it enables state locking: while one apply holds the lock, others are blocked, preventing two people from writing state at once and corrupting it. It also gives durability/versioning (S3 versioning, backups) and keeps the (plaintext) state off laptops behind access controls and encryption at rest.

Why:

The two big wins are a single shared source of truth (no divergent laptop copies) and state locking to prevent concurrent applies from corrupting state. Strong answers also mention durability/versioning and that state holds secrets in plaintext, so a remote backend with encryption and access control is a security improvement too. This is the question that separates someone who has run Terraform solo from someone who has run it on a team.

Design a Terraform remote state management strategy for a company with 20 engineering teams, each managing their own infrastructure across 3 environments (dev, staging, production) in AWS. The company currently uses a single S3 bucket with no locking for all teams. Recent incidents include state file corruption from concurrent applies, accidental state deletion, and one team's terraform destroy removing resources that another team depended on. Propose a complete remote state architecture addressing state isolation, locking, access control, and disaster recovery.#

Show answer

State isolation: give every team-and-environment pair its own state object, for example s3://tf-state-prod/teams/{team}/{env}/terraform.tfstate, with prefix-level separation (or separate buckets per environment). A terraform destroy run by one team then only touches that team's state, and shared infrastructure such as a platform-owned VPC is exposed through outputs read with the terraform_remote_state data source, not written by other teams. Locking: enable state locking on the S3 backend so concurrent applies cannot write the same state. Use a DynamoDB table (partition key LockID) via dynamodb_table, or on Terraform 1.10+ use the native S3 lock file (use_lockfile = true) and retire the DynamoDB table. Access control: give each team an IAM role whose s3:GetObject, s3:PutObject, s3:DeleteObject permissions are scoped to its own key prefix, s3:ListBucket is limited to that prefix with a condition key (Terraform needs ListBucket for init and state-version listing), and lock-table actions are limited to the needed DynamoDB operations; there is no cross-team write access, and read-only access is granted for shared outputs. Centralize backend configuration in a shared module or Terragrunt so teams cannot misconfigure their backend. Disaster recovery: enable S3 versioning on every state bucket so prior state versions can be restored after accidental deletion or corruption, enable cross-region replication to a backup region (with a KMS key available there), encrypt state with SSE-KMS, enable MFA Delete so versions cannot be permanently removed without MFA, and set lifecycle rules that retain noncurrent versions for a defined period.

Why:

A complete answer must cover four areas: (1) state isolation, with separate state files per team and environment so one team's destroy cannot remove another team's resources, and shared infrastructure exposed through read-only remote state outputs; (2) locking, using a DynamoDB lock table (or the native S3 lock file on recent Terraform) to prevent concurrent writes from corrupting state; (3) access control, with least-privilege IAM scoped to each team's prefix, including the s3:ListBucket permission that the S3 backend needs for init and state-version listing, plus the lock-table actions, and no cross-team write access; and (4) disaster recovery, with S3 versioning for rollback, cross-region replication for regional loss, SSE-KMS encryption, and MFA Delete plus retention lifecycle rules to guard against accidental or malicious deletion. The answer is open-ended, so the grader should check that each of the four areas is addressed concretely rather than only named.

In Terraform's S3 backend, which AWS service is typically used to provide state locking, and what is the primary purpose of state locking? Answer in two parts: name the service, then explain the purpose.#

Show answer

DynamoDB is the AWS service typically used for state locking with the S3 backend, configured through the dynamodb_table attribute. State locking prevents concurrent terraform apply (or other state-writing) operations from reading and writing the same state file at the same time, which would otherwise cause lost updates or state corruption. (Newer Terraform versions can also lock using a lock file in S3 via use_lockfile, but DynamoDB is the traditional and most widely used choice.)

Why:

The S3 backend stores the state file in an S3 bucket but does not natively coordinate access to it, so the traditional locking mechanism is a DynamoDB table, referenced by the dynamodb_table setting in the backend block. Terraform writes a lock item to that table before reading, modifying, and writing state, and releases it afterward. The purpose of this lock is to stop two runs (for example, two team members or CI pipelines) from executing state-changing operations at the same time, since simultaneous read-modify-write cycles on one state file can silently overwrite each other's changes and corrupt the state. Recent Terraform releases also support S3-native locking through use_lockfile, so the answer names DynamoDB as the typical service rather than the only option.

In a B+ tree used as a database index, data values are stored only in leaf nodes while internal (non-leaf) nodes store only keys for navigation, and the leaf nodes are linked together in a linked list. Explain the two key advantages this structure provides over a standard B-tree for database query workloads.#

Show answer

Because internal nodes hold only keys (not data), more keys fit in each node, increasing the tree's fanout. A higher fanout means a shallower tree, so point lookups require fewer disk I/O operations to traverse from root to leaf. Second, because all data values reside in the linked leaf nodes, range queries and sequential scans can be served by following the leaf-level pointers after a single root-to-leaf descent, without repeatedly walking back up and down the tree.

Why:

The two advantages stem from the same structural split: storing data only in leaves raises fanout (fewer nodes to read for any lookup) and linking the leaves enables efficient range and sequential access. A correct answer must mention that data lives in leaf nodes and that the linked leaves support range queries; it should also reference at least one practical consequence such as higher fanout, a shallower tree, fewer I/Os, or efficient sequential scans.

Terraform CLI: terraform state push behavior. When you run terraform state push local.tfstate against a remote backend that already contains a state, what does Terraform check by default before overwriting the remote state? Name the checks and what happens when they fail.#

Show answer

Terraform verifies that the local state is a valid successor of the remote state before overwriting it. It checks that both states share the same lineage (a UUID assigned when the state is first created) and that the local state's serial number is greater than or equal to the remote state's serial number. If either check fails, meaning a different lineage or a local serial lower than the remote serial, the push is refused unless the -force flag is provided. This guards against overwriting a newer remote state with a stale local copy, which could cause Terraform to lose track of resources and produce drift.

Why:

terraform state push overwrites the remote state with a local copy, but only after verifying that the local state is a valid successor of the remote state. It checks that both states share the same lineage (a UUID assigned when the state is first created) and that the local state's serial number is greater than or equal to the remote state's serial. If either check fails, the push is refused unless the -force flag is provided. This guards against overwriting a newer remote state with a stale local copy, which could cause Terraform to lose track of resources and produce drift.

You are the platform engineering lead at a company with 50+ engineering teams. Each team manages its own Terraform configurations across three environments (dev, staging, prod) in AWS. Currently, every team stores state in a single shared S3 bucket with a DynamoDB lock table, using workspace names for isolation (e.g. team-payment-dev, team-payment-prod). A recent incident occurred where Team A's terraform apply overwrote Team B's state because both teams were accidentally configured with the same state key. The platform team owns the backend infrastructure and must enforce guardrails.#

Show answer

State Isolation

We replace the single shared S3 bucket + workspace-based isolation with a dedicated S3 bucket per team (e.g. tfstate-payment, tfstate-checkout). Within each team bucket, state keys are organized by environment: payment/dev/terraform.tfstate, payment/staging/terraform.tfstate, payment/prod/terraform.tfstate. This eliminates the collision class entirely: a misconfigured workspace name in one team's backend block cannot overwrite another team's state because the teams write to physically separate buckets. Workspaces are insufficient as a team-boundary isolation mechanism because they share the same backend configuration and S3 key prefix — if two teams accidentally use the same workspace name or state key, Terraform happily writes one state over the other with no warning.

Access Control & Encryption

Each team bucket uses SSE-KMS with a per-team KMS CMK (e.g. alias/tfstate-payment). The platform team owns the KMS keys and grants each team's IAM role kms:Decrypt, kms:Encrypt, and kms:GenerateDataKey on only their key. S3 bucket policies restrict s3:GetObject and s3:PutObject to the team's IAM role, scoped to the team's key prefix within the bucket. Since Terraform state is stored as a single S3 object per workspace, IAM policy conditions use the object key path in the resource ARN (e.g. arn:aws:s3:::tfstate-payment/payment/prod/terraform.tfstate) to enforce isolation. The platform team retains kms:CreateKey, kms:ScheduleKeyDeletion, and kms:RotateKey on all keys for centralized rotation. DynamoDB lock tables are either per-team or use a shared table with a per-team hash key namespace, with IAM policies scoped accordingly so that a team cannot acquire or release another team's lock.

Cross-Team State Sharing

Teams that need to consume another team's outputs use a terraform_remote_state data source:

data "terraform_remote_state" "payment" {
  backend = "s3"
  config = {
    bucket         = "tfstate-payment"
    key            = "payment/prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "tflock-payment"
  }
}

The consuming team's IAM role is granted read-only s3:GetObject on the specific state object and kms:Decrypt on the producing team's KMS key — no write access, no DynamoDB lock-table write access. terraform_remote_state exposes only the outputs block of the state, not raw resource attributes, so the producing team curates which values are shared by defining outputs explicitly. Importantly, the remote-state read does NOT acquire a state lock, so multiple consumers can read concurrently without blocking the producing team's applies.

Policy Enforcement & Audit

  1. Config-level secret prevention: All CI/CD pipelines run tfsec or checkov scans that flag resources with sensitive attributes not marked sensitive = true, and tflint rules that reject output blocks exposing secrets. Additionally, a custom Sentinel policy (for Terraform Enterprise/Cloud users) or OPA policy (for open-source) checks that all variable and output declarations handling secrets use sensitive = true. These tools scan HCL configuration, not state file contents — they prevent secrets from leaking through outputs but cannot detect a secret already embedded in a resource attribute in state.
  2. State-content secret scanning: Because config-level tools do not inspect state files, the platform team runs a scheduled job that pulls each team's state object via terraform state pull and pipes the JSON through trufflehog to detect plaintext secrets in resource attributes (e.g., a password hardcoded in a resource argument). If a secret is found, the job alerts the owning team and triggers credential rotation. This complements the config-level prevention above by catching secrets that slip through into state despite config policies.
  3. Backend standards: The platform team publishes a versioned Terraform backend module that teams must use. This module hardcodes encryption settings, key naming conventions, and DynamoDB table configuration. A wrapper script or Makefile enforces terraform init -backend-config= with only the team-specific values, preventing manual misconfiguration.
  4. Audit logging: AWS CloudTrail data events are enabled on every state bucket, logging every GetObject and PutObject call with the caller IAM principal, timestamp, and object key. This provides a tamper-resistant audit trail. S3 server access logs are also enabled as a secondary, lower-cost log for long-term retention, but CloudTrail is the primary audit mechanism because it integrates with SIEM tools and captures the IAM principal identity.

Zero-Downtime Migration

Migration is performed team-by-team in a phased rollout:

  1. Prepare: The platform team creates the new per-team bucket, KMS key, and DynamoDB table for a single pilot team.
  2. Pull: The team runs terraform state pull against the old shared backend, producing a local terraform.tfstate file.
  3. Push: The team updates their backend configuration to point at the new bucket and runs terraform state push terraform.tfstate to write the state to the new backend.
  4. Verify: The team runs terraform state pull on the new backend and diffs the JSON against the old state. The lineage UUID must match and the serial must be equal or higher. The team runs terraform plan — it should show no changes (or only the expected diff from the backend switch).
  5. Cutover: The team's CI/CD pipeline is updated to use the new backend. The old state object in the shared bucket is preserved (not deleted) until all 50+ teams have confirmed migration.
  6. Rollback: If any step fails, the team reverts their backend config to the old shared bucket — the old state object is untouched and immediately usable. No state loss occurs because state push to the new backend is additive; it does not delete from the old backend.

This process is repeated for each team, one at a time, with the platform team monitoring CloudTrail for any unexpected state access patterns during the migration window.

Why:

This is a staff-level design question because it requires synthesizing Terraform state internals (lineage/serial verification, terraform_remote_state lock-free reads, output-only exposure), AWS security primitives (SSE-KMS with per-team CMKs, IAM conditions on S3 object keys, CloudTrail data events), policy-as-code tooling (Sentinel/OPA/tfsec), state-content secret scanning (trufflehog), and a phased zero-downtime migration strategy — all at organizational scale with 50+ teams. The rubric criteria were corrected so that: (1) c1 and c2 no longer double-credit the IAM-prefix isolation explanation — c1 owns the isolation architecture and workspace-insufficiency argument, while c2 focuses on encryption, IAM scoping, and DynamoDB lock table isolation as a distinct concern; and (2) c4's enforcement mechanisms are split into config-level prevention (tfsec/checkov enforcing sensitive = true) and state-content scanning (trufflehog/gitleaks/Macie pulling state files), so the rubric no longer conflates scanning HCL config with scanning state file contents.

You are using the Terraform S3 backend with DynamoDB state locking. Consider the serial and lineage fields stored inside the terraform.tfstate JSON. Which of the following statements are true? (Select all that apply.)#

Options

Pick every one that applies.

Show answer

The lineage field is a UUID fixed when a state file is created, and serial is a counter that increases on each state write. During terraform state push, a local state with a lower serial than the remote state is rejected, which blocks stale overwrites independently of locking.

Why:

The lineage UUID is assigned once when a state file is created and is carried unchanged through pushes, pulls, and backend migrations, so it acts as the state's permanent identity. The serial is an integer that Terraform increments on every state write, which makes it monotonically increasing. During terraform state push, Terraform compares serials for state with the same lineage and refuses to overwrite remote state with a lower serial, which guards against stale overwrites even when no lock is involved. The statements that lineage is regenerated on each apply and that serial is random are false, because lineage is never regenerated and serial is a deterministic counter.

Related interview questions

Job market

See terraform salaries and hiring demand from live job postings.

Practise these until they stick

That's every question we hold on this topic, and the page marks what you pick. What it can't do is remember. A free account keeps every answer, and what you miss comes back until it's right: after a day, then at longer gaps.

Start with this topic

Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan

Interview booked? Practise the job ad instead

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.