What are data sources in Terraform and how do they differ from resources?
Quick Answer
Data sources read existing infrastructure information without creating or modifying anything. Resources create, update, and manage infrastructure. Data sources use a 'data' block to query providers for information like existing VPC IDs, AMI IDs, or IAM policies that your configuration needs to reference.
Detailed Answer
Data sources in Terraform are read-only queries against your infrastructure provider's API. Think of the difference like this: a resource block is like writing a check at a bank — it changes your account balance. A data source is like checking your account statement — it only reads information without modifying anything. Both interact with the same bank (cloud provider), but their intentions and side effects are fundamentally different.
Internally, when Terraform encounters a data block, it makes an API call to the provider during the refresh phase of plan or apply and stores the returned attributes in state. The provider plugin handles translating the data source's filter criteria into the appropriate API call — for example, data.aws_ami.ubuntu_latest with filters for name and owner translates into an EC2 DescribeImages API call with those filters. The returned attributes become available for reference in other parts of your configuration.
Data sources serve several critical purposes in production Terraform code. First, they bridge the gap between managed and unmanaged infrastructure. If your networking team manages VPCs outside of your Terraform project, you use data.aws_vpc to look up the VPC by tags or ID rather than hardcoding the VPC ID. This makes your code portable and resilient to infrastructure changes. Second, data sources enable dynamic configuration — instead of hardcoding the latest Ubuntu AMI ID and updating it manually, data.aws_ami queries for the most recent AMI matching your criteria every time you plan. Third, they connect separate Terraform projects through terraform_remote_state data sources.
A subtle but important difference is lifecycle behavior. Resources have full lifecycle management: Terraform creates them, tracks them in state, updates them when configuration changes, and destroys them when removed from code. Data sources have no lifecycle — they are read during plan/apply and their results are cached in state, but Terraform never creates, updates, or destroys the underlying infrastructure. If a data source query returns no results, Terraform fails with an error rather than creating anything.
One common gotcha is circular dependencies between data sources and resources. If you create an AWS security group with a resource block and then try to use a data source to read that same security group in the same configuration, you may hit issues during the first apply because the data source tries to read something that does not exist yet. The rule of thumb is: if Terraform creates it, reference it directly as a resource; if something else creates it, use a data source.
Data sources also respect provider authentication and permissions, so your Terraform IAM role needs read access to whatever the data source queries. In production, teams often need broader read permissions than write permissions because data sources query across account boundaries — for example, reading a shared services account's Route53 hosted zone to create DNS records in it.