Read information Terraform does not manage with data sources, and render configuration files from variables with templatefile — keeping generated config in step with your infrastructure.
Why: a data block fetches information rather than creating anything — an existing image ID, the current account, an already-created network. You query it once and reference the result like any resource, so your config adapts to the live environment instead of hardcoding values that drift.
# Read your environment (no resource is created)
data "external" "env" {
program = ["sh", "-c", "echo '{\"user\": \"'\$USER'\"}'"]
}
# A cloud example: look up the latest Ubuntu AMI to launch
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-*"]
}
}Why: a data source is read with data.type.name.attribute — almost the same as a resource, just prefixed with data. Terraform reads it during plan so the value is available before anything is created. Here a resource consumes a looked-up value.
resource "local_file" "whoami" {
filename = "whoami.txt"
content = data.external.env.result.user # value from the data source
}Why: you often need to generate a real config file — nginx.conf, an env file, a startup script — filled with values from Terraform. templatefile reads a template and substitutes variables into it. The template uses ${...} for values and can loop with %{ for ... } directives.
# templates/app.conf.tftpl
# server_name = ${name}
# %{ for p in ports ~}
# listen ${p};
# %{ endfor ~}
resource "local_file" "conf" {
filename = "app.conf"
content = templatefile("${path.module}/templates/app.conf.tftpl", {
name = var.environment
ports = [80, 443]
})
}Why: for short cases you do not need a separate file — the same ${...} interpolation and %{ for }/%{ if } directives work inside any HCL string. Use a heredoc (<<-EOT) for multi-line content. Keep big templates in files; inline is for the small stuff.
locals {
hosts = ["web1", "web2", "web3"]
hostfile = <<-EOT
# generated by terraform
%{ for h in local.hosts ~}
127.0.0.1 ${h}
%{ endfor ~}
EOT
}
resource "local_file" "hosts" {
filename = "hosts.generated"
content = local.hostfile
}