DEV Community

Cover image for Create a Web Server with OpenTofu (IaC)
Melvin Sekhon
Melvin Sekhon

Posted on

Create a Web Server with OpenTofu (IaC)

Today we're going to learn how to create an AWS web server using a tool called OpenTofu.

What is OpenTofu?

OpenTofu is an open-source infrastructure as code tool that allows users to define and manage cloud and on-premises resources using human-readable configuration files.

Step 0: Prerequisites

Before we start, there are a few things we need to do:

Install OpenTofu

Go to OpenTofu installation page and choose a method of installation for your operating system. For example, on macOS, you can use Homebrew:

   brew install opentofu
Enter fullscreen mode Exit fullscreen mode

Create an AWS Account

Go to AWS sign-up page and follow the steps to create a new account.

Generate an SSH Key Pair

Run the following command to generate a new SSH key pair (replace ~/.ssh/id_ed25519 with your desired path if needed):

   ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
Enter fullscreen mode Exit fullscreen mode

Step 1: Setting Up the Playground

First we will need to let OpenTofu know which cloud provider we require, for this guide it will be AWS.

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Run the following command to tell OpenTofu to download the AWS provider binary.

tofu init
Enter fullscreen mode Exit fullscreen mode

After running the command we need to tell OpenTofu where our playground (called a "region") is located.
We will also need to add our AWS access & secret key so that we can create and remove resources.

provider "aws" {
  region = "ap-southeast-1"

  access_key = "AKxxx"
  secret_key = "SECRET_KEY"
}
Enter fullscreen mode Exit fullscreen mode

This tells OpenTofu to set up our playground in a place called "ap-southeast-1".

Note that the docs mention multiple ways to authenticate ourselves but we will hard code the credentials in the configuration this time.

Step 2: Creating a Big Sandbox (VPC)

Next, we will create a big sandbox where we can play. This sandbox is called a Virtual Private Cloud or VPC.

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

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

This creates a VPC with the name vpc. A VPC is like our very own playground with boundaries.

Step 3: Making Small Sand Pits (Subnets)

Inside our big sandbox, we will make two smaller sand pits called subnets. One will be for public use, and the other for private use.

resource "aws_subnet" "public" {
  vpc_id     = aws_vpc.vpc.id
  cidr_block = "10.0.0.0/24"

  tags = {
    Name = "public subnet"
  }
}

resource "aws_subnet" "private" {
  vpc_id     = aws_vpc.vpc.id
  cidr_block = "10.0.1.0/24"

  tags = {
    Name = "private subnet"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now we have two subnets in our VPC.

Step 4: Adding a Gate (Internet Gateway)

To let the outside world come into our playground, we will create a gate called the Internet Gateway.

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "igw"
  }
}
Enter fullscreen mode Exit fullscreen mode

This internet gateway allows our playground to connect to the internet.

Step 5: Getting a Special Key (Elastic IP)

we will get a special key called Elastic IP (which is a fancy word for a public IP address) to use with our Internet Gateway.

resource "aws_eip" "eip" {
  domain = "vpc"
  depends_on = [aws_internet_gateway.igw]
}
Enter fullscreen mode Exit fullscreen mode

This Elastic IP will help us connect to the internet.

Step 6: Setting Up a NAT Gateway

We create something called a NAT Gateway in our public subnet. This helps our private subnet talk to the internet without showing its identity.

resource "aws_nat_gateway" "nat_gw" {
  subnet_id = aws_subnet.public.id
  allocation_id = aws_eip.eip.id

  depends_on = [aws_internet_gateway.igw]

  tags = {
    Name = "nat"
  }
}
Enter fullscreen mode Exit fullscreen mode

This NAT Gateway helps our private subnet communicate with the outside world.

Step 7: Creating Routes (Route Tables)

we will create maps (route tables) for our public and private subnets.

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "route-table-public"
  }
}
resource "aws_route" "public" {
  destination_cidr_block = "0.0.0.0/0"
  route_table_id = aws_route_table.public.id
  gateway_id = aws_internet_gateway.igw.id
}
resource "aws_route_table_association" "public" {
  route_table_id = aws_route_table.public.id
  subnet_id = aws_subnet.public.id
}


