As I continue my Terraform journey, I am always trying to find better ways to deal with the complexities of the code and the resulting resources built from the code. Security Groups are no execption.
I did some web searching and found some code to parse a CSV and create a security group rule set. I took that idea and expanded on it. The idea is you pass a CSV with all the rules and the code is designed to create the Security Group (SG) and add the rules within the given SG.
A sample CSV:
id,type,name,cidr_block,protocol,from_port,to_port,description
1,egress,security-group-1,"10.2.20.0/24,10.2.21.0/24",tcp,27017,27017,docdb - 27017
2,egress,security-group-1,"10.2.20.0/24,10.2.21.0/24",tcp,6379,6379,docdb - 6379
3,egress,security-group-1,"10.2.20.0/24,10.2.21.0/24",tcp,8089,8089,docdb - 8089
4,egress,security-group-1,10.202.1.24/32,tcp,6611,6611,app1 - 6611
5,egress,security-group-1,10.202.1.24/32,tcp,6613,6613,app1 - 6613
6,egress,security-group-1,10.202.1.24/32,tcp,6617,6717,app1 - 6617
7,egress,security-group-1,10.202.1.24/32,tcp,6646,6647,app1 - 6646-6647
8,egress,security-group-1,192.168.27.7/32,tcp,6331,6331,app1-test - 6331
9,egress,security-group-1,192.168.27.7/32,tcp,6609,6609,app1-test - 6609
10,egress,security-group-1,192.168.27.7/32,tcp,6612,6613,app1-test 6612 - 6613
11,egress,security-group-2,192.168.1.101/32,tcp,876,876,app3-test
12,egress,security-group-2,192.168.1.101/32,tcp,877,876,app3-test
13,egress,security-group-2,192.168.1.101/32,tcp,999,999,app3-test
14,egress,security-group-2,192.168.1.101/32,tcp,989,989,app3-test
The above CSV is passed to the TF code and results in creating two Security Groups – “security-group-1” with 9 rules and “security-group-2” with 4 rules. The first column is used by the TF code to determine the each rule by id (sudo index).
Here’s the TF code:
data "aws_caller_identity" "current" {}
locals {
csv_data_app = file("${path.module}/securitygrouprules.csv")
appip = csvdecode(local.csv_data_app)
vpc_id = "vpc-001abc002efg0003"
sg_app_details = flatten([
for sg in local.appip : {
id = sg.id
type = sg.type
name = sg.name
description = sg.description
protocol = sg.protocol
from_port = sg.from_port
to_port = sg.to_port
cidr_blocks = sg.cidr_block
}
])
sg_app_names = distinct(flatten([
for sg in local.appip : {
name = sg.name
}
]))
}
resource "aws_security_group" "security_groups" {
for_each = {
for key in local.sg_app_names : key.name => key }
name = each.value.name
vpc_id = local.vpc_id
}
resource "aws_security_group_rule" "samplesg_ingress" {
for_each = { for key in local.sg_app_details : key.id => key }
security_group_id = aws_security_group.security_groups["${each.value.name}"].id
type = each.value.type
description = each.value.description
protocol = each.value.protocol
from_port = each.value.from_port
to_port = each.value.to_port
cidr_blocks = split(",", each.value.cidr_blocks)
}
output "sgs" {
value = values(aws_security_group.security_groups)[*].id
}
output "account" {
value = data.aws_caller_identity.current.account_id
}

Fun problem to work on.
Happy Building,
D