We have the network and the server, but we still need to protect it with a firewall (Security Group) and, optionally, give it a fixed IP address (Elastic IP). Remember the concepts from Chapter 4 (subchapters 4.2 and 4.4); now we write them in Terraform and complete our first functional infrastructure.

Step 1: Create the Security Group (the firewall)

The Security Group (subchapter 4.2) controls what traffic enters and leaves the instance. For a web server, we want to allow HTTP, HTTPS, and, in a limited way, SSH for administration.

resource "aws_security_group" "web" {
  name        = "sg-servidor-web"
  description = "Allows HTTP, HTTPS, and limited SSH"
  vpc_id      = aws_vpc.principal.id

  # INGRESS rule: allow HTTP from anywhere
  ingress {
    description = "HTTP from internet"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # INGRESS rule: allow HTTPS from anywhere
  ingress {
    description = "HTTPS from internet"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # INGRESS rule: allow SSH ONLY from my IP
  ingress {
    description = "SSH only from my IP"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["203.0.113.25/32"]   # ← YOUR IP, not 0.0.0.0/0
  }

  # EGRESS rule: allow all outgoing traffic
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"     # all protocols
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "sg-servidor-web"
  }
}

Let's review the decisions, which apply what we've learned about security:

  • HTTP (80) and HTTPS (443) from 0.0.0.0/0: correct, because we want anyone to be able to see the public website.
  • SSH (22) only from your IP (/32): critical! Remember the beginner's mistake from subchapter 4.2. NEVER set SSH open to 0.0.0.0/0. Here we limit it to a specific IP (the /32 means "exactly this IP"). Replace 203.0.113.25 with your real IP.
  • Open egress: allows the server to access the internet (to download software from user_data). Remember that Security Groups are stateful (subchapter 6.4), so responses to incoming connections are automatically allowed.

Now the instance from subchapter 12.2 already finds its Security Group, because it referenced it with vpc_security_group_ids = [aws_security_group.web.id]. The pieces fit together.

Step 2 (optional): Create an Elastic IP

Remember the problem of changing IPs (subchapter 4.4): the public IP of an instance changes if you stop and start it. If you want a fixed IP, use an Elastic IP:

resource "aws_eip" "web" {
  instance = aws_instance.web.id
  domain   = "vpc"

  tags = {
    Name = "eip-servidor-web"
  }
}

This reserves an Elastic IP and associates it with our instance. From now on, the server has a stable public address.

Remember the cost warning (subchapter 4.4): AWS charges for unused Elastic IPs and for public IPv4 addresses in general. For a single test instance it's fine, but in real architectures it's common to put a load balancer in front (Chapter 13) instead of one Elastic IP per server. For this first project, the Elastic IP is perfect to have a stable address.

The complete set

We now have a functional infrastructure! Let's summarize everything we've built in the chapter so far:

┌──────────── VPC (10.0.0.0/16) ─────────────────┐
│  Internet Gateway ──── Route Table (0.0.0.0/0) │
│                                                 │
│  ┌─ Public Subnet (10.0.1.0/24) ──────────┐     │
│  │                                         │    │
│  │   ┌─ EC2 Instance ──────────────┐       │    │
│  │   │  Web server (Apache)         │       │   │
│  │   │  Protected by Security Group │       │   │
│  │   │  Fixed Elastic IP associated │       │   │
│  │   └──────────────────────────────┘      │    │
│  └─────────────────────────────────────────┘    │
│                                                 │
│  Security Group: HTTP/HTTPS open, SSH only to your IP │
└──────────────────────────────────────────────────┘

If you run terraform apply (typing yes), Terraform creates everything in the correct order and, in a couple of minutes, your server will be serving the web page "Hello from my first server on AWS with Terraform!" on its public IP.

What you should remember

  • The Security Group is defined with ingress (incoming) and egress (outgoing) rules, each with ports, protocol, and sources (cidr_blocks).
  • Apply real security: HTTP/HTTPS open to the public, but SSH only to your IP (/32), never to 0.0.0.0/0.
  • Since it's stateful, you don't need to manually open response traffic.
  • An Elastic IP (aws_eip) gives a fixed public address to the instance; remember its cost and that in production a load balancer is usually preferred.
  • With this you have your first functional infrastructure: network + web server + firewall + fixed IP, all in code.

In the next subchapter you'll learn how to extract useful information from your infrastructure (like the server's IP) with outputs and better understand references between resources.

Cloud, AWS & Terraform — From Zero to Expert

Chapter 1 · What is cloud computing

Chapter 2 · The cloud market and major providers

Chapter 3 · Regions, availability zones and edge

Chapter 4 · Compute: EC2

Chapter 5 · Storage: S3

Chapter 6 · Networking: VPC

Chapter 7 · Identity and access: IAM

Chapter 8 · Managed databases

Chapter 9 · Why Infrastructure as Code

Chapter 10 · HCL: the Terraform language

Chapter 11 · Providers and state

Chapter 12 · Your first real infrastructure in Terraform

Chapter 13 · Load balancing and auto scaling

Chapter 14 · Serverless with Lambda

Chapter 15 · Messaging and events

Chapter 16 · Content delivery and DNS

Chapter 17 · Containers on AWS

Chapter 18 · Modules: reuse and composition

Chapter 19 · Workspaces and environment management

Chapter 20 · Remote backends and locking

Chapter 21 · Infrastructure testing

Chapter 22 · Terraform in CI/CD

Chapter 23 · Defense in depth

Chapter 24 · Observability: logs, metrics and traces

Chapter 25 · Cost optimization

Chapter 26 · High availability and disaster recovery

Chapter 27 · AWS Well-Architected Framework

Chapter 28 · Serverless architectures at scale

Chapter 29 · Data platforms on AWS

Chapter 30 · Multi-account and landing zones

Chapter 31 · Platform Engineering and Internal Developer Platform

Chapter 32 · Relevant AWS certifications

Chapter 33 · Projects to consolidate what you've learned

Chapter 34 · Resources and community

© Copyright 2024. All rights reserved