Setting Up Security in AWS with Terraform – EC2 Security Groups & AMI Lookup Explained

In this post, I’ll walk you through a key part of deploying an Application Load Balancer (ALB) on AWS using Terraform configuring security groups and retrieving the latest Amazon Linux 2 AMI.
We’ll go step by step through each Terraform module and output, so if you’re new to infrastructure as code or still building your confidence in AWS and Terraform, you’re in the right place.
Also, if you’re not familiar with setting up a VPC in Terraform, I covered that in this earlier article: 👉 Streamlining AWS Infrastructure with Terraform: A Modular Approach
🔐 What Are We Setting Up?
We’re defining three security groups using the terraform-aws-modules/security-group/aws module:
- A public bastion host
- Private EC2 instances
- A public load balancer
We’re also using a data block to retrieve the latest Amazon Linux 2 AMI dynamically.
1️⃣ Public Bastion Host Security Group
module "public_bastion_sg" {
source = "terraform-aws-modules/security-group/aws"
version = "5.2.0"
name = "public-bastion-sg"
description = "Security Group with SSH port open for everybody (IPv4 CIDR), egress ports are all world open"
vpc_id = module.vpc.vpc_id
ingress_rules = ["ssh-tcp"]
ingress_cidr_blocks = ["0.0.0.0/0"]
egress_rules = ["all-all"]
tags = local.common_tags
}
🔍 Explanation:
- source and version: Load a specific version of a well-maintained security group module.
- name & description: Used for identification and documentation.
- vpc_id: Assigns this security group to your VPC.
- ingress_rules: Allows SSH (port 22) from any IP.
- egress_rules: Allows all outbound traffic.
- tags: Reuses standard tags (from a locals block).
2️⃣ Private EC2 Instances Security Group
module "private_sg" {
source = "terraform-aws-modules/security-group/aws"
version = "5.2.0"
name = "private-sg"
description = "Security Group with HTTP & SSH port open for entire VPC Block (IPv4 CIDR), egress ports are all world open"
vpc_id = module.vpc.vpc_id
ingress_rules = ["ssh-tcp", "http-80-tcp"]
ingress_cidr_blocks = [module.vpc.vpc_cidr_block]
egress_rules = ["all-all"]
tags = local.common_tags
}
🔍 Explanation:
- This group allows both SSH and HTTP traffic, but only from within the VPC which is more secure than open access.
- Suitable for EC2 instances that shouldn’t be exposed to the public internet.
3️⃣ Public Load Balancer Security Group
module "loadbalancer_sg" {
source = "terraform-aws-modules/security-group/aws"
version = "5.2.0"
name = "loadbalancer-sg"
description = "Security Group with HTTP open for entire Internet (IPv4 CIDR), egress ports are all world open"
vpc_id = module.vpc.vpc_id
ingress_rules = ["http-80-tcp"]
ingress_cidr_blocks = ["0.0.0.0/0"]
egress_rules = ["all-all"]
tags = local.common_tags
ingress_with_cidr_blocks = [
{
from_port = 81
to_port = 81
protocol = 6
description = "Allow Port 81 from internet"
cidr_blocks = "0.0.0.0/0"
},
]
}
🔍 Explanation:
- Allows HTTP traffic (port 80) and a custom rule for port 81 from any IP.
- Useful for public-facing applications like web frontends or ALBs.
📤 Security Group Outputs
output "public_bastion_sg_group_id" {
description = "The ID of the security group"
value = module.public_bastion_sg.security_group_id
}
This block and others like it make key values (like SG IDs, names, and VPC IDs) available for use in other modules, files, or outputs.
Repeat for:
- public_bastion_sg_group_vpc_id
- public_bastion_sg_group_name
- private_sg_group_id
- etc.
🧠 Bonus: Latest Amazon Linux 2 AMI Lookup
data "aws_ami" "amzlinux2" {
most_recent = true
owners = [ "amazon" ]
filter {
name = "name"
values = [ "amzn2-ami-hvm-*-gp2" ]
}
filter {
name = "root-device-type"
values = [ "ebs" ]
}
filter {
name = "virtualization-type"
values = [ "hvm" ]
}
filter {
name = "architecture"
values = [ "x86_64" ]
}
}
🔍 Explanation:
- Dynamically fetches the latest Amazon Linux 2 AMI based on filters.
- Ensures your EC2 instances always launch with the most up-to-date AMI.
✅ Wrapping Up
In this article, we covered how to securely configure access to your EC2 instances and load balancer using Terraform. You also learned how to dynamically fetch the latest Amazon Linux 2 AMI for automation-ready infrastructure.
In the next part, we’ll provision EC2 instances, associate them with these security groups, and prepare them to serve traffic via an Application Load Balancer.
👉 Stay tuned and follow me to get notified when the next article drops!