Terraform AWS Security Group Module
Β Β Β Β Β Β Β Β Β
A Terraform module for creating and managing AWS security groups and their ingress/egress rules as two independent submodules. The split-module design avoids circular dependencies between cross-referencing security groups and prevents drift caused by inline rule blocks on aws_security_group.
Split-module design β security groups and rules are independent resources
Single map(object({...})) input per submodule, consumed via for_each
No inline ingress/egress blocks β all rules via aws_vpc_security_group_ingress_rule / aws_vpc_security_group_egress_rule
Cross-references between security groups (e.g. ALB β app tier) without circular dependency
Supports all rule source types: cidr_ipv4, cidr_ipv6, referenced_security_group_id, prefix_list_id
Built-in input validation (name length, vpc_id format, port ranges, protocol, mutual exclusivity of rule sources)
create_before_destroy lifecycle on every security group
Module
Description
security-group
Creates aws_security_group resources (no inline rules)
security-group-rules
Creates aws_vpc_security_group_ingress_rule and aws_vpc_security_group_egress_rule resources
Single security group with rules
module "security_groups" {
source = " github.com/subhamay-bhattacharyya-tf/terraform-aws-security-group/modules/security-group?ref=main"
region = " us-east-1"
security_groups = {
web = {
name = " web-sg"
description = " Web tier security group"
vpc_id = " vpc-0123456789abcdef0"
tags = {
Tier = " web"
}
}
}
}
module "security_group_rules" {
source = " github.com/subhamay-bhattacharyya-tf/terraform-aws-security-group/modules/security-group-rules?ref=main"
region = " us-east-1"
rules = {
web_https_ingress = {
security_group_id = module.security_groups.security_group_ids[" web" ]
type = " ingress"
from_port = 443
to_port = 443
ip_protocol = " tcp"
cidr_ipv4 = " 0.0.0.0/0"
description = " Allow HTTPS from the internet"
}
web_all_egress = {
security_group_id = module.security_groups.security_group_ids[" web" ]
type = " egress"
from_port = 0
to_port = 0
ip_protocol = " -1"
cidr_ipv4 = " 0.0.0.0/0"
description = " Allow all egress"
}
}
}
Cross-referenced security groups (ALB β app)
module "security_groups" {
source = " github.com/subhamay-bhattacharyya-tf/terraform-aws-security-group/modules/security-group?ref=main"
region = " us-east-1"
security_groups = {
alb = {
name = " alb-sg"
description = " ALB (public-facing) security group"
vpc_id = " vpc-0123456789abcdef0"
}
app = {
name = " app-sg"
description = " Application tier security group"
vpc_id = " vpc-0123456789abcdef0"
}
}
}
module "security_group_rules" {
source = " github.com/subhamay-bhattacharyya-tf/terraform-aws-security-group/modules/security-group-rules?ref=main"
region = " us-east-1"
rules = {
alb_https_ingress = {
security_group_id = module.security_groups.security_group_ids[" alb" ]
type = " ingress"
from_port = 443
to_port = 443
ip_protocol = " tcp"
cidr_ipv4 = " 0.0.0.0/0"
}
alb_to_app_egress = {
security_group_id = module.security_groups.security_group_ids[" alb" ]
type = " egress"
from_port = 8080
to_port = 8080
ip_protocol = " tcp"
referenced_security_group_id = module.security_groups.security_group_ids[" app" ]
}
app_from_alb_ingress = {
security_group_id = module.security_groups.security_group_ids[" app" ]
type = " ingress"
from_port = 8080
to_port = 8080
ip_protocol = " tcp"
referenced_security_group_id = module.security_groups.security_group_ids[" alb" ]
}
}
}
module "security_group_rules" {
source = " github.com/subhamay-bhattacharyya-tf/terraform-aws-security-group/modules/security-group-rules?ref=main"
region = " us-east-1"
rules = {
ipv6_https_ingress = {
security_group_id = module.security_groups.security_group_ids[" web" ]
type = " ingress"
from_port = 443
to_port = 443
ip_protocol = " tcp"
cidr_ipv6 = " ::/0"
}
}
}
Rule using a managed prefix list
module "security_group_rules" {
source = " github.com/subhamay-bhattacharyya-tf/terraform-aws-security-group/modules/security-group-rules?ref=main"
region = " us-east-1"
rules = {
s3_gateway_egress = {
security_group_id = module.security_groups.security_group_ids[" app" ]
type = " egress"
from_port = 443
to_port = 443
ip_protocol = " tcp"
prefix_list_id = " pl-0123456789abcdef0"
}
}
}
Example
Description
basic
Single security group with HTTPS/HTTP ingress and all-egress via cidr_ipv4
cross-referenced
Two security groups (alb and app) demonstrating SG-to-SG references via referenced_security_group_id
Name
Version
terraform
>= 1.3.0
aws
>= 5.0.0
Name
Version
aws
>= 5.0.0
Name
Description
Type
Default
Required
region
AWS region where the security groups will be created
string
-
yes
security_groups
Map of security groups to create
map(object)
-
yes
security_groups object properties
Property
Type
Default
Description
name
string
-
AWS resource name of the security group (required)
description
string
"Managed by Terraform"
Security group description
vpc_id
string
-
VPC ID (must match ^vpc-[a-f0-9]+$, required)
tags
map(string)
{}
Tags applied to the security group
Name
Description
security_group_ids
Map of logical keys to security group IDs
security_group_arns
Map of logical keys to security group ARNs
security_group_names
Map of logical keys to security group names
security_group_vpc_ids
Map of logical keys to VPC IDs
security-group-rules submodule
security-group-rules inputs
Name
Description
Type
Default
Required
region
AWS region where the rules will be created
string
-
yes
rules
Map of ingress/egress rules to create
map(object)
-
yes
Property
Type
Default
Description
security_group_id
string
-
ID of the target security group (required)
type
string
-
"ingress" or "egress" (required)
from_port
number
-
Start of the port range, 0β65535 (required)
to_port
number
-
End of the port range, 0β65535 (required)
ip_protocol
string
-
One of tcp, udp, icmp, icmpv6, -1 (required)
cidr_ipv4
string
null
IPv4 CIDR source/destination
cidr_ipv6
string
null
IPv6 CIDR source/destination
referenced_security_group_id
string
null
Source/destination security group ID
prefix_list_id
string
null
Managed prefix list ID
description
string
"Managed by Terraform"
Rule description
tags
map(string)
{}
Tags applied to the rule
Exactly one of cidr_ipv4, cidr_ipv6, referenced_security_group_id, or prefix_list_id must be set per rule.
security-group-rules outputs
Name
Description
ingress_rule_ids
Map of logical keys to ingress rule IDs
ingress_rule_arns
Map of logical keys to ingress rule ARNs
egress_rule_ids
Map of logical keys to egress rule IDs
egress_rule_arns
Map of logical keys to egress rule ARNs
Resource
Description
aws_security_group
Security group (one per map entry, no inline rules)
security-group-rules resources
Resource
Description
aws_vpc_security_group_ingress_rule
One ingress rule per map entry where type == "ingress"
aws_vpc_security_group_egress_rule
One egress rule per map entry where type == "egress"
All validation lives in each submodule's variables.tf:
security_groups[*].name is non-empty and at most 255 characters
security_groups[*].vpc_id matches ^vpc-[a-f0-9]+$
rules[*].type is one of ingress, egress
rules[*].ip_protocol is one of tcp, udp, icmp, icmpv6, -1
rules[*].from_port and rules[*].to_port are in [0, 65535]
Exactly one of cidr_ipv4, cidr_ipv6, referenced_security_group_id, prefix_list_id is non-null per rule
The module includes a Terratest-based integration test that creates real security groups and rules, asserts the outputs, then destroys them:
cd test
go mod tidy
go test -v -timeout 30m -run TestSecurityGroupBasic ./security_group_basic_test.go ./helpers_test.go
AWS credentials must be configured via environment variables, AWS CLI profile, or (in CI) OIDC. AWS_REGION and AWS_VPC_ID are required.
The CI workflow (.github/workflows/ci.yaml) runs on:
Push to main, feature/**, and bug/** branches (when modules/**, examples/**, or test/** change)
Pull requests to main (same path filter)
Manual workflow dispatch
Jobs:
terraform-validate β fmt -check, init, validate on both submodules
examples-validate β init + validate on all examples/*
security-group-terratest β real AWS integration test via OIDC
generate-changelog β runs git-cliff on non-main branches
semantic-release β runs only on main; uses Conventional Commits to auto-version
Secret
Description
AWS_ROLE_ARN
IAM role ARN for OIDC authentication
Variable
Description
Default
AWS_REGION
AWS region for Terratest
-
AWS_VPC_ID
VPC ID used by Terratest to provision security groups
-
TERRAFORM_VERSION
Terraform version for CI jobs
1.3.0
GO_VERSION
Go version for Terratest
1.21
MIT License β see LICENSE for details.