templatefile() and file(): External Content in Terraform
Scenario
You need to pass a startup script to an EC2 instance. The script is complex — it installs packages, configures services, and needs values from Terraform variables (like the S3 bucket name and region). Inlining a 30-line bash script directly in HCL is messy and hard to maintain.
templatefile() lets you store the script in a separate file and inject Terraform values into it. file() reads a static file without any variable interpolation.
- Create an external template file (userdata.sh.tpl) with Terraform variable placeholders.
- Use templatefile() to render it with actual values.
- Use file() to read a static file.
- Understand heredoc syntax for inline multi-line strings.
- Output the rendered template to verify variable substitution.
Additional Context
file() reads a file as a raw string — no variable substitution. Use it for static content like SSH public keys or policy documents.
templatefile() reads a file and substitutes ${var_name} placeholders with the provided variables. It also supports %{for ...} and %{if ...} directives for loops and conditionals inside templates.
Heredoc syntax (<<-EOT ... EOT) lets you write multi-line strings inline. The - prefix strips leading whitespace, keeping your HCL neatly indented.
templates/userdata.sh.tpl (template file)
#!/bin/bash
# This script is rendered by templatefile() — variables are injected by Terraform
echo "Setting up instance for environment: ${environment}"
echo "Region: ${region}"
echo "Bucket: ${bucket_name}"
# Install packages
yum update -y
yum install -y httpd
# Write config using Terraform-provided values
cat > /etc/myapp/config.env <main.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
}
provider "aws" { region = "us-east-1" }
variable "environment" { default = "dev" }
variable "team_members" { default = ["alice", "bob"] }
data "aws_caller_identity" "current" {}
locals {
bucket_name = "app-data-${var.environment}-${data.aws_caller_identity.current.account_id}"
# templatefile() — renders the template with variable substitution
rendered_userdata = templatefile("${path.module}/templates/userdata.sh.tpl", {
environment = var.environment
region = "us-east-1"
bucket_name = local.bucket_name
team_members = var.team_members
})
# Heredoc — inline multi-line string (alternative to external file)
inline_script = <<-EOT
#!/bin/bash
echo "Hello from ${var.environment}"
echo "Account: ${data.aws_caller_identity.current.account_id}"
EOT
}
resource "aws_s3_bucket" "data" {
bucket = local.bucket_name
tags = { Environment = var.environment, ManagedBy = "terraform" }
}outputs.tf
# See the rendered template — variable placeholders replaced with real values
output "rendered_userdata" {
value = local.rendered_userdata
}
output "inline_script" {
value = local.inline_script
}
output "bucket_name" {
value = aws_s3_bucket.data.id
}Workflow Commands
# Create the templates directory and template file first
mkdir -p templates
# (paste the template content into templates/userdata.sh.tpl)
terraform init && terraform apply
# View the rendered template
terraform output rendered_userdata
terraform destroy#!/bin/bash echo "Setting up instance for environment: dev" echo "Region: us-east-1" echo "Bucket: app-data-dev-123456789012" yum update -y yum install -y httpd cat > /etc/myapp/config.env <

