For hosting multiple websites, administrators commonly rely on ‘Virtual Hosting’ technique. Virtual hosting hosts multiple websites using a single machine. It can be achieved either by an ‘IP-based’ method or a ‘name-based’ approach. In ‘IP-based’ hosting we have distinct IP addresses for each web site. In case of ‘name-based’ hosting we have multiple names running on each IP address.

There are many tools available today for automating cloud-based infrastructure. Terraform is one such tool that has recently gained enormous popularity in the DevOps world. Terraform is an Open source tool developed and maintained by HashiCorp. It uses its own Hashicorp Configuration Language- HCL to provision multiple cloud service providers. Basically, Terraform will compare your current infrastructure configuration to the desired state and only modify those parts of the infrastructure that are required to reach the desired state.

What will we cover?

In this tutorial, we will see how we can host two virtual hosts on an Ubuntu 22.04 system using Terraform. We will use the Apache web server to perform this lab. 

Pre-Flight Check

Before proceeding further, check the requirements for performing this guide:

  1. Basics of Terraform.
  2. Terraform should be installed on your local system.
  3. AWS account configured on your local system.
  4. Basics of setting up a Virtual host using Apache web server.

Setting Up the Lab

On our Ubuntu 22.04 server, we will proceed by installing Apache web server, then we will configure virtual hosts with each having a different index.html file:  “Webpage from: Virtual Host 1.” for vhost1 and  “Webpage from: Virtual Host 2.” for vhost2.

Also to keep things simple, we have mapped the domain name for the two vhosts(virtual hosts) to the local host IP address(127.0.0.1). This lab uses multiple files to make a clear working environment. The description of the files is as:

  1. userdata.sh: It is a bash script that will set up the EC2 server and configure the web server for virtual hosts.
  2. sec-grp.tf: It defines a resource for creating a security group.
  3. vhost-template.tf: The file containing the actual configuration to be used.
  4. main.tf: Here the web server resource and rest of the infrastructure is declared.

It is also possible to merge multiple files but that will cause more complication in reviewing the code. Let us now proceed to the main steps:

Step 1. Let’s start with creating a directory where we will place all the project files:

$ mkdir virtual-hosts-terraform

Step 2. To set up the EC2 instance for virtual hosting, we will use an userdata script. This will also automate our common server configuration:

$ vi userdata.sh
#!/bin/bash

sudo apt-get update

sudo apt-get upgrade -y

sudo apt-get install apache2 -y

sudo systemctl restart apache2

sudo sh -c "echo 127.0.0.1 www.vhost1.com >> /etc/hosts"

sudo sh -c "echo 127.0.0.1 www.vhost2.com >> /etc/hosts"

sudo mkdir -p /var/www/vhost_1/public_html

sudo mkdir -p /var/www/vhost_2/public_html

sudo chown -R $USER:$USER /var/www/vhost_1/public_html

sudo chown -R $USER:$USER /var/www/vhost_2/public_html

sudo chmod -R 755 /var/www

sudo echo "Webpage from: Virtual Host 1." > /var/www/vhost_1/public_html/index.html

sudo echo "Webpage from: Virtual Host 2." > /var/www/vhost_2/public_html/index.html

sudo cp /home/ubuntu/vhosts.conf /etc/apache2/sites-available/vhosts.conf

sudo a2ensite vhosts.conf

sudo a2dissite 000-default.conf

sudo systemctl restart apache2

Step 3. Next, we will configure a security group resource to set the incoming and outgoing traffic rules. Permit the SSH and HTTP incoming traffic from all side and egress(outgoing) to everywhere:

$ vi sec-grp.tf
resource "aws_security_group" "ec2-sg" {

  name = "ec2-grp"

  description = "Set Ingress and Egress Rules "

  ingress {

    from_port   = 80

    to_port     = 80

    protocol    = "tcp"

    cidr_blocks = ["0.0.0.0/0"]

  }

  ingress {

    from_port   = 22

    to_port     = 22

    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"]

  }

}

Step 4. This file contains the actual configuration for each virtual host. The first ‘’ section contains the entry for vhost1. Similarly, the second one corresponds to vhost2vhost2. You can add more vhost entries here:

$ vi vhost-template.conf


    ServerAdmin [email protected]

    ServerName vhost1

    ServerAlias www.vhost1.com

    DocumentRoot /var/www/vhost_1/public_html

    ErrorLog ${APACHE_LOG_DIR}/error.log

    ServerAdmin [email protected]

    ServerName vhost2

    ServerAlias www.vhost2.com

    DocumentRoot /var/www/vhost_2/public_html

    ErrorLog ${APACHE_LOG_DIR}/error.log

Step 5. In the main.tf file, declare a web server resource and set the rest of the infrastructure:

$ vi main.tf
provider "aws" {

  region ="us-east-1"

}

resource "aws_instance" "webserver" {

  ami ="ami-09d56f8956ab235b3"

  instance_type = "t2.micro"

  key_name = "Name-of-EC2-Key-Pair"

  vpc_security_group_ids = [aws_security_group.demo-sg.id]

  associate_public_ip_address = true

     provisioner "file" {

        source      = "vhost-template.conf"

        destination = "https://www.howtoforge.com/home/ubuntu/vhosts.conf"

        connection {

      type        = "ssh"

      user        = "ubuntu"

      private_key = "${file("/Path/to/EC2-Key-Pair")}"

      host        = "${self.public_dns}"

    }

      }

user_data = "${file("userdata.sh")}"

  tags = {

    Name = "VirtualHostTutorial"

  }

}

output "IPAddress" {

  value = "${aws_instance.webserver.public_dns}"

}

The file provisioner is used to upload the ‘vhost-template.conf’ file to the EC2 instance. The output block prints the public dns name of the instance. Similarly, the ‘file’ command executes the userdata script. 

<img alt="File Structure" data-ezsrc="https://kirelos.com/wp-content/uploads/2022/07/echo/image1.png62d905789a22d.jpg" ezimgfmt="rs rscb5 src ng ngcb5" height="173" loading="lazy" src="data:image/svg xml,” width=”750″>

Step 6. Now initialize the project directory with the ‘init’ command followed by ‘apply’ command:

$ terraform init
$ terraform apply

Testing the Setup

Now SSH to your instance and run the command:

$ curl www.vhost1.com

The above command should return the message from virtual host1 index page, similarly the below command should display the message from the virtual host2:

$ curl www.vhost2.com

<img alt="Testing the setup" data-ezsrc="https://kirelos.com/wp-content/uploads/2022/07/echo/image2.png62d90578aed2c.jpg" ezimgfmt="rs rscb5 src ng ngcb5" height="166" loading="lazy" src="data:image/svg xml,” width=”750″>

Conclusion

We have finally made it out, our virtual hosting is working as expected. We can also perform this tutorial using Terraform, just try it out.