Managing AWS Default VPC Security Groups with Terraform
When it comes to Amazon Web Services support Terraform
has coverage that’s second to none. It includes most of Amazons current
services, rapidly adds newly released ones, and even helps granularise existing
resources by adding terraform specific extensions for things like individual
rules with aws_security_group_rule
. This awesome coverage makes it even more
jarring when you encounter one of the rare edge cases, such as VPC default
security groups.
It’s worth taking a step back and thinking about how Terraform normally works.
When you write code to manage resources terraform expects to fully own the
created resources life cycle. It will create it, ensure that changes made are
correctly reflected (and remove those made manually), and when resources code
is removed from the .tf
files it will destroy it. While this is fine for 99%
of the supported Amazon resources the VPC default security group is a little
different.
Each Amazon Virtual Private Cloud (VPC) created will have a default security
group provided. This is created by Amazon itself and is often undeletable.
Rather than leaving it unmanaged, which happens all too often, we can instead
add it to terraforms control with the special
aws_default_security_group
resource. When used this resource works a little differently than most others.
Terraform doesn’t attempt to create the group, instead it’s adopted under its
management umbrella. This allows you to control what rules are placed in this
default group and stops the security group already exists
errors that will
happen if you try to manage it as a normal group.
The terraform code to add the default VPC security group looks surprisingly normal:
resource "aws_vpc" "myvpc" {
cidr_block = "10.2.0.0/16"
}
resource "aws_default_security_group" "default" {
vpc_id = "${aws_vpc.myvpc.id}"
# ... snip ...
# security group rules can go here
}
One nice little tweak I’ve found useful is to customise the default security group to only allow inbound access on port 22 from my current (very static) IP address.
# use the swiss army knife http data source to get your IP
data "http" "my_local_ip" {
url = "https://ipv4.icanhazip.com"
}
resource "aws_security_group_rule" "ssh_from_me" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["${chomp(data.http.my_local_ip.body)}/32"]
security_group_id = "${aws_default_security_group.default.id}"
}