11 — Data sources¶
Assumes: Outputs.
A data block is a read-only query. It fetches information about something that
exists, but does not create, update or delete it. The target can be something created
manually, by another team, or by a completely separate Terraform config and state.
data "google_compute_network" "existing" {
name = "default"
project = var.project_id
}
resource "google_compute_subnetwork" "sub" {
network = data.google_compute_network.existing.id
}
resource vs data¶
resource |
data |
|
|---|---|---|
| Lifecycle | owned (create/update/delete) | read-only |
terraform destroy effect |
deletes the real thing | only forgets it locally; real thing untouched |
| Refresh behaviour | refreshed to detect drift | re-queried live on every plan/apply |
| Tracked in state | yes, full lifecycle | yes, but only as a cached copy of the last read |
| If the real thing disappears | Terraform offers to recreate it | Terraform errors — nothing to manage, only something to look up |
Rule of thumb: if this config should be responsible for the resource's lifecycle →
resource. If it only needs to read something owned elsewhere → data.
Data sources are queried live, every time¶
A data block hits the provider API fresh on every single plan/apply. It does not
serve a remembered value from state. Delete the underlying resource outside Terraform and
the very next plan fails with a not-found error rather than quietly returning a stale
value.
That has a consequence worth planning for: every data source is a plan-time dependency on an external system being up. If the source (a Consul cluster, another project's API) is unreachable, the query fails and the plan fails. Terraform does not fall back to a last known good value.
Escape hatch:
Useful in an outage. Not a routine practice — you are now planning against possibly stale reality.
Why google_client_config is a data source, not a stored credential¶
data "google_client_config" "default" {}
provider "kubernetes" {
host = "https://${module.gke.endpoint}"
token = data.google_client_config.default.access_token
}
A GCP OAuth access token expires hourly. A data source that re-fetches it live on every run
is exactly the right tool. A resource would only update on apply, and only if Terraform
detected drift — so it would hand kubectl/Helm a stale, expired token much of the time.
The expiry window of the value is what decides data vs resource here, not
convenience.
The same reasoning applies to reading a secret from Secret Manager or Vault via data at
apply time: the value is fetched when needed and never written into the config.
The ownership boundary¶
Splitting ownership across configs — repo A owns the database as a resource, repo B reads
its connection details via a data block — lets several independent consumers share
information about one owned thing without any of them holding write permissions on it. That
pattern is big enough to get its own page:
Cross-config composition.
Key takeaway¶
resource = "I own this." data = "someone else owns this and I need to look at it." The
second one buys you a live value and costs you a hard runtime dependency on whatever you're
querying.
Labs: Data sources, Lab 7 · Quiz: Resources, state & drift · Next: Modules