Template Files
templatefile()function = render template file ด้วย variables — สำหรับ user-data, config files, IAM policies
templatefile() Function
templatefile(path, vars)
path— path ไฟล์ templatevars— map ของ variables ที่ส่งให้ template
ตัวอย่างพื้นฐาน
main.tf
resource "aws_instance" "web" {
ami = "ami-12345"
instance_type = "t2.micro"
user_data = templatefile("user_data.sh.tpl", {
server_name = "web-${var.env}"
port = 8080
})
}
user_data.sh.tpl
#!/bin/bash
echo "Starting ${server_name} on port ${port}"
yum install -y httpd
echo "<h1>${server_name}</h1>" > /var/www/html/index.html
systemctl start httpd
หลัง render:
#!/bin/bash
echo "Starting web-prod on port 8080"
yum install -y httpd
echo "<h1>web-prod</h1>" > /var/www/html/index.html
systemctl start httpd
Template Syntax
ใช้ syntax เดียวกับ HCL:
Interpolation
echo "${variable_name}"
Directive
%{ if condition }
...
%{ else }
...
%{ endif }
%{ for item in list }
${item}
%{ endfor }
ตัวอย่าง: Conditional Logic
main.tf
resource "aws_instance" "web" {
user_data = templatefile("init.sh.tpl", {
install_monitoring = var.enable_monitoring
monitoring_url = var.monitoring_url
})
}
init.sh.tpl
#!/bin/bash
yum install -y httpd
%{ if install_monitoring ~}
# Install monitoring agent
curl -L ${monitoring_url} | bash
%{ endif ~}
systemctl start httpd
ตัวอย่าง: Loop
resource "aws_instance" "web" {
user_data = templatefile("config.tpl", {
services = ["nginx", "redis", "memcached"]
})
}
config.tpl
#!/bin/bash
%{ for svc in services ~}
yum install -y ${svc}
systemctl enable ${svc}
systemctl start ${svc}
%{ endfor ~}
หลัง render:
#!/bin/bash
yum install -y nginx
systemctl enable nginx
systemctl start nginx
yum install -y redis
systemctl enable redis
systemctl start redis
yum install -y memcached
systemctl enable memcached
systemctl start memcached
Whitespace Stripping
~}→ strip trailing whitespace${~ var}→ strip leading whitespace
ตัวอย่าง: nginx Config
resource "aws_instance" "web" {
user_data = templatefile("nginx.conf.tpl", {
domain = "example.com"
backend_servers = aws_instance.app[*].private_ip
})
}
nginx.conf.tpl
upstream backend {
%{ for ip in backend_servers ~}
server ${ip}:8080;
%{ endfor ~}
}
server {
listen 80;
server_name ${domain};
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
}
}
ตัวอย่าง: IAM Policy
resource "aws_iam_role_policy" "lambda" {
role = aws_iam_role.lambda.id
policy = templatefile("policies/lambda.json.tpl", {
s3_bucket_arn = aws_s3_bucket.data.arn
sns_topic_arn = aws_sns_topic.alerts.arn
})
}
policies/lambda.json.tpl
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "${s3_bucket_arn}/*"
},
{
"Effect": "Allow",
"Action": "sns:Publish",
"Resource": "${sns_topic_arn}"
}
]
}
ตัวอย่าง: Multi-Line YAML
resource "kubernetes_manifest" "deployment" {
manifest = yamldecode(templatefile("deployment.yaml.tpl", {
name = "myapp"
image = "nginx:1.25"
replicas = 3
namespace = "default"
}))
}
deployment.yaml.tpl
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${name}
namespace: ${namespace}
spec:
replicas: ${replicas}
selector:
matchLabels:
app: ${name}
template:
metadata:
labels:
app: ${name}
spec:
containers:
- name: ${name}
image: ${image}
ports:
- containerPort: 80
วิธีใช้ที่ดี
Pattern 1: Pass Object
resource "aws_instance" "web" {
user_data = templatefile("init.sh.tpl", {
config = {
env = "prod"
log_level = "info"
features = ["auth", "logging"]
}
})
}
init.sh.tpl
export ENV=${config.env}
export LOG_LEVEL=${config.log_level}
%{ for f in config.features ~}
echo "Enable feature: ${f}"
%{ endfor ~}
Pattern 2: Use jsonencode for JSON
resource "aws_instance" "web" {
user_data = templatefile("init.sh.tpl", {
db_config_json = jsonencode({
host = aws_db_instance.main.address
port = aws_db_instance.main.port
database = aws_db_instance.main.db_name
})
})
}
init.sh.tpl
echo '${db_config_json}' > /etc/app/db.json
Pattern 3: Heredoc Inline (no file)
ถ้าไม่อยากแยกไฟล์:
locals {
user_data = <<-EOT
#!/bin/bash
echo "Server: ${var.server_name}"
yum install -y ${var.package}
EOT
}
resource "aws_instance" "web" {
user_data = local.user_data
}
→ Heredoc ใช้ interpolation เหมือนกัน — แต่ไม่มี directive (%{ if })
ทางเลือก: file() function
ถ้าไม่ต้องการ interpolation:
resource "aws_instance" "web" {
user_data = file("user_data.sh") # อ่าน raw, ไม่ render
}
Common Pitfalls
1. Quote Hell
# ❌
templatefile("script.tpl", {
message = "He said \"hello\""
})
# ✅ ใช้ heredoc
templatefile("script.tpl", {
message = <<-EOT
He said "hello"
EOT
})
2. Multi-line String
# ❌ Newline ใน string ตรงๆ
templatefile("x.tpl", {
config = "line1
line2"
})
# ✅ Heredoc
templatefile("x.tpl", {
config = <<-EOT
line1
line2
EOT
})
3. Loop ที่ Empty
%{ for x in [] ~}
${x}
%{ endfor ~}
→ ไม่ render อะไร — OK ไม่ error
4. Undefined Variable
templatefile("x.tpl", {
foo = "bar"
})
x.tpl
${foo}
${baz} # ← Error: undeclared variable
ต้อง pass ทุก variable ที่ template ใช้
ตัวอย่าง: Real-World Bootstrap
main.tf
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
user_data = base64encode(templatefile("user_data.sh.tpl", {
cluster_name = var.cluster_name
monitoring_url = var.monitoring_endpoint
log_group = aws_cloudwatch_log_group.app.name
secret_arn = aws_secretsmanager_secret.app.arn
install_features = ["auth", "metrics", "tracing"]
}))
iam_instance_profile = aws_iam_instance_profile.web.name
}
user_data.sh.tpl
#!/bin/bash
set -e
# Install CloudWatch agent
yum install -y amazon-cloudwatch-agent
cat > /opt/aws/amazon-cloudwatch-agent/etc/config.json <<EOF
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [{
"file_path": "/var/log/app.log",
"log_group_name": "${log_group}"
}]
}
}
}
}
EOF
systemctl start amazon-cloudwatch-agent
# Fetch secret
SECRET=$(aws secretsmanager get-secret-value --secret-id "${secret_arn}" --query SecretString --output text)
echo "$SECRET" > /etc/app/secret.json
# Install features
%{ for feature in install_features ~}
echo "Installing feature: ${feature}"
yum install -y app-${feature}
systemctl enable app-${feature}
%{ endfor ~}
# Connect to cluster
echo "${cluster_name}" > /etc/app/cluster
systemctl start app
# Heartbeat to monitoring
curl -X POST ${monitoring_url}/heartbeat -d "host=$(hostname)"
สรุป
templatefile(path, vars)= render template- ใช้ interpolation
${var}+ directive%{ if },%{ for } - Whitespace stripping:
~},${~ ...} - ใช้สำหรับ user-data, IAM policy, K8s manifest, config file
- ทางเลือก:
file()(no render), heredoc (inline) - Pass complex types ด้วย
jsonencode()/yamlencode()
ต่อไป → Section 15: CI/CD & Workspaces