By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
(file, lookup, element, merge, jsonencode/decode)
You’re a cloud engineer maintaining a Terraform codebase for a multi-environment (dev/stage/prod) AWS deployment. Your team just inherited a legacy module where hardcoded values are scattered across files, and secrets are accidentally committed to Git. Worse, the same VPC CIDR block is duplicated in three places—meaning a typo in one file breaks everything.
Built-in functions are your escape hatch. They let you: - Dynamically read files (file) instead of hardcoding secrets or configs.- Safely fetch values from maps (lookup) without crashing if a key is missing.- Slice and dice lists (element) to pick the right subnet or AMI.- Merge configurations (merge) to combine dev/stage/prod settings without copy-pasting.- Encode/decode JSON (jsonencode/jsondecode) to pass structured data to APIs (e.g., IAM policies, Lambda environment variables).
file
lookup
element
merge
jsonencode
jsondecode
What breaks if you ignore this?- Security risks: Hardcoded secrets in .tf files get leaked.- Brittle code: A single typo in a duplicated value (e.g., us-east-1a vs us-east-1b) breaks deployments.- Wasted time: Manually updating the same value in 10 places.
.tf
us-east-1a
us-east-1b
Superpower: You’ll write DRY (Don’t Repeat Yourself), dynamic, and self-healing Terraform code that adapts to environments without manual edits.
file(path)
path
user_data
sops
lookup(map, key, [default])
key
map
default
null
lookup(var.instance_sizes, var.environment, "t3.micro")
element(list, index)
index
list
element(["a", "b"], 2)
"a"
merge(map1, map2, ...)
merge(local.common_tags, { Environment = "prod" })
jsonencode(value)
jsondecode(string)
>= 1.0.0
You’ll: 1. Load user_data from an external file.2. Use lookup to pick an instance type based on environment.3. Use element to select a subnet from a list.4. Merge tags with environment-specific overrides.5. Pass a JSON IAM policy to the instance.
mkdir tf-functions-demo && cd tf-functions-demo touch main.tf variables.tf outputs.tf user_data.sh
user_data.sh
#!/bin/bash echo "Hello from $(hostname)" > /tmp/hello.txt
variables.tf
variable "environment" { description = "Deployment environment (dev/stage/prod)" type = string default = "dev" } variable "instance_sizes" { description = "Map of instance sizes per environment" type = map(string) default = { dev = "t3.micro" stage = "t3.small" prod = "m5.large" } } variable "subnets" { description = "List of subnet IDs" type = list(string) default = ["subnet-12345678", "subnet-87654321"] # Replace with real subnet IDs } variable "common_tags" { description = "Tags applied to all resources" type = map(string) default = { Terraform = "true" Owner = "team-infra" } }
main.tf
provider "aws" { region = "us-east-1" } # Load user_data from file data "template_file" "user_data" { template = file("${path.module}/user_data.sh") } # Lookup instance size based on environment locals { instance_size = lookup(var.instance_sizes, var.environment, "t3.micro") } # Pick a subnet using element (round-robin) locals { subnet_id = element(var.subnets, 0) # Change index to distribute across subnets } # Merge common tags with environment-specific tags locals { tags = merge(var.common_tags, { Environment = var.environment }) } # Create IAM policy as JSON locals { iam_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = ["s3:ListBucket"] Resource = ["arn:aws:s3:::example-bucket"] } ] }) } # Deploy EC2 instance resource "aws_instance" "example" { ami = "ami-0c55b159cbfafe1f0" # Amazon Linux 2 (us-east-1) instance_type = local.instance_size subnet_id = local.subnet_id user_data = data.template_file.user_data.rendered tags = local.tags # Attach IAM policy (simplified for demo) iam_instance_profile = aws_iam_instance_profile.example.name } # IAM role and profile (simplified) resource "aws_iam_role" "example" { name = "ec2-role-${var.environment}" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } Action = "sts:AssumeRole" } ] }) } resource "aws_iam_role_policy" "example" { name = "ec2-policy-${var.environment}" role = aws_iam_role.example.id policy = local.iam_policy } resource "aws_iam_instance_profile" "example" { name = "ec2-profile-${var.environment}" role = aws_iam_role.example.name }
terraform init terraform plan -var="environment=stage" # Test with stage environment terraform apply -var="environment=stage" -auto-approve
Verify success:1. Check the EC2 instance in the AWS Console: - Tags should include Environment = "stage". - Instance type should be t3.small (for stage).2. SSH into the instance and check /tmp/hello.txt: bash ssh -i your-key.pem ec2-user@<public-ip> cat /tmp/hello.txt
Environment = "stage"
t3.small
stage
/tmp/hello.txt
bash ssh -i your-key.pem ec2-user@<public-ip> cat /tmp/hello.txt
can(lookup(...))
t3.micro
m5.large
aws_instance
spot_price
name = "ec2-${md5(jsonencode(var.tags))}"
Environment
Owner
echo "User data executed at $(date)" >> /var/log/user-data.log
lookup(var.map, "key", "default_value")
length(var.list) > 0 ? element(var.list, 0) : null
jq
echo '{"key": "value"}' | jq .
try
try(var.map.key, "default")
Answer: Use lookup(var.map, "key", "default").
lookup(var.map, "key", "default")
element vs list[index]:
list[index]
var.subnets[2]
Answer: element(var.subnets, 2) wraps around.
element(var.subnets, 2)
jsonencode for IAM policies:
Answer: Use jsonencode({ Version = "2012-10-17", Statement = [...] }).
jsonencode({ Version = "2012-10-17", Statement = [...] })
merge for tags:
merge(local.common_tags, { Environment = var.env })
Challenge: Write a Terraform config that: 1. Reads a JSON file (config.json) with environment-specific settings (e.g., {"dev": {"instance_type": "t3.micro"}}).2. Uses jsondecode to parse the file.3. Uses lookup to fetch the instance type for the current environment (var.environment).4. Deploys an EC2 instance with the correct type.
config.json
{"dev": {"instance_type": "t3.micro"}}
var.environment
Solution:
locals { config = jsondecode(file("${path.module}/config.json")) instance_type = lookup(local.config[var.environment], "instance_type", "t3.micro") } resource "aws_instance" "challenge" { ami = "ami-0c55b159cbfafe1f0" instance_type = local.instance_type }
Why it works: - jsondecode parses the JSON file into a Terraform map.- lookup safely fetches the instance type, defaulting to t3.micro if missing.
file("user_data.sh")
lookup(map, key)
lookup(var.sizes, "prod", "m5.large")
element(list, i)
element(var.subnets, 0)
merge(map1, map2)
merge(local.tags, { Env = "prod" })
jsonencode(val)
jsonencode({ Key = "value" })
jsondecode(str)
jsondecode(file("config.json"))
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.