Security is one of the most important elements of any cloud architecture and in AWS, Security Groups act as firewalls to control the inbound and outbound traffic to your EC2 instances and other resources. While you can easily manage the AWS Console, doing so on a larger scale is extremely prone to human errors.
This is where you need Terraform. The platform enables you to define and manage Security Groups as IaC, making the rules repeatable, version-controlled, and easy to audit.
In this guide, we will learn how to create, manage, and scale AWS Security Group Terraform.
What Are AWS Security Groups?
AWS Security Groups act as virtual firewalls for your EC2 instances and other AWS resources. They control all the inbound and outbound traffic based on the rules that you define. Each rule is defined for specific protocols, ports, and IP ranges. Key characteristics of an AWS Security Group are as follows:
- Stateful means that if you allow inbound traffic on a part, the outbound response is automatically allowed.
- AWS Security Groups are attached to the network interfaces and are typically applied to the EC2 instances.
- One Security Group can be attached to multiple resources at one time, making it highly reusable.
Why Use Terraform for Security Groups?
You can easily create Security Groups using the AWS Console manually, but Terraform offers multiple advantages.
Get exclusive access to all things tech-savvy, and be the first to receive
the latest updates directly in your inbox.
- Define Security Groups as code and automate the deployments.
- Using Git, you can easily track changes and other version control systems.
- Prevent human-prone errors by enforcing predefined configurations.
- Easily modify and replicate groups across different environments.
- Seamless integration with other AWS infrastructures.
Example: Basic Security Group Terraform
Here is an example Terraform configuration to setup an AWS Security Group with inbound SSH access from a certain IP range.
resource “aws_security_group” “example_sg” {
name = “example-sg”
description = “Allow SSH”
vpc_id = aws_vpc.main.id
ingress {
description = “SSH”
from_port = 22
to_port = 22
protocol = “tcp”

cidr_blocks = [“203.0.113.0/24”]
}
egress {
from_port = 0
to_port = 0
protocol = “-1”
cidr_blocks = [“0.0.0.0/0”]
}
tags = {
Name = “example-sg”
}
}
Adding Ingress and Egress Rules
Terraform allows you to define the inbound traffic rules, known as ingress, and outbound traffic rules, known as the egress inside the Security Group to manage them separately.
- Ingress Rule Example
Allow HTTP (port 80) access from anywhere:
ingress {
description = “HTTP”
from_port = 80
to_port = 80
protocol = “tcp”
cidr_blocks = [“0.0.0.0/0”]
}
- Egress Rule Example
Allow all outbound traffic (default behavior):
egress {
from_port = 0
to_port = 0
protocol = “-1” # all protocols
cidr_blocks = [“0.0.0.0/0”]
}
You can also use security_group_rule resources to manage these rules independently of the security group.
Related Article: Terraform on AWS: A Complete Guide to Infrastructure Automation
Managing Security Group Rules Separately
Some use cases that are based in large environments or use modules, you might need to define ingress and egress rules separately from the primary Security Group. Terraform enables you to do this via the aws_security_group_rule resource.
- Example: Standalone Security Group Rules
resource “aws_security_group” “web_sg” {
name = “web-sg”
description = “Web Server SG”
vpc_id = aws_vpc.main.id
}
resource “aws_security_group_rule” “allow_http” {
type = “ingress”
from_port = 80
to_port = 80
protocol = “tcp”
cidr_blocks = [“0.0.0.0/0”]
security_group_id = aws_security_group.web_sg.id
}
resource “aws_security_group_rule” “allow_all_egress” {
type = “egress”
from_port = 0
to_port = 0
protocol = “-1”
cidr_blocks = [“0.0.0.0/0”]
security_group_id = aws_security_group.web_sg.id
}
This approach is modular and useful when reusing or dynamically creating rules via for_each or count.
Using Terraform Modules for Security Groups
Modules allow you to extract and reuse Security Group configurations across different environments and teams.
- Example Module Structure
Folder structure:
modules/
└── security_group/
├── main.tf
├── variables.tf
└── outputs.tf
main.tf (inside module):
resource “aws_security_group” “this” {
name = var.name
description = var.description
vpc_id = var.vpc_id
dynamic “ingress” {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
dynamic “egress” {
for_each = var.egress_rules
content {
from_port = egress.value.from_port
to_port = egress.value.to_port
protocol = egress.value.protocol
cidr_blocks = egress.value.cidr_blocks
}
}
}
Then call it like this:
module “web_sg” {
source = “./modules/security_group”
name = “web-sg”
description = “Allow web traffic”
vpc_id = aws_vpc.main.id
ingress_rules = [
{
from_port = 80
to_port = 80
protocol = “tcp”
cidr_blocks = [“0.0.0.0/0”]
}
]
egress_rules = [
{
from_port = 0
to_port = 0
protocol = “-1”
cidr_blocks = [“0.0.0.0/0”]
}
]
}
Common Pitfalls and Troubleshooting Guide For AWS Security Group Terraform
Issue | Cause | Solution |
InvalidCIDR error | CIDR block is incorrectly formatted | Use valid CIDR notation, e.g., 0.0.0.0/0 |
Duplicate rule errors | Rules are defined both in SG and via security_group_rule | Use only one method per rule set |
Dependency issues | Resource order isn’t clear | Use depends_on or split modules clearly |
Port mismatch | From/To ports misconfigured | Ensure correct port ranges are set |
VPC ID missing | VPC not linked to SG | Pass correct vpc_id in the SG block |
Overlapping rules | Multiple rules do the same thing | Simplify or deduplicate rule definitions |
Use Cases for AWS Security Groups with Terraform
- Provisioning Security Groups for Web Servers
Your team needs to launch a fleet of EC2 instances. So you can use Terraform to create the instances automatically.
- Allows inbound HTTP (port 80) and HTTPS (port 443)
- Allows SSH (port 22) access from a specific IP range
- Restricts all other access
ingress {
from_port = 80
to_port = 80
protocol = “tcp”
cidr_blocks = [“0.0.0.0/0”]
}
- Dynamic Rules for Bastion Hosts
Your team uses a bastion server to access the instances securely in a private environment. Define a Terraform module to host Security Groups for:
- Allows SSH (port 22) from your office IP
- Allows outbound traffic to private subnet resources
- Denies all other inbound access
- Security Groups for RDS Databases
Your RDS instance should only accept connections from application servers. Use a Terraform aws_security_group_rule to allow inbound traffic from the app server’s SG ID.
source_security_group_id = aws_security_group.app_sg.id
- Creating Environment-Specific Rules (Dev, Staging, Prod)
If you want to set a specific set of rules for different development environments, use variables or workspaces in Terraform to dynamically assign different CIDR blocks or ports.
variable “env” {
default = “dev”
}
- Restricting Egress for Compliance
Your organization needs to block all outbound traffic except for a specific IP range, so you can define the rules in Terraform Security Group.
egress {
from_port = 443
to_port = 443
protocol = “tcp”
cidr_blocks = [“198.51.100.0/24”]
}
- Using for_each to Create Multiple Rules Dynamically
If your team wants to open multiple ports for the same Security Group, you can use a port list and loop through them with for_each:
variable “ports” {
default = [80, 443, 8080]
}
resource “aws_security_group_rule” “http_rules” {
for_each = toset(var.ports)
type = “ingress”
from_port = each.value
to_port = each.value
protocol = “tcp”
cidr_blocks = [“0.0.0.0/0”]
security_group_id = aws_security_group.web_sg.id
}
Conclusion – AWS Security Group Terraform
Security Groups are one of the core components of an AWS infrastructure, and when managed with Terraform, it brings advanced automation, control, and consistency to your cloud environment. Therefore, whether you need to automate a few specific ports or set rules for all of them, Terraform makes it easy to maintain the AWS infrastructure.
How do I create a basic Security Group with Terraform?
You can use the aws_security_group
resource block in Terraform to define the name, description, VPC, and rule sets (ingress/egress) for your Security Group.
What’s the benefit of using modules for Security Groups in Terraform?
Modules promote reuse, simplify maintenance, and make it easier to manage security groups across environments like dev, staging, and production.
What are common issues with Security Groups in Terraform?
Common pitfalls include cyclical dependencies, overlapping rules, improper CIDR formatting, or not updating state after manual changes in the AWS Console.