DEV Community

Udoh Deborah
Udoh Deborah

Posted on

Day 66 : Terraform Hands-on project

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
}
Enter fullscreen mode Exit fullscreen mode

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" }
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Terraform output will show the instance_public_ip. Copy that IP and open in your browser:

http://<instance_public_ip>/
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode
• 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).
Enter fullscreen mode Exit fullscreen mode

7) Clean up when done

To avoid charges, destroy resources:

terraform destroy -auto-approve
Enter fullscreen mode Exit fullscreen mode

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)