DEV Community

Cover image for Deploying Your First Server with Terraform: A Beginner's Guide
Victor Robin
Victor Robin

Posted on

Deploying Your First Server with Terraform: A Beginner's Guide

Deploying Your First Server with Terraform: A Beginner's Guide

This is a simple walkthrough of how I deployed a basic web server on AWS using Terraform. The goal is not to overcomplicate things but to help you understand how the pieces fit together and get something running end to end.


Overview

In this project, we are:

  • Creating a VPC
  • Creating a public subnet
  • Attaching an Internet Gateway
  • Configuring a route table for internet access
  • Setting up a security group (ports 22, 80, 443)
  • Launching an EC2 instance
  • Installing Apache using user data
  • Accessing the server via public IP

At the end, you should be able to open your browser and see a simple HTML page served from your EC2 instance.


Prerequisites

Before you start, make sure you have:

  • AWS CLI installed and configured (aws configure)
  • Terraform installed (terraform version)
  • VS Code installed
  • Extensions:
    • HashiCorp Terraform
    • AWS Toolkit

Also ensure:

  • You are using an IAM user, not root
  • Your IAM user has enough permissions to create VPC, EC2, etc.

Step by Step Guide

1. Initialize your project

Create a folder and add a main.tf file. Paste your Terraform code into it.

You can as well just clone this repo, butit's advieable to always write it out manually, especially for beginners; to build muscle memory.

git clone https://github.com/Vivixell/Terraform-Aws-Webserver.git

Enter fullscreen mode Exit fullscreen mode
cd Terraform-Aws-Webserver
Enter fullscreen mode Exit fullscreen mode

2. Initialize Terraform

terraform init
Enter fullscreen mode Exit fullscreen mode

This downloads the AWS provider and prepares your working directory.

3. Review the execution plan

terraform plan
Enter fullscreen mode Exit fullscreen mode

This shows what Terraform is about to create. Nothing is deployed yet.

4. Apply the configuration

terraform apply
Enter fullscreen mode Exit fullscreen mode

Type yes when prompted.

Terraform will now create all the resources defined in your code.

Terraform Code Breakdown

Terraform and Provider Block


terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.9"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

Enter fullscreen mode Exit fullscreen mode

This defines:

  • Terraform version

  • AWS provider

  • Region to deploy resources

Terraform will use your AWS CLI credentials automatically.

VPC

resource "aws_vpc" "my_vpc" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "my-vpc"
  }
}
Enter fullscreen mode Exit fullscreen mode

Creates a private network where all resources will live.

Subnet

resource "aws_subnet" "my_public_subnet" {
  vpc_id            = aws_vpc.my_vpc.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-east-1a"

  tags = {
    Name = "my-public-subnet"
  }
}
Enter fullscreen mode Exit fullscreen mode

This subnet will host our EC2 instance.

Security Group

resource "aws_security_group" "my_web_sg" {
  name   = "web-sg"
  vpc_id = aws_vpc.my_vpc.id

  dynamic "ingress" {
    for_each = [80, 443, 22]
    content {
      from_port   = ingress.value
      to_port     = ingress.value
      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"]
  }
}
Enter fullscreen mode Exit fullscreen mode

This allows:

  • SSH (22)

  • HTTP (80)

  • HTTPS (443)

The dynamic block is just a cleaner way to avoid repeating the same config three times. I stated the other way you can write it without using this method in the code.

Internet Gateway


resource "aws_internet_gateway" "my_igw" {
  vpc_id = aws_vpc.my_vpc.id

  tags = {
    Name = "my-igw"
  }
}

Enter fullscreen mode Exit fullscreen mode

This enables internet access for your VPC.

Route Table

resource "aws_route_table" "my_public_rt" {
  vpc_id = aws_vpc.my_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.my_igw.id
  }

  tags = {
    Name = "my-public-rt"
  }
}
Enter fullscreen mode Exit fullscreen mode

This routes internet traffic through the Internet Gateway.

Route Table Association

resource "aws_route_table_association" "my_public_rt_assoc" {
  subnet_id      = aws_subnet.my_public_subnet.id
  route_table_id = aws_route_table.my_public_rt.id
}
Enter fullscreen mode Exit fullscreen mode

Connects the subnet to the route table.

Key Pair

resource "aws_key_pair" "my_key_pair" {
  key_name   = "my-key-pair"
  public_key = "your-public-key"
}
Enter fullscreen mode Exit fullscreen mode

Used to SSH into the instance.

EC2 Instance

resource "aws_instance" "my_web_server" {
  ami                    = "ami-0ec10929233384c7f"
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.my_public_subnet.id
  vpc_security_group_ids = [aws_security_group.my_web_sg.id]

  associate_public_ip_address = true
  key_name                    = aws_key_pair.my_key_pair.key_name

  user_data_replace_on_change = true

  user_data = <<-EOF
              #!/bin/bash
              apt update -y
              apt install -y apache2
              systemctl start apache2
              systemctl enable apache2
              echo "<h1>Hello from Terraform Day3 By OVR </h1>" > /var/www/html/index.html
              EOF

  tags = {
    Name = "my-web-server"
  }
}
Enter fullscreen mode Exit fullscreen mode

This:

  • Launches the instance

  • Installs Apache

  • Serves a simple HTML page

Output

output "Public_IP" {
  value = aws_instance.my_web_server.public_ip
}
Enter fullscreen mode Exit fullscreen mode

Prints the public IP after deployment.

Accessing Your Server

After terraform apply, copy the public IP and open:

http://<your-public-ip>
Enter fullscreen mode Exit fullscreen mode

Do not use HTTPS since SSL is not configured.

Challenges I Faced

1. Instance type not eligible for free tier

Error:

InvalidParameterCombination: The specified instance type is not eligible for Free Tier

Fix:

Switched from t2.micro to t3.micro (or any free tier eligible type for you, it won't be a problem if you're not in free tier mode)

Tip:

Always confirm supported instance types in your region.

2. Could not access server from browser

Issue:

curl worked but browser didn’t

Fix:

Used http:// instead of https://

3. IAM permission issues (common)

If your IAM user lacks permissions, Terraform will fail.

Fix:

Attach appropriate policies like:

  • EC2FullAccess

  • VPCFullAccess (or scoped policies)

Possible Improvements

This setup works, but it can be better.

  • Use variables instead of hardcoding values

  • Split resources into modules

  • Use remote state (S3 + DynamoDB)

  • Use latest AMI lookup instead of hardcoding

  • Add proper domain and HTTPS

Final Thoughts

This is a basic but complete Terraform workflow:

  • To verify setup run
terraform init
terraform plan
terraform apply
Enter fullscreen mode Exit fullscreen mode

Don't forget to terraform destroy to clean up the infra.

Top comments (0)