resource "aws_route_table" "private" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "route-table-private"
  }
}
resource "aws_route" "private" {
  destination_cidr_block = "0.0.0.0/0"
  route_table_id = aws_route_table.private.id
  nat_gateway_id =  aws_nat_gateway.nat_gw.id
}
resource "aws_route_table_association" "private" {
  route_table_id = aws_route_table.private.id
  subnet_id = aws_subnet.private.id
}
Enter fullscreen mode Exit fullscreen mode

These maps show our subnets how to reach different places.

Step 8: Making Rules for Entry (Security Group)

we will create rules that say who can come into our playground. These rules are called a Security Group.

resource "aws_security_group" "sg_web" {
  name        = "web_security"
  description = "Allow HTTP, HTTPS & SSH"
  vpc_id      = aws_vpc.vpc.id

  tags = {
    Name = "web-security"
  }
}

resource "aws_vpc_security_group_ingress_rule" "allow_http" {
  security_group_id = aws_security_group.sg_web.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "tcp"
  from_port         = 80
  to_port           = 80

}
resource "aws_vpc_security_group_ingress_rule" "allow_https" {
  security_group_id = aws_security_group.sg_web.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "tcp"
  from_port         = 443
  to_port           = 443
}
resource "aws_vpc_security_group_ingress_rule" "allow_ssh" {
  security_group_id = aws_security_group.sg_web.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "tcp"
  from_port         = 22
  to_port           = 22
}
resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv4" {
  security_group_id = aws_security_group.sg_web.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "-1"
}
Enter fullscreen mode Exit fullscreen mode

This security group allows anyone to visit our web server and let us manage it from anywhere.

Step 9: Adding SSH Key Pair to AWS

Using the previously created SSH key pair, we will upload the public key to AWS. This will let us access our web server.

resource "aws_key_pair" "key_pair" {
  key_name   = "my OpenTofu key"
  public_key = file("~/.ssh/id_ed25519.pub")
}
Enter fullscreen mode Exit fullscreen mode

Step 10: Create the Web Server (EC2 Instance)

Finally, we will create our web server, which is an EC2 instance.
Additionally we will use 30GB of EBS storage and run some commands to install a web server.

resource "aws_instance" "webserver" {
  ami = "ami-0acbb557db23991cc"
  instance_type = "t2.micro"

  subnet_id = aws_subnet.public.id
  associate_public_ip_address = true
  key_name = aws_key_pair.key_pair.key_name

  vpc_security_group_ids     = [aws_security_group.sg_web.id]

  depends_on = [aws_internet_gateway.igw]

  tags = {
    Name = "webserver"
  }

  root_block_device {
    volume_size = 30
    volume_type = "gp3"
    delete_on_termination = true
  }

  user_data = <<-EOF
              #!/bin/bash
              sudo apt-get update
              sudo apt-get install -y lighttpd
              #echo "hi world" > /var/www/html/index.html
              EOF
}
Enter fullscreen mode Exit fullscreen mode

This EC2 instance will have:

  • An AMI (Debian template) for creating the instance.
  • A type t2.micro (small but sufficient for learning).
  • A public subnet to connect to the internet.
  • A security group to allow access to HTTP,HTTPS & SSH.
  • A SSH key pair for secure access.
  • 30GB EBS storage space.
  • A script that installs Lighttpd when the instance starts.

Apply configuration to AWS

To apply the configuration to AWS, run the following commands.

tofu validate
tofu apply
Enter fullscreen mode Exit fullscreen mode

tofu validate validates the syntax
tofu apply will apply the config to AWS

Display public IP address

Adding the following block will display our web server public IP address after the configuration has been applied

output "webserver_ip" {
  value = aws_instance.webserver.public_ip
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Congratulations! You've just set up a web server using OpenTofu.
With these steps, you now have a functional playground (VPC) with a sand pit (subnet), a gate (internet gateway), maps (route tables), rules (security group), a special key (SSH key pair), and your very own web server (EC2 instance) ready to greet the world.

Further readings

Top comments (0)