Terraform Level 1, Task 2: Building a Virtual Firewall with Code¶
Building on my first experience with Terraform, today's task was to create a foundational piece of cloud security: an AWS Security Group. This was a perfect next step because it moved from a simple resource to one with a more complex, nested structure, including rules for network traffic.
I learned how to translate security requirements (like "allow HTTP and SSH traffic") into a declarative Terraform script. This process of codifying security rules is a core practice in DevOps, ensuring that my infrastructure's firewall is as version-controlled and reproducible as the servers it protects.
Table of Contents¶
- The Task
- My Step-by-Step Solution
- Why Did I Do This? (The "What & Why")
- Deep Dive: Decoding the
aws_security_groupResource - Common Pitfalls
- Exploring the Commands Used
The Task¶
My objective was to use Terraform to create a new AWS Security Group with specific firewall rules. The requirements were:
1. All code must be in a single main.tf file.
2. The security group's name must be nautilus-sg.
3. It needed a specific description: Security group for Nautilus App Servers.
4. It required two inbound rules:
- Allow HTTP traffic on port 80 from the entire internet (0.0.0.0/0).
- Allow SSH traffic on port 22 from the entire internet (0.0.0.0/0).
My Step-by-Step Solution¶
The process followed the standard, predictable Terraform workflow.
Phase 1: Writing the Code¶
In the /home/bob/terraform directory, I created my main.tf file and wrote the following declarative code to define my security group.
# 1. Configure the AWS Provider to set the region
provider "aws" {
region = "us-east-1"
}
# 2. Define the Security Group Resource
resource "aws_security_group" "nautilus_sg_resource" {
name = "nautilus-sg"
description = "Security group for Nautilus App Servers"
# 3. Define the Inbound Rule for HTTP
ingress {
description = "Allow HTTP inbound"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# 4. Define the Inbound Rule for SSH
ingress {
description = "Allow SSH inbound"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
Phase 2: The Terraform Workflow¶
From my terminal in the same directory, I executed the three essential commands.
-
Initialize: This downloaded the AWS provider plugin.
terraform init -
Plan: This showed me a "dry run" preview, confirming that Terraform intended to create one
aws_security_groupresource with the rules I defined.terraform plan -
Apply: This command actually built the resource in my AWS account.
After I confirmed by typingterraform applyyes, Terraform created the security group and reported success:Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
This successful output was the primary verification that my task was complete.
Why Did I Do This? (The "What & Why")¶
- Security Group (SG): This is the fundamental building block of network security for AWS EC2 instances. It acts as a virtual firewall at the server level. By defining rules, I am controlling exactly what kind of traffic is allowed to reach my server.
- ingress (Inbound) Rules: These rules apply to traffic coming into the server. By default, an SG blocks all inbound traffic. I had to explicitly create rules to open the "doors" for web and SSH traffic.
- egress (Outbound) Rules: These rules apply to traffic leaving the server. By default, an SG allows all outbound traffic. I didn't need to define any egress rules for this task.
- 0.0.0.0/0 (CIDR Block): This is a special network address that means "the entire internet." By specifying this as the source cidr_blocks, I was allowing anyone from anywhere to connect to my server on ports 80 and 22. In a real production environment, I would restrict the SSH source to a much smaller, trusted IP range (like my office or a VPN) for better security.
Deep Dive: Decoding the aws_security_group Resource¶
Understanding the structure of this Terraform resource was the core of the task.
# This is the resource block.
# "aws_security_group" is the Resource TYPE, telling Terraform what to create.
# "nautilus_sg_resource" is the local NAME, which I use to refer to this resource
# within my Terraform code. It doesn't affect the name in AWS.
resource "aws_security_group" "nautilus_sg_resource" {
# This is the 'name' argument. This IS the name that will appear
# in the AWS console.
name = "nautilus-sg"
description = "Security group for Nautilus App Servers"
# This is an 'ingress' block, defining an inbound rule.
# I can have multiple ingress blocks for multiple rules.
ingress {
description = "Allow HTTP inbound" # A helpful comment for the rule.
from_port = 80 # The start of the port range.
to_port = 80 # The end of the port range. For a single port, they are the same.
protocol = "tcp" # The network protocol (most web traffic is TCP).
cidr_blocks = ["0.0.0.0/0"] # A list of source IP ranges to allow.
}
ingress {
description = "Allow SSH inbound"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
Common Pitfalls¶
-
Conflicting Security Group Names: Security group names must be unique within a single AWS VPC. If a group named nautilus-sg already existed, my terraform apply command would have failed.
-
Forgetting cidr_blocks: The cidr_blocks argument is a list. Even for a single entry, it must be enclosed in square brackets []. Forgetting them would cause a syntax error.
-
Typos in Protocol Names: Using "TCP" instead of the required lowercase "tcp" would cause an error during the plan or apply phase.
-
Mixing up name and the local resource name: It's important to remember that the name argument inside the resource block is what sets the name in AWS, while the name after the resource type (nautilus_sg_resource) is just for use within Terraform.
Exploring the Commands Used¶
-
terraform init: The first command to run. It prepares the working directory by downloading the necessary provider plugins (in this case, the aws provider). -
terraform plan: A "dry run" command. It reads my code, compares it to the current state of my infrastructure in AWS, and shows me a detailed plan of what it will create, change, or destroy. -
terraform apply: The command that executes the plan. It builds the infrastructure as defined in the code, asking for a final yes for safety before making any changes.