Terraform Tutorial — String Interpolation and Types
HCL has a rich type system and powerful string interpolation. This page covers how to build dynamic strings, work with heredocs, and understand the primitive types that underpin every Terraform value.
String Interpolation
String interpolation lets you embed expressions inside strings using ${} syntax:
variable "project" {
default = "web-app"
}
variable "environment" {
default = "dev"
}
resource "aws_s3_bucket" "app" {
bucket = "${var.project}-${var.environment}-assets"
# Resolves to: "web-app-dev-assets"
}
The ${} wrapper evaluates the expression inside and inserts the result into the string.
When Interpolation is NOT Needed
If the entire value is a single reference (no surrounding text), skip the interpolation:
# ✅ Correct — direct reference, no interpolation needed
region = var.region
# ❌ Unnecessary — wrapping a single reference in interpolation
region = "${var.region}"
Both work, but the direct reference is cleaner. terraform fmt won’t remove the unnecessary interpolation, but linters will flag it.
Heredoc Strings
For multi-line strings, use heredoc syntax:
resource "aws_iam_policy" "example" {
name = "example-policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}
EOF
}
The <<EOF starts the heredoc, and EOF on its own line ends it. You can use any marker — EOF, POLICY, SCRIPT, etc.
Indented Heredoc
Use <<- to strip leading whitespace, keeping your code indented nicely:
resource "aws_instance" "web" {
user_data = <<-SCRIPT
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl start nginx
SCRIPT
}
The <<- variant removes the common leading whitespace from all lines, so the resulting string starts at column 0.
Primitive Types
HCL has three primitive types:
String
name = "my-bucket"
Strings are enclosed in double quotes. They support interpolation and escape sequences (\n, \t, \\, \").
Number
port = 8080
pi = 3.14159
timeout = 300
Numbers can be integers or floating-point. No quotes — quoted numbers are strings.
Boolean
enable_versioning = true
public_access = false
Only two values: true and false (lowercase, no quotes).
Type Conversion Functions
Sometimes you need to convert between types:
# Convert number to string
name_suffix = tostring(var.count_index)
# Convert string to number
port = tonumber(var.port_string)
# Convert string to boolean
enabled = tobool(var.enable_string)
Terraform also performs implicit type conversion in many cases. If a resource expects a string but you pass a number, Terraform converts it automatically.
The null Value
null represents the absence of a value. It’s useful for conditionally omitting optional arguments:
variable "custom_domain" {
type = string
default = null
}
resource "aws_s3_bucket_website_configuration" "site" {
count = var.custom_domain != null ? 1 : 0
bucket = aws_s3_bucket.site.id
index_document {
suffix = "index.html"
}
}
When var.custom_domain is null, the website configuration resource isn’t created at all.
Template Directives
For advanced string building, HCL supports template directives inside strings:
Conditional Directive
output "message" {
value = "Environment: %{if var.environment == "prod"}PRODUCTION%{else}${var.environment}%{endif}"
}
Loop Directive
output "server_list" {
value = <<-EOT
Servers:
%{for name in var.server_names}
- ${name}
%{endfor}
EOT
}
Template directives are powerful but can make strings hard to read. For complex templates, consider using the templatefile() function with a separate template file.
What’s Next?
You know how to work with strings and primitive types. Next, let’s explore collection types — lists, maps, and objects — that let you manage groups of values.