Project architecture
VPC
│
├── Public Subnet-1 ── EC2-1
├── Public Subnet-2 ── EC2-2
├── Internet Gateway
└── Route Table (0.0.0.0/0 → IGW)
Complete starter configuration
A single-subnet version you can apply and then expand:
resource "aws_vpc" "main" {
cidr_block = "10.10.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "terraform-vpc"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-south-1a"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet"
}
}
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "main-igw"
}
}
resource "aws_route_table" "public_rt" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
tags = {
Name = "public-route-table"
}
}
resource "aws_route_table_association" "public_assoc" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public_rt.id
}
resource "aws_security_group" "web_sg" {
name = "web-sg"
description = "Allow SSH and HTTP"
vpc_id = aws_vpc.main.id
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "web" {
ami = "ami-07a00cf47dbbc844c"
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web_sg.id]
tags = {
Name = "terraform-ec2"
}
}Opening SSH (22) to 0.0.0.0/0 is fine for short labs. Lock it to your IP (or remove it) for anything longer-lived.
Two-subnet walkthrough
1. VPC
resource "aws_vpc" "myvpc" {
cidr_block = var.cidr
}2–3. Public subnets
resource "aws_subnet" "sub1" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.0.0/24"
availability_zone = "ap-south-1a"
map_public_ip_on_launch = true
}
resource "aws_subnet" "sub2" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-south-1b"
map_public_ip_on_launch = true
}4. Internet gateway
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.myvpc.id
}5. Route table
resource "aws_route_table" "RT" {
vpc_id = aws_vpc.myvpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
}6. Associations
resource "aws_route_table_association" "rta1" {
subnet_id = aws_subnet.sub1.id
route_table_id = aws_route_table.RT.id
}
resource "aws_route_table_association" "rta2" {
subnet_id = aws_subnet.sub2.id
route_table_id = aws_route_table.RT.id
}7. Security group
Allow SSH (22) and HTTP (80) on a group attached to the VPC.
8–9. EC2 instances
resource "aws_instance" "webserver1" {
ami = "ami-0db56f446d44f2f09"
instance_type = "t3.micro"
subnet_id = aws_subnet.sub1.id
}
resource "aws_instance" "webserver2" {
ami = "ami-0db56f446d44f2f09"
instance_type = "t3.micro"
subnet_id = aws_subnet.sub2.id
}10. Outputs
output "server1_public_ip" {
value = aws_instance.webserver1.public_ip
}
output "server2_public_ip" {
value = aws_instance.webserver2.public_ip
}Replace AMI IDs with current ones for your region (ap-south-1 in these examples). Attach the security group and a key pair if you need SSH access.
Hands-on project
Create:
1 VPC
↓
2 Public Subnets
↓
1 Internet Gateway
↓
1 Route Table (+ associations)
↓
1 Security Group
↓
2 EC2 Instances
Day 2 assignment
Create:
- 1 VPC
- 3 public subnets
- 1 internet gateway
- 1 route table
- 1 security group
- 3 EC2 instances
Display as outputs:
- VPC ID
- Subnet IDs
- Public IPs
Clean up
terraform destroyDay 2 summary
Provider configuration
↓
Multiple providers / regions
↓
Variables + terraform.tfvars
↓
Outputs
↓
Interpolation & references
↓
VPC → Subnet → IGW → Route table → SG → EC2