100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
HCL

HCL Syntax Fundamentals

HashiCorp Configuration Language (HCL) is the declarative syntax underlying every Terraform file. Mastering its blocks, arguments, and expressions is the foundation for writing correct configurations.

HCL Language BasicsBeginner8 min readJul 9, 2026
Analogies

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.

hcl
# 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

Was this page helpful?

Topics covered

#HCL#TerraformInfrastructureAsCodeStudyNotes#DevOps#HCLSyntaxFundamentals#Syntax#Fundamentals#Blocks#Arguments#StudyNotes#SkillVeris