Hey everyone 👋
If you're learning Terraform or building projects on AWS, you’ll eventually bump into the need to control who can access your resources — especially EC2 instances. And that’s where Security Groups come in.
In this post, I’ll walk you through how I learned to create security group rules using Terraform — with real examples, gotchas, and how to think about it like you’re coding a smart lock system for your cloud environment.
Let me break it down the way I wish someone had for me early on 👇
🧸 Think of It Like Programming a Gatekeeper (for Your Server)
Imagine your EC2 instance is a secure building.
Each port is like a door:
- Port 22 = the door to your terminal (SSH)
- Port 80 = the door for website traffic (HTTP)
But you don’t want to leave all the doors open. That’s where the Security Group comes in — it’s like the gatekeeper that checks visitor IDs before letting them through any door.
Now imagine you could write code to tell the gatekeeper exactly who gets in and what door they can use.
That’s what you’re doing with Terraform. 🚪🔐
🛠️ What I Wanted to Build
I wanted a simple firewall (Security Group) called terraform-firewall
with two rules:
🚦 Rule Type | 🎯 Goal |
---|---|
Inbound | Allow HTTP (port 80) from the internet (0.0.0.0/0 ) |
Outbound | Allow ALL traffic out |
A pretty standard setup for a public-facing EC2 instance that needs internet access.
⚙️ How You Do It in Terraform
Terraform breaks it into three resources:
🧩 Terraform Resource | 💡 What It Does |
---|---|
aws_security_group |
Creates the actual security group (like an empty gatehouse) |
aws_vpc_security_group_ingress_rule |
Adds an inbound rule |
aws_vpc_security_group_egress_rule |
Adds an outbound rule |
Here’s what my final Terraform looked like:
provider "aws" {
region = "eu-west-2"
}
resource "aws_security_group" "terraform_firewall" {
name = "terraform-firewall"
description = "Managed from Terraform"
}
resource "aws_vpc_security_group_ingress_rule" "allow_http" {
security_group_id = aws_security_group.terraform_firewall.id
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr_ipv4 = "0.0.0.0/0"
}
resource "aws_vpc_security_group_egress_rule" "allow_all" {
security_group_id = aws_security_group.terraform_firewall.id
from_port = 0
to_port = 0
ip_protocol = "-1" # means all protocols
cidr_ipv4 = "0.0.0.0/0"
}
🧠 Wait… What’s That -1
Protocol?
Good catch. In the egress
rule, ip_protocol = "-1"
means:
👉 Allow all protocols — not just TCP or UDP.
It’s Terraform’s way of saying “let everything through.”
🛑 Common Mistakes I Made (So You Don’t Have To)
Inbound vs Outbound Confusion
I once added an ICMP (ping) rule in the outbound block and couldn’t figure out why I couldn’t ping my instance.
➡️ Solution: Ping is inbound traffic, so it needs to go in the ingress rule.Forgot to Attach the Rule to the Right SG
Terraform rules must reference the correctsecurity_group_id
— otherwise, they won’t apply where you expect.
➡️ Useaws_security_group.<name>.id
Port Range Logic
You can allow a range like 80 to 100 using:
from_port = 80
to_port = 100
🎓 Bonus: Swapping Rules Between Security Groups
You can also assign rules to existing groups by directly referencing their IDs:
security_group_id = "sg-0abcd1234..."
➡️ That means you can update default groups or share rules across projects — just be careful not to accidentally open things up too wide in production 😅
🚀 Final Thoughts
When I first saw Terraform security groups, I thought:
“Why do I need 3 resources just to open port 80?”
But now I get it. The split between group and rules gives you flexibility — especially when scaling to modular Terraform or managing shared resources.
So if you're building on AWS and want:
- ✅ Reusable, codified firewall rules
- ✅ Consistent environments
- ✅ Easy-to-read configurations
Then learning how to manage Security Groups with Terraform is a skill you’ll use again and again.
Want to swap Terraform tips or chat about cloud stuff?
Drop a comment or hit me up on LinkedIn — always happy to connect with fellow builders ☁️💬
Top comments (0)