Skip to main content

Terraform Configuration Syntax

Terraform configurations are written in HCL (HashiCorp Configuration Language), which is designed to be both human-readable and machine-parseable. This page covers the detailed syntax rules.

File Extensions

Terraform supports two file formats:
  • .tf - Native HCL syntax (recommended)
  • .tf.json - JSON syntax for machine generation
This documentation focuses on the native HCL syntax, which is the standard format for human-authored configurations.

Basic Syntax

Blocks

Blocks are the primary structural element. A block has a type, optional labels, and a body:
<BLOCK_TYPE> "<LABEL>" "<LABEL>" {
  # Block body
  <ARGUMENT> = <EXPRESSION>
  
  <NESTED_BLOCK> {
    # Nested block body
  }
}

Example

resource "aws_instance" "web" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  
  network_interface {
    device_index = 0
    description  = "Main network interface"
  }
}
In this example:
  • resource is the block type
  • "aws_instance" and "web" are labels
  • ami and instance_type are arguments
  • network_interface is a nested block

Arguments

Arguments assign values within blocks:
<IDENTIFIER> = <EXPRESSION>
The identifier must be a valid variable name (letters, digits, underscores, and hyphens).

Identifiers

Valid identifiers:
  • Start with a letter or underscore
  • Contain letters, digits, underscores, and hyphens
  • Are case-sensitive
variable "instance_type" { }    # Valid
variable "instance-type" { }    # Valid
variable "_private" { }         # Valid
variable "2nd_instance" { }     # Invalid - starts with digit
While hyphens are allowed in identifiers, underscores are preferred for consistency with most programming languages.

Expressions

Expressions produce values and can be:

Literal Values

# String
name = "example"

# Number
count = 3
pi = 3.14159

# Boolean
enabled = true
disabled = false

# Null
default_value = null

Collections

# List/tuple
az_list = ["us-west-2a", "us-west-2b", "us-west-2c"]

# Map/object
tags = {
  Name        = "example"
  Environment = "production"
}

References

Reference other values in the configuration:
# Resource attribute
ami = aws_ami.ubuntu.id

# Variable
instance_type = var.instance_type

# Local value
subnet_id = local.primary_subnet

# Data source
vpc_id = data.aws_vpc.main.id

Operators

Terraform supports standard operators:
# Arithmetic
result = 2 + 3      # 5
result = 10 - 4     # 6
result = 3 * 4      # 12
result = 10 / 2     # 5
result = 10 % 3     # 1

# Comparison
equal = 5 == 5           # true
not_equal = 5 != 3       # true
greater = 5 > 3          # true
greater_or_equal = 5 >= 5 # true

# Logical
and_op = true && false   # false
or_op = true || false    # true
not_op = !false          # true

Conditional Expressions

instance_type = var.environment == "production" ? "t2.large" : "t2.micro"

For Expressions

Transform collections:
# List transformation
uppercase_names = [for name in var.names : upper(name)]

# Map transformation
port_map = {for k, v in var.ports : k => v + 1000}

# Filtering
production_instances = [for i in var.instances : i if i.environment == "production"]

Splat Expressions

Shorthand for extracting attributes:
# Extract all IDs
instance_ids = aws_instance.web[*].id

# Works with resources created with count
private_ips = aws_instance.web[*].private_ip

String Templates

Interpolation

Embed expressions in strings:
greeting = "Hello, ${var.name}!"
path = "/var/lib/${var.service}/data"

Directives

Control whitespace and flow:
# For loop
user_names = <<EOT
%{ for name in var.users ~}
- ${name}
%{ endfor ~}
EOT

# Conditional
config = <<EOT
%{ if var.debug_mode ~}
debug = true
%{ else ~}
debug = false
%{ endif ~}
EOT

Heredoc Syntax

Multi-line strings:
user_data = <<-EOT
  #!/bin/bash
  echo "Hello World"
  apt-get update
  apt-get install -y nginx
EOT

Comments

Three comment styles:
# Single-line comment (preferred)

// Alternative single-line comment

/*
  Multi-line comment
  Useful for larger blocks
*/

Special Characters

Escaping

Escape special characters in strings:
message = "Quote: \"Hello\" and newline: \n"
path = "C:\\Users\\Admin"
template = "$${not_interpolated}"

Unicode

Unicode characters are supported:
variable "π" {
  default = 3.14159265359
}

output "emoji" {
  value = "🚀"
}

Style Conventions

Use 2 spaces for indentation (not tabs):
resource "aws_instance" "example" {
  ami = "ami-12345678"
  
  network_interface {
    device_index = 0
  }
}
Align values for better readability:
resource "aws_instance" "web" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  subnet_id     = var.subnet_id
}
Use blank lines to separate logical sections:
variable "region" {
  type = string
}

variable "instance_type" {
  type = string
}

Formatting

Terraform includes a formatter:
# Format current directory
terraform fmt

# Format specific file
terraform fmt main.tf

# Check if files are formatted
terraform fmt -check

JSON Syntax

For machine-generated configurations:
{
  "resource": {
    "aws_instance": {
      "web": {
        "ami": "ami-12345678",
        "instance_type": "t2.micro"
      }
    }
  }
}
The JSON syntax is functionally equivalent to HCL but is less human-friendly. Use it only when generating configurations programmatically.

Validation

The parser validates syntax according to the HCL specification. Common errors include:
  • Missing closing braces
  • Invalid identifier names
  • Incorrect expression syntax
  • Mismatched quotes
Use terraform validate to check for syntax errors:
terraform validate

Next Steps

Expressions

Learn about expression syntax in detail

Functions

Explore built-in functions

Build docs developers (and LLMs) love