Terraform solution for Day 66 that creates a VPC, public & private subnets, IGW + route table, an EC2 instance in the public subnet with Apache (user-data), an Elastic IP attached to the instance, plus outputs so you can open the website in your browser.
Important: replace every (region, bucket, key name, AMI if needed) with values for your AWS account/region. The AMI ami-0557a15b87f6559cf may be region-specific — verify it’s available in your chosen region or swap it for a region-appropriate Amazon Linux/Ubuntu AMI.
Project layout
Create a directory (e.g. day66-terraform) and these files:
day66-terraform/
├─ main.tf
├─ variables.tf
├─ outputs.tf
└─ terraform.tfvars (optional: values for variables)
1) variables.tf
variable "region" {
type = string
default = "us-east-1"
}
variable "vpc_cidr" {
type = string
default = "10.0.0.0/16"
}
variable "public_subnet_cidr" {
type = string
default = "10.0.1.0/24"
}
variable "private_subnet_cidr" {
type = string
default = "10.0.2.0/24"
}
variable "instance_ami" {
type = string
default = "ami-0557a15b87f6559cf" # <--- replace if not valid in your region
}
variable "instance_type" {
type = string
default = "t2.micro"
}
variable "key_name" {
type = string
default = "<YOUR_KEY_PAIR_NAME>" # create/download in EC2 console first
}
2) main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
# 1) VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = { Name = "day66-vpc" }
}
# 2) Public subnet
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidr
map_public_ip_on_launch = true
tags = { Name = "day66-public-subnet" }
}
# 3) Private subnet
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidr
tags = { Name = "day66-private-subnet" }
}
# 4) Internet Gateway
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = { Name = "day66-igw" }
}
# 5) Public route table
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
tags = { Name = "day66-public-rt" }
}
# 6) Associate route table with public subnet
resource "aws_route_table_association" "public_assoc" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
# 7) Security Group (allow SSH and HTTP)
resource "aws_security_group" "allow_ssh_http" {
name = "day66-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"] # NOTE: insecure for production. Prefer your IP/CIDR.
}
ingress {
description = "HTTP"
from_port = 80
to_port = 80
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"]
}
tags = { Name = "day66-sg" }
}
# 8) EC2 Instance in public subnet with user_data to install Apache
resource "aws_instance" "web" {
ami = var.instance_ami
instance_type = var.instance_type
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.allow_ssh_http.id]
key_name = var.key_name
user_data = <<-EOF
#!/bin/bash
# Basic bootstrap to install Apache and serve a simple page
if command -v yum >/dev/null 2>&1; then
# Amazon Linux / RHEL family
yum update -y
yum install -y httpd
systemctl enable httpd
systemctl start httpd
echo "<html><body><h1>Welcome to my Terraform webserver!</h1></body></html>" > /var/www/html/index.html
elif command -v apt-get >/dev/null 2>&1; then
# Debian/Ubuntu family
apt-get update -y
apt-get install -y apache2
systemctl enable apache2
systemctl start apache2
echo "<html><body><h1>Welcome to my Terraform webserver!</h1></body></html>" > /var/www/html/index.html
fi
EOF
tags = {
Name = "day66-web"
}
}
# 9) Elastic IP for the instance
resource "aws_eip" "web_eip" {
instance = aws_instance.web.id
vpc = true
depends_on = [aws_internet_gateway.igw]
tags = { Name = "day66-eip" }
}
Notes on user_data: script handles Amazon Linux (yum) and Ubuntu (apt-get). The AMI you choose should match the package manager. If you get errors, swap AMI to match your script or adjust script to the distro.
3) outputs.tf
output "vpc_id" {
value = aws_vpc.main.id
}
output "public_subnet_id" {
value = aws_subnet.public.id
}
output "private_subnet_id" {
value = aws_subnet.private.id
}
output "instance_id" {
value = aws_instance.web.id
}
output "instance_public_ip" {
value = aws_eip.web_eip.public_ip
}
output "instance_public_dns" {
value = aws_instance.web.public_dns
}
4) Optional terraform.tfvars (put real values here)
region = "us-east-1"
key_name = "my-key-pair"
instance_ami = "ami-0557a15b87f6559cf"
instance_type = "t2.micro"
5) Commands — run in your project directory
# init
terraform init
# validate files
terraform validate
# plan (inspect changes)
terraform plan -out plan.tfplan
# apply (create infra)
terraform apply "plan.tfplan"
# or run: terraform apply -auto-approve
Terraform output will show the instance_public_ip. Copy that IP and open in your browser:
http://<instance_public_ip>/
You should see: Welcome to my Terraform webserver!
6) Verify & Troubleshoot
• If webpage doesn’t load:
• Confirm terraform output instance_public_ip.
• From local, ssh -i .pem ec2-user@ (or ubuntu@ depending on AMI) to check instance logs:
sudo journalctl -u httpd -b # for Amazon Linux httpd
sudo journalctl -u apache2 -b # for Ubuntu
tail -n 100 /var/log/cloud-init-output.log
• Check Security Group allows port 80 (ingress).
• If AMI uses ubuntu user, use ssh ubuntu@<ip>.
• Ensure Elastic IP associated (terraform output shows it).
• If terraform apply fails due to AMI not found:
• Replace var.instance_ami with an AMI valid for your region (use AWS console or aws ec2 describe-images).
7) Clean up when done
To avoid charges, destroy resources:
terraform destroy -auto-approve
Note: Elastic IP will be released and instance terminated.
8) Security & best practices (short)
• Don’t keep SSH open to 0.0.0.0/0 in production — restrict to your IP (or use a bastion host). Change cidr_blocks to your IP/net.
• Use IAM roles for EC2 if instance needs AWS access; avoid embedding credentials.
• Use Terraform remote state (S3 + DynamoDB lock) for team projects.
• Tag everything for cost tracking.
Top comments (0)