HCL: Resources, Variables & Outputs
HCL is the declarative language Terraform is written in. Master resource blocks, variables, locals, data sources and outputs to parameterize and reuse configurations.
What is this?
HCL (HashiCorp Configuration Language) is Terraform's declarative syntax. Everything is built from blocks and arguments. Resource blocks create and manage objects, data sources read existing objects, variables parameterize inputs, locals compute reused expressions, and outputs publish results after apply.
Why do you need it?
- Variables turn one configuration into many environments without copy-paste.
- Outputs expose IPs, IDs and ARNs for use by other tools and modules.
- Data sources read existing infrastructure without managing or destroying it.
- References and functions wire resources together and keep configuration DRY.
- The sensitive flag keeps passwords and keys out of apply output and logs.
Mental model
A recipe with blank ingredient amounts
A Terraform configuration with variables
The same recipe card can make a different batch size by filling the blanks differently. The same .tf files produce different infrastructure per environment by supplying different variable values.
How it works
- resource blocks declare objects you own, for example resource "aws_instance" "web" { ... }.
- Arguments inside the block set desired attributes; references like aws_vpc.main.id wire resources together.
- variable blocks define typed inputs with defaults, descriptions and validation.
- locals compute values once and reuse them throughout the configuration.
- data blocks query existing objects, for example data "aws_ami" "ubuntu" { ... }.
- output blocks publish values after apply, optionally marked sensitive.
Data flow through HCL block types.
Key commands
terraform consoleOpen an interactive shell to evaluate expressions and references against your config.
terraform fmtReformat HCL to canonical style.
terraform validateCheck configuration validity without contacting providers.
terraform plan -var 'username=bob'Override a variable value for a single run.
terraform showDisplay the current state or a saved plan in human-readable form.
terraform providersList the provider plugins required by the configuration.
Examples
Variables, locals and outputs working together
variable "username" { type = string default = "alice"} variable "greeting_file" { type = string default = "./greeting.txt" description = "Path where the greeting file is written"} locals { message = "Hello, ${var.username}!"} resource "local_file" "greeting" { content = local.message filename = var.greeting_file} output "greeting_content" { value = local.message description = "The message that was written to the file"}The variables parameterize the config, the local computes the message once, the resource uses both, and the output publishes the result. Override values with a terraform.tfvars file or -var at the CLI.
In production
- Environment-specific values flow in via terraform.tfvars, .tfvars.json or CI/CD variables.
- Sensitive variables (passwords, API keys) are marked sensitive and never printed.
- Outputs feed downstream tools: scripts, CI steps and terraform_remote_state consumers.
- Data sources fetch the current AMI, subnet or DNS zone so nothing is hardcoded.
Best practices
- Declare types, defaults and descriptions for every variable.
- Use locals for any expression used more than once.
- Mark secrets sensitive = true and inject them from environment or secret stores.
- Reference other resources by attribute, never by hardcoded ID.
- Group related variables into variables.tf, outputs into outputs.tf, resources into main.tf.
Common mistakes
- Hardcoding IDs and values instead of referencing resources or data sources.
- Using dashes or leading digits in resource names, which are invalid identifiers.
- Missing sensitive = true on secrets, leaking them into state and logs.
- Storing real variable values inside the config instead of tfvars files.
Common issues
Error: Invalid resource nameThe resource name starts with a number or contains a dash.
Use identifiers starting with a letter, containing only letters, digits and underscores.
Error: Unsupported argumentThe attribute name does not exist for this resource type.
Check the provider documentation for the exact argument name, and confirm with terraform validate.
Error: Reference to undeclared resourceAn expression references a resource that does not exist in the configuration.
Define the resource, or read it with a data source, before referencing its attributes.
Debugging tips
- terraform console lets you test an expression like aws_vpc.main.id before applying.
- terraform validate catches unknown attributes and invalid references early.
- terraform fmt --check in CI fails the build on unformatted HCL.
- When a value looks wrong, print it with a temporary output block or terraform show.
Interview checkpoint
Hands-on lab
- Declare typed variables with defaults
- Pass values via tfvars files and -var flags
- Use locals and output blocks
- Mark a sensitive value and observe redaction
Parameterize a Configuration with Variables
30 min ยท beginner
Quick quiz
Which block type reads an existing object without managing it?
What is the purpose of an output block?
Marking a variable sensitive = true encrypts its value inside the state file.
Cheat sheet
resource "type" "name" {}Create and manage an objectdata "type" "name" {}Read an existing objectvariable "name" {}Declare a typed inputoutput "name" {}Publish a result after applylocals { name = ... }Compute a reusable valuevar.fooReference a variableresource.x.attrReference an attribute-var 'k=v'CLI overrideterraform.tfvarsAuto-loaded default file*.auto.tfvarsAuto-loaded named fileenv var TF_VAR_nameEnvironment variable input