TFLAB17-templatefile() and file(): External Content in Terraform

templatefile() and file(): External Content in Terraform

🔧 Terraform Core ⭐ Intermediate templatefile() file() Heredoc Syntax User Data

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.

Your Objectives
  • 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
✓ Rendered Template Output
#!/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 <