Day 63 – Variables, Outputs, Data Sources and Expressions in Terraform
On Day 63 of my 90 Days of DevOps journey, I learned how to make Terraform configurations dynamic and reusable using variables, outputs, data sources, locals, and expressions.
Until now, most of my Terraform configurations contained hardcoded values such as:
AWS Region
VPC CIDR
Subnet CIDR
Instance Type
AMI IDs
Tags
The problem with hardcoded values is that the configuration becomes difficult to reuse.
Today I learned how Terraform solves this problem.
Variables
The first thing I did was move all hardcoded values into variables.
I created a:
variables.tf
file and defined variables for:
region
vpc_cidr
subnet_cidr
instance_type
project_name
environment
allowed_ports
extra_tags
Then I replaced hardcoded values with:
var.<variable_name>
references throughout the configuration.
This made the infrastructure much more flexible.
I also learned about Terraform variable types:
string
number
bool
list
map
Variable Files
Next, I learned how to use variable files.
I created:
terraform.tfvars
for development configuration.
I also created:
prod.tfvars
for production configuration.
This allowed me to use different values without changing the Terraform code itself.
For example:
Development environment used:
t2.micro
Production environment used:
t3.small
I found this very useful because the same code can now be reused across multiple environments.
Outputs
Today I also learned about Terraform outputs.
I created outputs for:
VPC ID
Subnet ID
EC2 Instance ID
Public IP
Public DNS
Security Group ID
After running:
terraform apply
Terraform automatically displayed these values.
I also tested:
terraform output
and:
terraform output instance_public_ip
to retrieve specific information from the deployed infrastructure.
This felt very useful for automation and scripting.
Data Sources
One of the biggest improvements today was removing hardcoded AMI IDs.
Instead of manually providing an AMI ID, I used:
data "aws_ami"
to automatically fetch the latest Amazon Linux 2 image.
I also used:
data "aws_availability_zones"
to dynamically select an Availability Zone.
This made the configuration much more portable.
Now the same Terraform code can work in different regions without manually updating AMI IDs.
I learned an important difference:
Resources create infrastructure.
Data Sources fetch existing information.
Locals
Next, I learned about Terraform locals.
I created a local block to generate consistent naming and tagging.
For example:
project-environment
was automatically combined into a common name prefix.
I also used:
merge()
to apply common tags to every resource.
This helped make the infrastructure more organized and easier to manage.
Built-in Functions
Today I also experimented with:
terraform console
and explored several built-in functions.
Some functions I practiced were:
upper()
join()
format()
length()
lookup()
cidrsubnet()
These functions make Terraform expressions much more powerful.
Conditional Expressions
Finally, I learned about conditional expressions.
I configured:
instance_type = var.environment == "prod" ? "t3.small" : "t2.micro"
This means:
Production gets a larger instance.
Development gets a smaller instance.
This was one of the most practical Terraform features I have learned so far.
What I Learned Today
Today I learned:
Terraform Variables
Variable Types
terraform.tfvars
prod.tfvars
Outputs
Data Sources
Locals
Built-in Functions
Conditional Expressions
Variable Precedence
Dynamic Infrastructure Configuration
Final Thoughts
Today helped me understand how to write Terraform configurations that are reusable and environment-aware.
Instead of creating infrastructure that works only once, I can now create configurations that work across development, staging, and production environments.
This felt like a major step toward writing production-ready Infrastructure as Code
See you on Day 64
