What is a Security Group?
A Security Group in AWS acts as a virtual firewall for your instances to control inbound and outbound traffic. It operates at the instance level, not the subnet level, and therefore provides a secondary layer of security in addition to Network Access Control Lists (NACLs).
Key Features
- Stateful Filtering: Security Groups are stateful, meaning if you send a request from your instance, the response is automatically allowed, regardless of outbound rules.
- Default Security Groups: Every VPC comes with a default security group that you can modify.
- Rule Flexibility: You can specify rules based on IP protocol, port number, and source/destination IP address or CIDR block.
- No Deny Rules: Security Groups allow only ALLOW rules. There are no DENY rules. If no rules are matched, the default is to deny.
Components of Security Group Rules
- Type: The protocol (TCP, UDP, ICMP) for the rule.
- Port Range: The port number or range of port numbers for the rule.
- Source/Destination: The allowed IP addresses or CIDR ranges for inbound/outbound traffic.
How to Configure
- Create Security Group: Go to the EC2 or VPC dashboard in the AWS Management Console and create a new Security Group.
- Specify Rules: Add inbound and outbound rules to allow the necessary traffic.
- Associate with Instances: Attach the Security Group to one or more EC2 instances.
Common Use Cases
- Web Servers: Allow inbound traffic on HTTP and HTTPS ports.
- Databases: Allow inbound traffic only from specific IP addresses or other Security Groups.
- SSH Access: Allow inbound traffic on port 22 (SSH) but restrict it to specific IPs to maintain secure access.
Best Practices
- Least Privilege: Only open up the ports that are absolutely necessary.
- IP Restriction: Restrict inbound rules to known IP addresses where possible.
- Security Layering: Use Security Groups in conjunction with NACLs for layered security.
- Monitoring: Regularly review and audit your Security Groups and their associated rules.
Limitations
- Rule Limits: You can associate up to 5 security groups per network interface.
- Entries Per Group: Up to 60 inbound and 60 outbound rules per security group are allowed.
For the purpose of this tutorial how to deploy wordpress in AWS using terraform. We are going to create 5 security group:
- Security group for Bastion instances – dev-ssh_security_group: It will allow traffic only from your IP Address
- Security group for ALB – dev-alb_security_group: This will all traffic from the internet on port 443 and 80
- Security group for web instances (Web server) – dev-webserver_security_group: this will only allow traffic from ALB security group on port 443 and 80 and traffic from SSH security group on port 22.
- Security group for Amazon RDS – dev_database_security_group: accept traffic from web server security group on port 3306.
- Security group for EFS mount targets – dev-efs_security_group: it will accept traffic from Webserver security group and EFS security group on port 2049 and from SSH security group on port 22

Lets update our main.tf file to create our security groups:
# Security groups
# Security Group 1: ALB Security Group
resource "aws_security_group" "dev-alb_security_group" {
name = "ALBSecurityGroup"
description = "Security group for Application Load Balancer (ALB)"
vpc_id = aws_vpc.aws-vpc.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "dev-alb_security_group"
env = "dev"
}
}
# Security Group 2: SSH Security Group
resource "aws_security_group" "dev-ssh_security_group" {
name = "SSHSecurityGroup"
description = "Security group for SSH access"
vpc_id = aws_vpc.aws-vpc.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["86.42.105.138/32"] # Replace with your IP address
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "dev-ssh_security_group"
env = "dev"
}
}
# Security Group 3: Webserver Security Group
resource "aws_security_group" "dev-webserver_security_group" {
name = "WebserverSecurityGroup"
description = "Security group for webservers"
vpc_id = aws_vpc.aws-vpc.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [aws_security_group.dev-alb_security_group.id]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [aws_security_group.dev-alb_security_group.id]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
security_groups = [aws_security_group.dev-ssh_security_group.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "dev_webserver_security_group"
env = "dev"
}
}
# Security Group 4: Database Security Group
resource "aws_security_group" "dev_database_security_group" {
name = "DatabaseSecurityGroup"
description = "Security group for database access"
vpc_id = aws_vpc.aws-vpc.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.dev-webserver_security_group.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "dev_database_security_group"
env = "dev"
}
}
# Security Group 5: EFS Security Group
resource "aws_security_group" "dev-efs_security_group" {
name = "EFSSecurityGroup"
description = "Security group for Amazon EFS"
vpc_id = aws_vpc.aws-vpc.id
ingress {
from_port = 2049
to_port = 2049
protocol = "tcp"
security_groups = [aws_security_group.dev-webserver_security_group.id]
}
ingress {
from_port = 2049
to_port = 2049
protocol = "tcp"
self = true
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
security_groups = [aws_security_group.dev-ssh_security_group.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "dev-efs_security_group"
env = "dev"
}
}
Apply changes:
terraform apply
Check AWS Console.

Remember to destroy resources at the end of the tutorial..
terraform destroy