Terraform Tutorial — Combining Data Sources with Resources
The real power of data sources comes from combining them with resources. Instead of hardcoding IDs, you look them up dynamically.
Pattern 1: Dynamic AMI + EC2
Hardcoded (fragile):
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0" # hardcoded!
instance_type = "t3.micro"
}
Dynamic (robust):
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id # always latest
instance_type = "t3.micro"
}
Pattern 2: Existing VPC + New Subnet
Deploy into a VPC you don’t manage:
data "aws_vpc" "shared" {
tags = {
Name = "shared-services-vpc"
}
}
resource "aws_subnet" "app" {
vpc_id = data.aws_vpc.shared.id
cidr_block = "10.0.50.0/24"
tags = {
Name = "app-subnet"
}
}
Pattern 3: Dynamic AZs + Subnets
Create subnets across all available AZs:
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "public" {
count = length(data.aws_availability_zones.available.names)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet("10.0.0.0/16", 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "public-${data.aws_availability_zones.available.names[count.index]}"
}
}
Pattern 4: IAM Policy Attachment
Reference an AWS managed policy without knowing its ARN:
data "aws_iam_policy" "readonly" {
name = "ReadOnlyAccess"
}
resource "aws_iam_role_policy_attachment" "readonly" {
role = aws_iam_role.app.name
policy_arn = data.aws_iam_policy.readonly.arn
}
Pattern 5: Account ID in Resource Names
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
resource "aws_s3_bucket" "logs" {
bucket = "${data.aws_caller_identity.current.account_id}-${data.aws_region.current.name}-logs"
}
Produces: 123456789012-us-east-1-logs
What’s Next?
Practice everything with the data sources exercises.