Terraform Best Practice | Split Terraform Code into Multiple Files | EC2 with Variables & Outputs
👉 Before we start, I assume:
- You already have the provider file created.
- Terraform is initialized.
- AWS CLI is configured with an IAM user.
Why Split Terraform Files?
Terraform doesn’t care how many .tf files you have. It looks at the whole directory as a single configuration.
So instead of dumping everything in main.tf (or ec2.tf), we create multiple files like:
ec2.tf→ EC2-related resourcesvariables.tf→ Input variablesoutput.tf→ Outputs like IPs or DNS namesprovider.tf→ Provider details
This way, your code is more structured and modular.
Old Code (Single File)
Earlier, we defined everything inside one file. Example snippet:
# Create SSH Key
resource "aws_key_pair" "my_ssh_key" {
key_name = "terra-key-auto"
public_key = file("/home/ubuntu/terra-key-auto.pub")
}# Create Default VPC
resource "aws_default_vpc" "default" {}# Security Group
resource "aws_security_group" "my_sg" {
name = "TWS-SG"
description = "this is a sooper se ooper upper security group"
vpc_id = aws_default_vpc.default.id ingress = {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "this is for ssh access"
} ingress = {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "this is for http access"
} egress = {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "this is for outside world requests"
}
}# EC2 Instance
resource "aws_instance" "my_instance" {
ami = "ami-xxxxxxx"
instance_type = "t2.micro"
key_name = aws_key_pair.my_ssh_key.key_name
security_groups = [aws_security_group.my_sg.name] tags = {
Name = "My-Auto-server"
}
}
Improved Code (Multiple Files with Best Practices)
variables.tf
Here we define our variables instead of hardcoding values.
✅ Change #1 → Replaced hardcoded AMI ID & Instance Type with variables.
variable "ec2_ami_id" {
default = "ami-021eda" # Replace with correct AMI for your region
description = "This is AMI ID for EC2 instance"
type = string
}variable "ec2_instance_type" {
default = "t2.micro"
description = "This is the instance type for EC2 instance"
type = string
}output.tf
Here we define outputs like the public IP of our instance.
✅ Change #2 → Added an output file to display EC2 public IP after deployment.
output "my_ec2_ip" {
value = aws_instance.my_instance.public_ip
}ec2.tf
Now we rewrite our EC2 code, but instead of hardcoding values, we use variables.
✅ Change #3 → Used var.ec2_ami_id and var.ec2_instance_type.
✅ Change #4 → Code split across separate files (variables.tf, output.tf, ec2.tf).
# Create SSH Key
resource "aws_key_pair" "my_ssh_key" {
key_name = "terra-key-auto"
public_key = file("/home/ubuntu/terra-key-auto.pub")
}# Create Default VPC
resource "aws_default_vpc" "default" {}# Security Group
resource "aws_security_group" "my_sg" {
name = "TWS-SG"
description = "this is a sooper se ooper upper security group"
vpc_id = aws_default_vpc.default.id ingress = {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "this is for ssh access"
} ingress = {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "this is for http access"
} egress = {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "this is for outside world requests"
}
}# EC2 Instance
resource "aws_instance" "my_instance" {
ami = var.ec2_ami_id # ✅ Using variable instead of hardcoded value
instance_type = var.ec2_instance_type # ✅ Using variable instead of hardcoded value
key_name = aws_key_pair.my_ssh_key.key_name
security_groups = [aws_security_group.my_sg.name] tags = {
Name = "My-Auto-server"
}
}# Manage EC2 Instance State
resource "aws_ec2_instance_state" "my_state" {
instance_id = aws_instance.my_instance.id
state = "stopped"
}
Terraform Execution Flow (Step by Step)
Once you have the files ready (provider.tf, variables.tf, ec2.tf, output.tf), run the following commands:
# 1. Initialize Terraform (downloads provider plugins)
terraform init # 2. Check what Terraform will create (dry run)
terraform plan # 3. Apply changes and create resources
terraform apply -auto-approve # 4. Check the outputs (like EC2 public IP)
terraform output # 5. Destroy resources when not needed
terraform destroy -auto-approve
Final Thoughts
We made our Terraform code:
- Modular (separate files).
- Reusable (variables instead of hardcoded values).
- Clearer (outputs for visibility).
This approach is not just a best practice but also makes your Terraform code scalable and maintainable.
In the next blog, we’ll move one step ahead — maybe creating multiple environments (dev, staging, prod) using the same Terraform code. 🚀
🌐 Online References
- LinkedIn: https://www.linkedin.com/in/raees-yaqoob-qazi-ryqs/
- Medium Blog: https://medium.com/@raeesyaqubqazi
- Blogspot: https://brillertechnologies.blogspot.com/
- Facebook Page: https://web.facebook.com/profile.php?id=61553548371216
- YouTube Channel: https://www.youtube.com/@RaeesQ.
- TikTok: https://www.tiktok.com/@mrryqs?_t=ZS-8y7t0fQfJKu&_r=1
✍️ By Raees Qazi
DevOps Engineer | Learner | Mentor | Creator
Comments
Post a Comment