What We're Building

VPC
├── Private Subnet (10.0.1.0/24)
├── Public Subnet  (10.0.2.0/24)
│     └── EC2 (Nginx installed via user_data)
├── Internet Gateway
├── Route Table → Public Subnet → IGW
└── Security Group → Allow HTTP port 80

Output: http://<public-ip> → Nginx webpage in browser


File Structure (Recommended)

aws-vpc-ec2-nginx/
├── main.tf              # Provider config
├── vpc.tf               # VPC, subnets, IGW, route table
├── ec2.tf               # EC2 instance
├── security_groups.tf   # Security group rules
└── outputs.tf           # Website URL output

One file works too but separate files = easier to manage


main.tf (Provider)

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "6.32.1"
    }
  }
}

provider "aws" {
  region = "eu-north-1"
}

vpc.tf

# VPC - main private network
resource "aws_vpc" "my_vpc" {
  cidr_block = "10.0.0.0/16"
  tags = { Name = "my_vpc" }
}

# Private Subnet - no internet (for DB, backend)
resource "aws_subnet" "private-subnet" {
  cidr_block = "10.0.1.0/24"
  vpc_id     = aws_vpc.my_vpc.id
  tags = { Name = "private-subnet" }
}

# Public Subnet - has internet access
# map_public_ip_on_launch = true → auto assign public IP to instances
resource "aws_subnet" "public-subnet" {
  cidr_block              = "10.0.2.0/24"
  vpc_id                  = aws_vpc.my_vpc.id
  map_public_ip_on_launch = true   # Every instance here gets public IP
  tags = { Name = "public-subnet" }
}
 
# Internet Gateway - connects VPC to internet
resource "aws_internet_gateway" "my-igw" {
  vpc_id = aws_vpc.my_vpc.id
  tags = { Name = "my-igw" }
}

# Route Table - send all traffic to IGW
resource "aws_route_table" "my-rt" {
  vpc_id = aws_vpc.my_vpc.id

  route {
    cidr_block = "0.0.0.0/0"                    # All traffic
    gateway_id = aws_internet_gateway.my-igw.id  # Go through IGW
  }
}

# Associate public subnet with route table
resource "aws_route_table_association" "public-sub" {
  route_table_id = aws_route_table.my-rt.id
  subnet_id      = aws_subnet.public-subnet.id
}

security_groups.tf

resource "aws_security_group" "nginx-sg" {
  vpc_id = aws_vpc.my_vpc.id

  # Inbound - Allow HTTP (port 80) from anywhere
  # Without this → server runs but no one can access it from browser
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]  # Anyone can access
  }

  # Outbound - Allow all traffic out
  # Needed for yum install to download packages
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"           # -1 = all protocols
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = { Name = "nginx-sg" }
}

Security Group - Ingress vs Egress (refer to documentation agar yaad na ho format to simply)

Ingress = Inbound  = Traffic coming IN  (HTTP request from browser)
Egress  = Outbound = Traffic going OUT  (downloading nginx package)

ec2.tf