skip to content

You delete a resource block from your Terraform configuration and run apply. The config no longer says anything about what that resource depended on, yet Terraform still destroys it in a safe order. How?

level: middleimportance: should knowfreq 38%

answer

  1. the config is gone, the entry is not
  2. edges written at apply time
  3. reverse the arrows to destroy
  4. dependent first, dependency second
  5. an unapplied reference means stale edges

basics

~20 s

Each instance in Terraform's state carries a dependencies list of the addresses it referenced at the last apply. For a resource whose block is gone, Terraform works entirely from that recorded entry and reverses the recorded edges to order the destroy.

solid answer

~50 s

State records more than identity. Every instance carries a `dependencies` list — the resource addresses that instance referenced when it was last applied — and that list is what survives the deletion of the configuration. When a state entry has no matching block, Terraform treats it as an object to destroy, and it takes the ordering from the recorded edges rather than the config, destroying in reverse: the dependent goes before the thing it depended on, so it never tries to delete a security group while an instance still references it. This is also why the recorded edges have to be current. If you added a reference and never applied, or the entry predates the dependency, the recorded list is stale and Terraform can attempt a destroy in the wrong order, which surfaces as a provider error about the object still being in use.

code

json · 16 lines
json
{
  "mode": "managed",
  "type": "aws_instance",
  "name": "web",
  "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
  "instances": [
    {
      "schema_version": 1,
      "attributes": { "id": "i-0abc123def456" },
      "dependencies": [
        "aws_security_group.web",
        "aws_subnet.private"
      ]
    }
  ]
}

go deeper

for a junior

Know that state remembers which resources depended on which, so Terraform can still remove things in a sensible order after you delete the code that declared them.

for a middle

Explain the mechanism: each instance carries a dependencies list of addresses recorded at apply time, and destroy walks those edges in reverse so a dependent goes before what it depended on.

for a senior

Bring the failure case: edges are only as current as the last apply, so an unapplied reference or a hand-removed entry produces a wrong-order destroy that fails at the provider mid-apply. Say how you sequence the change to avoid it.

for a principal

Own the review rule this implies: changes that both add relationships and remove resources should not land in one apply, and the standard for how removals reach production should reflect that recorded edges lag the code.

## The problem the recorded edges solve Dependency ordering during a normal apply comes from the configuration: Terraform reads your references and `depends_on` entries and builds a graph. Destroying a resource that is *still declared* — a replacement, say — is covered by that graph. The hard case is the orphan. You delete the block entirely. Now the configuration says nothing at all about that resource: not its type, not its provider, and not what it depended on. Terraform still has to delete the real object, and deleting things in the wrong order fails loudly in every cloud — a subnet with an attached network interface, a security group referenced by a running instance, an IAM role still attached to a service. The answer is that the state entry is self-sufficient. It contains everything needed to destroy the object without consulting the config: ```json { "mode": "managed", "type": "aws_instance", "name": "web", "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", "instances": [ { "attributes": { "id": "i-0abc123def456" }, "dependencies": ["aws_security_group.web", "aws_subnet.private"] } ] } ``` The entry names the provider to call, the object ID to delete, and the addresses this object depended on the last time it was applied. ## Reversal is the whole trick Create order follows the edges; destroy order reverses them. If `aws_instance.web` recorded a dependency on `aws_security_group.web`, then on the way out the instance must be deleted **first**, and only then the security group. Terraform gets that from the recorded list, not from re-deriving anything, so deleting both blocks in one commit still produces the correct sequence. This also means a destroy of an orphan is a genuinely different code path from an update. Terraform is not diffing anything — there is nothing to diff against. It is walking recorded entries with no configuration counterpart and deleting them in reverse-recorded order. ## When the recorded list is stale Because the edges are written at apply time, they describe the *last applied* relationship, not the current one. Two situations make them wrong: **Never applied.** You added a reference in the config but the change was never applied, so the recorded entry still reflects the older, edge-free relationship. Delete both resources now and Terraform may order the destroy by an obsolete graph. **Removed from state manually.** If the entry a resource depended on is no longer in state, the edge points at nothing and contributes no ordering. The symptom is always the same: an apply that fails partway through a destroy with a provider error saying the object is still in use, still attached, or has dependent objects. The remedy is equally boring — apply the reference-adding change first so the edges get recorded, then remove the resources; or destroy in two steps, dependent first. ## Implicit versus explicit, as it lands in state Both kinds of dependency end up in the same list. An implicit dependency — one resource's argument referencing another's attribute — and an explicit one both get recorded as an address in `dependencies`. Once written, state does not distinguish where the edge came from, which is convenient: destroy ordering works identically for either. ## What this tells you about state's role This is the clearest demonstration that state is not a cache of the configuration. If it were, deleting the config would delete the knowledge, and there would be nothing left to destroy or order. Instead, state is a self-contained description of the managed estate — enough on its own to identify every object, call the right provider, and take it all down in a safe sequence. The configuration describes what you *want*; state describes what *is*, including the shape of the relationships between the things that are. ## In an interview Say it in one line — "the dependency edges are recorded in the state entry, and destroy walks them in reverse" — then add the staleness caveat, because that is what turns a fact into experience. Candidates who assume Terraform re-reads the config, or that it asks the cloud provider what depends on what, are describing a system that could not delete an orphan at all.

  • What goes wrong if the recorded dependencies are stale when you delete two related resources at once?
    Terraform orders the destroy by an obsolete graph and can try to delete the depended-on object first, which the provider rejects because something still references it. The apply fails midway, leaving a partial destroy. Applying the reference-adding change before removing the blocks keeps the recorded edges current and avoids it.
  • Does Terraform look at the configuration at all when destroying a resource that is no longer declared?
    Not for that resource. Everything it needs is in the state entry: the provider to call, the type, the recorded object ID, the attributes, and the dependency list for ordering. The configuration is still needed for the run as a whole — provider configuration has to come from somewhere — but the orphan itself is driven entirely by its record.
  • Are explicit and implicit dependencies distinguishable in state?
    No. Both are written into the same dependencies list as plain resource addresses, with no marker for how the edge arose. That is fine for the purpose: destroy ordering only needs to know that an edge existed, not whether it came from a reference in an argument or from an explicit declaration.

saying these in an interview costs you the question

  • Terraform asks the cloud provider what depends on what
  • Deleting the block deletes the dependency information too
  • Destroy order is the same order as create order
  • Orphaned resources are destroyed in arbitrary order
  • Terraform re-reads the old config from version control

context