Skip to main content
RunBook Academy

TerraformVII · Resources, Data Sources, and count/for_eachData sources

Data Sources: Reading Infrastructure Terraform Does Not Manage

Foundation⏱ ~12 min🧪 Lab requiredbashterraform

What you'll learn

  • Distinguish managed resources from data sources
  • Read existing infrastructure via data sources
  • Use data sources to bridge configuration and existing resources
  • Recognise when data sources are the right tool

Prerequisites

Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-12

Not yet marked complete on this device.

A data source is a read-only reference to information that Terraform does not manage. Data sources are the bridge between the configuration and the real world when Terraform does not own the resource. This lesson teaches when data sources are the right tool, and how to use them safely.

Managed vs data sources

A managed resource is something Terraform owns:

resource "aws_instance" "web" {
  ami = "ami-0e1bed4f"
  # ...
}

A data source is something Terraform reads:

data "aws_ami" "ubuntu" {
  most_recent = true
  # ...
}

The difference:

AspectManaged resourceData source
CRUDTerraform creates, reads, updates, deletesTerraform reads only
StateThe resource is in stateThe data source is in state, but no attributes are stored
DriftTerraform detects and reconcilesTerraform only reads the current value
Production warningsAffects real-world resourcesNo side effects

When to use a data source

A data source is appropriate when:

  • The resource exists outside Terraform.
  • The resource is managed by another tool.
  • The resource is read-only (e.g. an AMI ID).
  • The resource is dynamic (e.g. the latest AMI).

A data source is not appropriate when:

  • The resource should be managed by Terraform (use resource).
  • The resource is dynamic and you want Terraform to track it (use resource).
  • The resource is a static configuration value (use variable).

Common data sources

The most common data sources are:

  • aws_ami — the latest Amazon Machine Image.
  • aws_caller_identity — the current AWS account.
  • aws_region — the current AWS region.
  • aws_availability_zones — the AZs in the current region.
  • azurerm_subscription — the current Azure subscription.
  • google_project — the current GCP project.
  • kubernetes_namespace — a Kubernetes namespace.
  • github_repository — a GitHub repository.
  • tls_public_key — a public key generated from a private key.

The provider documentation is the source of truth for the full list.

How to use a data source

A data source is declared with the data block:

data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]  # Canonical

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

The data source is referenced via data.<type>.<name>.<attribute>:

resource "aws_instance" "web" {
  ami = data.aws_ami.ubuntu.id
  # ...
}

The data source is read at plan time. The attribute is the providers response.

The output of a data source

A data source has attributes. The attributes are provider-specific:

output "ami_id" {
  value = data.aws_ami.ubuntu.id
  description = "The AMI ID used for the web server."
}

The data.aws_ami.ubuntu.id is the AMI ID. The output makes the value available to other configurations.

The state of a data source

A data source is in the state. The state records that the data source was read:

"data_sources": [
  {
    "type": "aws_ami",
    "name": "ubuntu",
    "provider": "...",
    "instances": [
      {
        "schema_version": 0,
        "attributes": {
          "id": "ami-0abc123def456789",
          "name": "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20210401",
          # ...
        }
      }
    ]
  }
]

The state records the data sources attributes as of the last refresh. The state is refreshed at plan time.

The dangerous pattern

A data source that reads the latest AMI is dangerous:

data "aws_ami" "ubuntu" {
  most_recent = true
}

The most_recent = true means “the AMI with the latest creation date”. A new AMI is published, and the next plan proposes to replace the instance because the AMI changed.

The fix is to pin the AMI by ID:

data "aws_ami" "ubuntu" {
  filter {
    name   = "image-id"
    values = ["ami-0e1bed4f"]
  }
}

The course has a dedicated lesson on provider upgrade unexpected plans (Part XCV).

What comes next

The next lesson is managed vs unmanaged infrastructure — the operational distinction between what Terraform owns and what it reads.

Verification

Knowledge check · 7 questions

  1. Q1. What is a resource address?

  2. Q2. When does a resource trigger replacement vs in-place update?

  3. Q3. `count` provides stable resource identity.

  4. Q4. When is for_each preferred over count?

  5. Q5. Which of the following are data sources used for? (Select all that apply.)

  6. Q6. What is the role of `lifecycle.ignore_changes`?

  7. Q7. A resource shows a +20 to change in the plan for an attribute that should be stable. What is the most likely cause?

Passing score: 75%. Answers are checked in this browser.