How to configure Apache virtual hosts on Ubuntu using Terraform
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:
- Basics of Terraform.
- Terraform should be installed on your local system.
- AWS account configured on your local system.
- 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:
- userdata.sh: It is a bash script that will set up the EC2 server and configure the web server for virtual hosts.
- sec-grp.tf: It defines a resource for creating a security group.
- vhost-template.tf: The file containing the actual configuration to be used.
- 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 ‘<VirtualHost>’ section contains the entry for vhost1. Similarly, the second one corresponds to vhost2vhost2. You can add more vhost entries here:
$ vi vhost-template.conf
<VirtualHost *:80>
ServerAdmin [email protected]
ServerName vhost1
ServerAlias www.vhost1.com
DocumentRoot /var/www/vhost_1/public_html
ErrorLog ${APACHE_LOG_DIR}/error.log
</VirtualHost>
<VirtualHost *:80>
ServerAdmin [email protected]
ServerName vhost2
ServerAlias www.vhost2.com
DocumentRoot /var/www/vhost_2/public_html
ErrorLog ${APACHE_LOG_DIR}/error.log
</VirtualHost>
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 = "/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.
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
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.