Documentation · What it is

Records

Ownership lives on the resource, as a marker. Everything else a run has to remember between applies lives in the record store: one record for every managed resource instance, written by choudoufu, in a place you choose.

Every managed instance has a record, of one of two kinds, in one of three stores

Two kinds of record

kindWritten forHoldsLosing it costs
identityA resource with a live object, which is most of themWhat a read of the live object cannot give back: an import identity, arguments the API never returns, whether a create-time provisioner ran, and on Kubernetes the label and annotation keys the configuration last declaredA slower or noisier plan. Ownership is the marker and does not depend on the record
objectA record-backed resource, one with no live object: null_resource, terraform_data, random_*, time_*, tls_*The whole value, the provider’s private data, and which attributes were sensitiveThe resource. The record is the only copy

The resource tier lookup uses a similar word for a different thing. A record-carried type has a live object and no tags, so its record holds its identity. A record-backed type has no live object, so its record holds all of it.

The kind inside the record decides what a plan may do with a record that no configuration declares. Only an object record is proposed for destroy.

A record is never consulted for ownership, and its values are never folded into a marker: an identity-bearing argument is evaluated over var, local, path, terraform and tofu alone. A lost object record can still cost more than itself. name = "svc-${random_pet.suffix.id}" is the ordinary shape, and losing the pet’s record regenerates the pet, so everything named after it is proposed for create under a new name. Recover an estate has the procedure.

Three stores

BackendRecords areFor
localFiles in .tofu-records beside the moduleOne operator, or a demo. It is what an estate gets when it declares nothing
s3Objects in a bucket you ownA team on AWS
kubernetesSecrets in a namespace you ownA team on a cluster, with no AWS account involved
# estate.chdf.hcl
estate = "my-estate"

record_store "s3" {
  bucket = "my-records-bucket"
}

All three give the same guarantees; a run cannot tell them apart.

Every write is conditional. A create succeeds only if the record does not exist, and an update or a delete only if the record still has the version the writer read. A losing writer gets a named conflict and changes nothing. The remote stores lock nothing, so a crashed run leaves nothing held; the local store takes a lock file for one write. Two runs at once has the cases.

A run reads its estate’s records whole before it plans: the read is complete or the run fails, never a smaller estate.

Each record is its own object, tagged or labelled with the estate’s name the way a managed resource is. One bucket or cluster serves any number of estates, and your IAM or RBAC decides who reads which.

It holds secrets

A record store holds what a state file would have held. Under the default, strict { secrets = "store" }, a generated password is recorded in clear, and so is a database password the API never returns. Whoever can read the store can read them, so pick who that is before you pick a backend. Secrets in the record store says exactly what is recorded and what secrets = "refuse" changes.

Where things are stored has the layout of each store, and What you set up by hand has what must exist before the first plan.