Terraform Associate
Deep Dive
Variables make modules parameterizable and reusable. Outputs expose values to the outside. Locals are intermediate computed values. Mastering their precedence and behavior is key for the exam.
Contents
# Full declaration of a variable
variable "instance_type" {
type = string # data type
default = "t3.micro" # default value (makes the variable optional)
description = "EC2 instance type" # documentation
sensitive = false # if true, hides the value in logs
validation {
condition = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
error_message = "instance_type must be t3.micro, t3.small or t3.medium."
}
}
# Variable with no default (REQUIRED — Terraform will prompt for the value)
variable "db_password" {
type = string
sensitive = true
description = "Database password"
}
# Variable with a complex type
variable "allowed_cidrs" {
type = list(string)
default = ["10.0.0.0/8", "172.16.0.0/12"]
}
variable "tags" {
type = map(string)
default = {
Environment = "dev"
Team = "platform"
}
}💡 Validation with condition
The validation block inside a variable lets you validate the value before Terraform runs the plan. The condition must be a boolean expression that uses var.name. If it evaluates to false, Terraform shows the error_message.
Terraform evaluates variables in order of precedence. Methods with a higher number in the list override the earlier ones.
Default value in the variable {} block
default = "us-east-1"
terraform.tfvars file
aws_region = "us-west-2"
*.auto.tfvars files (alphabetical order)
prod.auto.tfvars — loaded automatically
TF_VAR_name environment variables
export TF_VAR_aws_region="eu-west-1"
-var-file=file.tfvars flag
terraform plan -var-file=prod.tfvars
-var="key=value" flag
terraform plan -var="aws_region=ap-southeast-1"
# terraform.tfvars — loaded automatically if it exists in the directory
aws_region = "us-east-1"
instance_type = "t3.small"
db_password = "super-secret" # ⚠️ do not commit if it contains secrets
# prod.auto.tfvars — loaded automatically (thanks to the .auto.)
environment = "prod"
instance_type = "t3.large" # overrides terraform.tfvars
# Reference in code: var.variable_name
resource "aws_instance" "web" {
instance_type = var.instance_type
ami = var.ami_id
}🎯 For the exam: the precedence question
The exam often asks: "If variable X is defined as a default, in terraform.tfvars and with -var, which value is used?". The answer is -var (the highest precedence). Memorize the order: default → tfvars → auto.tfvars → TF_VAR → -var-file → -var.
The sensitive = true attribute on a variable tells Terraform to hide its value in the CLI output (plan, apply). However, it has important limitations.
✅ What sensitive = true DOES do
(sensitive value)⚠️ What sensitive = true does NOT do
variable "db_password" {
type = string
sensitive = true
}
# In the plan you will see:
# + db_password = (sensitive value) ← value hidden in the CLI
# BUT in terraform.tfstate you will see:
# "db_password": "my-secret-password" ← in plain text ⚠️
# That's why it's recommended to:
# 1. Use a remote backend with encryption (S3 + KMS or HCP Terraform)
# 2. Retrieve secrets from Vault/Secrets Manager at runtimeEphemeral values are values that Terraform uses in memory during plan/apply but does not persist in the state or in logs. Designed to handle real secrets (passwords, tokens) securely.
# Ephemeral variable — not written to state
variable "db_password" {
type = string
ephemeral = true # new in Terraform 1.10+
}
# Ephemeral resource — data that does not persist in state
ephemeral "aws_secretsmanager_secret_version" "db_creds" {
secret_id = "prod/db/password"
}
# Write-only argument — accepts an ephemeral value on a resource
resource "aws_db_instance" "main" {
identifier = "prod-db"
engine = "postgres"
password = ephemeral.aws_secretsmanager_secret_version.db_creds.secret_string
# password has write_only = true: sent to the API but not saved in state
}variable ephemeral
An input value that is not saved in state. Ideal for passwords and tokens only needed during the apply.
ephemeral resource
An ephemeral {} block for external data (Vault, Secrets Manager) whose value must not persist in state.
write-only argument
A resource attribute that Terraform sends to the provider API but does not store in state or show in the plan.
🎯 For the exam (004)
sensitive = true hides the value in CLI output but it is still saved in the stateephemeral = true (Terraform 1.10+) makes the value not persist in the state at all — real security for secretsephemeral resources can be used with secret providers (Vault, AWS Secrets Manager)Outputs expose module values to the outside world: to the user (shown in the apply), to the parent module (accessible as module.name.output), and as state data (accessible via terraform output).
# Declaring outputs
output "vpc_id" {
value = aws_vpc.main.id
description = "ID of the main VPC"
}
output "db_endpoint" {
value = aws_db_instance.main.endpoint
description = "Database connection endpoint"
sensitive = true # hidden in CLI, but available to parent modules
}
output "subnet_ids" {
value = aws_subnet.public[*].id # list of IDs with a splat expression
description = "IDs of the public subnets"
}
# Access from a parent module:
# module.networking.vpc_id
# module.networking.subnet_ids
# Access from the CLI:
# terraform output vpc_id
# terraform output -json| Argument | Required | Description |
|---|---|---|
| value | ✅ Yes | The expression whose result is exposed as the output |
| description | ❌ No | Documentation for the output (a good practice, always recommended) |
| sensitive | ❌ No | If true, hides the value in the CLI. Inherited if value references a sensitive variable. |
| depends_on | ❌ No | Declares explicit dependencies when the value does not have implicit ones |
Locals are values computed inside the module that you can reuse in multiple places. Unlike variables, they cannot be overridden from outside the module.
# locals block (plural: the block is called "locals")
locals {
# Combine variables to build a standard name
name_prefix = "${var.project}-${var.environment}"
# Common tags applied to all resources
common_tags = {
Project = var.project
Environment = var.environment
ManagedBy = "terraform"
Owner = var.team
}
# Complex calculation
is_production = var.environment == "prod"
instance_type = local.is_production ? "t3.large" : "t3.micro"
}
# Access: local.name (singular: "local")
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-vpc"
})
}| Feature | variable {} | locals {} |
|---|---|---|
| Configurable from outside? | ✅ Yes — that is its purpose | ❌ No — internal to the module |
| Can it contain expressions? | Only in default and validation | ✅ Yes — any HCL expression |
| Access prefix | var.name | local.name (singular) |
| Declaration block | variable "name" {} | locals {} (a single block with several keys) |
| When to use it | A parameter the user defines | A derived/computed value internal to the module |
Did you get this topic?
Test what you just learned
An engineer defines a db_password variable with sensitive = true and uses it as an argument on an aws_db_instance resource. They then run terraform apply successfully. Where will the password appear in plain text?
Put what you learned to the test with original Terraform Associate exam questions.
Start Terraform Associate mock examUnlimited mock exams with the Pro plan
Log in to track your progress.