A concise way of creating AWS Security Groups (using Terraform)
aws |
Security Group Overview
A security group is a virtual firewall which can be assigned to an instance running in an AWS Virtual Private Cloud(VPC).
Inbound and Outbound Rules
For ingress (inbound) rules the source can be the same security group (self), another security group, an IPv4 or IPv6 CIDR block, a single IPv4 or IPv6 address, or a prefix list ID.
For egress(outbound rules only) the destination can be the same security group (self), another security group, an IPv4 or IPv6 CIDR block, a single IPv4 or IPv6 address, or a prefix list ID.
Defining multiple ingress and egress rules using Terraform can become quite verbose.
For one ingress rule you would have to write
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [ "0.0.0.0/0" ]
description = "Allow HTTPS"
}
Having multiple security groups with many ingress and egress rules can make it difficult to analyze and update your configurations, especially when these rules are distributed across multiple files.
What if you could sum up the rule above as
"443,443,tcp,0.0.0.0/0,Allow HTTPS"
Here is an example describing how self(same security group), source security group, and CIDR block rules work.
These are some of the rules created for the 3 security groups which are part of the example below - where I’ll try to demonstrate a (hopefully) more concise method of creating security groups using a module I wrote.
Create an example VPC and Security Groups
The security group module code (v1.2.6) used in this example.
git clone https://github.com/serbangilvitu/terraform-examples.git
cd terraform-examples/aws/vpc-sg/
git checkout v1.0.3
terraform init
terraform apply
...
Plan: 8 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
This example has created a VPC with 1 subnet and 3 security groups.
You might have noticed I have not passed any argument to terraform apply
, as the values are being read from the *.auto.tfvars files
SG A - Allow traffic from self and IPv4 CIDR block
The values used for the rules of this security group are
sg_a_ingress_from_self_list = [ "80,80,tcp,Allow HTTP" ]
sg_a_ingress_cidr_list = [
"443,443,tcp,0.0.0.0/0,Allow HTTPS",
"22,22,tcp,10.0.32.0/20,Allow SSH"
]
sg_a_egress_cidr_list = [
"0,0,-1,0.0.0.0/0,Allow any"
]
One ingress or egress rule can be defined in one line, and all the rules of the security group can be swiftly analyzed.
The code for this security group contains arguments which could have been skipped, as they are not being used, and they will default to an empty list [].
The only argument which doesn’t default to an empty list is egress_cidr_list which defaults to [ "0,0,-1,0.0.0.0/0" ]
.
The reason I’ve added so many arguments in this security groups was to present all scenarios supported by the module.
module "sg_a" {
source = "git::https://github.com/serbangilvitu/terraform-modules.git//aws/security-group?ref=v1.2.6"
name_prefix = "${var.stack_name}-a"
description = "Example a"
vpc_id = module.vpc.id
ingress_cidr_list = var.sg_a_ingress_cidr_list
ingress_from_self_list = var.sg_a_ingress_from_self_list
ingress_ipv6_cidr_list = var.sg_a_ingress_ipv6_cidr_list
ingress_prefix_list_ids = var.sg_a_ingress_prefix_list_ids
ingress_source_sg_list = var.sg_a_ingress_source_sg_list
egress_cidr_list = var.sg_a_egress_cidr_list
egress_from_self_list = var.sg_a_egress_from_self_list
egress_ipv6_cidr_list = var.sg_a_egress_ipv6_cidr_list
egress_prefix_list_ids = var.sg_a_egress_prefix_list_ids
egress_source_sg_list = var.sg_a_egress_source_sg_list
stack_name = var.stack_name
additional_tags = var.additional_tags
}
And this is the result
SG B - An even more concise version of SG A
SG B was created by copying SG A’s code, then removing the arguments which are not being used (and will use the module’s default values as mentioned above).
Here’s the code
module "sg_b" {
source = "git::https://github.com/serbangilvitu/terraform-modules.git//aws/security-group?ref=v1.2.6"
name_prefix = "${var.stack_name}-b"
description = "Example b"
vpc_id = module.vpc.id
ingress_from_self_list = var.sg_b_ingress_from_self_list
ingress_cidr_list = var.sg_b_ingress_cidr_list
stack_name = var.stack_name
additional_tags = var.additional_tags
}
The values can be found here.
The following security group was created:
SG C - Allow traffic from SG A and SG B
For SG C we’re allowing traffic to and from SG A and SG B
module "sg_c" {
source = "git::https://github.com/serbangilvitu/terraform-modules.git//aws/security-group?ref=v1.2.6"
name_prefix = "${var.stack_name}-c"
description = "Example c"
vpc_id = module.vpc.id
ingress_source_sg_list = [
join(",", ["0", "0", "-1", module.sg_a.id, "Allow all traffic from a"]),
join(",", ["443", "443", "tcp", module.sg_b.id, "Allow HTTPS traffic from b"])
]
egress_source_sg_list = [
join(",", ["0", "0", "-1", module.sg_a.id, "Allow all traffic to a"]),
join(",", ["0", "0", "-1", module.sg_b.id, "Allow all traffic to b"])
]
egress_cidr_list = []
stack_name = var.stack_name
additional_tags = var.additional_tags
}
More arguments
There are some arguments I did not include like ingress_ipv6_cidr_list, ingress_prefix_list_ids - those should will work in the same way.