HCL Syntax Fundamentals
HCL (HashiCorp Configuration Language) is a structured configuration language designed to be both human-readable and machine-parseable. Every .tf file you write — providers, resources, variables, modules — is HCL. It is built around two core constructs: blocks, which group related configuration and support nesting, and arguments, which assign a value to an identifier within a block. Terraform also accepts an equivalent JSON syntax for HCL, useful for generated configuration, but almost all hand-written Terraform uses native HCL because it is far more readable.
Cricket analogy: HCL is like the standardized scorecard format every team uses — readable by commentators and computable by scoring software alike — with resource, variable, and provider files all following the same structured layout.
Blocks and Arguments
A block has a type (like resource, variable, or provider), zero or more labels (quoted strings that further identify it, such as the resource type and name), and a body enclosed in braces containing arguments and nested blocks. Arguments follow an identifier = expression pattern. Some block types require a specific number of labels — a resource block always takes exactly two: the resource type and a local name you choose — while others like terraform or locals take none.
Cricket analogy: A block is like a match scorecard entry requiring exactly two labels — team name and player name — just as a resource block always takes exactly two labels: the resource type and local name.
# resource block: type has two labels, then a body of arguments
resource "aws_s3_bucket" "reports" {
bucket = "acme-quarterly-reports"
tags = {
Environment = "production"
Team = "finance"
}
}
# variable block: one label, and a nested type constraint
variable "region" {
type = string
default = "us-east-1"
}
# a block with no labels
locals {
full_name = "${var.region}-reports"
}Identifiers, Comments, and Strings
Identifiers (block labels, argument names, variable names) may contain letters, digits, underscores, and hyphens, but must not start with a digit. Comments can use # or // for single-line comments, and /* */ for multi-line. String values are usually double-quoted and support interpolation using ${...} syntax, which lets you embed the result of an expression directly inside a string, e.g. "${var.environment}-vpc". Heredoc syntax (<<EOT ... EOT) is available for multi-line strings such as embedded shell scripts or JSON policy documents.
Cricket analogy: Like a scorer using string interpolation to write '${batsman.name}-century' on the board, and heredoc-style extended notes in the margin for a detailed over-by-over commentary that spans multiple lines.
HCL's design goal was to sit between YAML (too loose, easy to misindent) and a full programming language (too much for declarative infra). That's why HCL supports expressions and functions but deliberately omits general-purpose control flow like arbitrary loops — count and for_each cover the vast majority of repetition needs declaratively.
HCL natively supports lists (ordered sequences in square brackets), maps/objects (key-value pairs in curly braces), and sets. These map directly onto Terraform's type system (list(string), map(number), object({...})) used when declaring variable types. Nested blocks, such as an ingress block inside an aws_security_group resource, can also be repeated multiple times within the same parent block to represent a list of similar sub-configurations.
Cricket analogy: Like a squad list (ordered list of players in batting order), a fixture map (key-value pairs of team-to-venue), and repeated 'over' blocks inside an innings summary representing each over's outcome, mirroring nested ingress blocks.
A common beginner mistake is quoting numbers or booleans unnecessarily, e.g. instance_count = "3" instead of instance_count = 3. HCL is type-aware, and while Terraform often coerces types leniently, relying on that coercion can produce confusing type-constraint errors in variable validation or module inputs.
- HCL configurations are built from blocks (type, optional labels, body) and arguments (identifier = expression).
- resource blocks always take exactly two labels: resource type and local name.
- String interpolation uses ${...}; heredoc syntax (<<EOT) handles multi-line strings.
- HCL supports lists, maps/objects, and repeatable nested blocks for complex, structured configuration.
- Comments use #, //, or /* */.
- HCL intentionally avoids general-purpose control flow, favoring declarative constructs like count and for_each.
Practice what you learned
1. In HCL, what are the two labels required on a resource block?
2. How does HCL perform string interpolation?
3. Which comment styles are valid in HCL?
4. Why does HCL deliberately omit general-purpose loops and conditionals like a full programming language would have?
5. What is a nested block used for, such as an ingress block inside aws_security_group?
Was this page helpful?
You May Also Like
What Is Terraform?
Terraform is HashiCorp's open-source Infrastructure as Code tool that uses a declarative language, HCL, to provision and manage resources across hundreds of cloud and SaaS providers.
Variables and Variable Types
Input variables let Terraform configurations be parameterized and reused across environments. This topic covers declaring variables, type constraints, defaults, validation, and how to supply values.
Expressions and Functions
HCL expressions let you reference values, perform operations, and transform data using Terraform's extensive built-in function library. This topic covers references, operators, conditionals, and common functions.
Resources and Resource Blocks
The resource block is Terraform's fundamental unit of infrastructure declaration, describing a single managed object like a virtual machine, network, or storage bucket.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics