DEV Community

Cover image for Getting Started with AWS and Terraform: Deploying a Linux Apache Web Server on AWS EC2 Instance using Terraform
Chinmay Tonape
Chinmay Tonape

Posted on • Edited on

Getting Started with AWS and Terraform: Deploying a Linux Apache Web Server on AWS EC2 Instance using Terraform

In our earlier post, we walked through the process of setting up a beginner's environment on AWS using Terraform. Now, let's dive into the basic services of AWS cloud - network and compute.
In this post, I will guide you through the steps of launching a simple website on an EC2 Linux instance using Terraform. It will use single AZ, single EC2 server without auto-scaling.

We'll break down the process into manageable components by creating Terraform modules for network and web server.

Architecture Overview:

Before we get started, let's take a quick look at the architecture we'll be working with:
Architecture Diagram

Step 1: Creating the VPC and Network Components

The first step is to set up the foundational elements. We'll create a Virtual Private Cloud (VPC) with an Internet Gateway, a public subnet, and a route table. Additionally, we'll set up a security group to manage access to the compute resources.

Variables used throughout the modules are mentioned in variables.tf file.
variables.tf

variable "aws_region" {
  type        = string
  description = "AWS region to use for resources."
  default     = "us-east-1"
}

variable "aws_azs" {
  type        = string
  description = "AWS Availability Zones"
  default     = "us-east-1a"
}

variable "enable_dns_hostnames" {
  type        = bool
  description = "Enable DNS hostnames in VPC"
  default     = true
}

variable "vpc_cidr_block" {
  type        = string
  description = "Base CIDR Block for VPC"
  default     = "10.0.0.0/16"
}

variable "vpc_public_subnets_cidr_block" {
  type        = string
  description = "CIDR Block for Public Subnets in VPC"
  default     = "10.0.0.0/24"
}

variable "instance_type" {
  type        = string
  description = "Type for EC2 Instance"
  default     = "t2.micro"
}

variable "instance_key" {
  default = "MyKeyPair"
}
Enter fullscreen mode Exit fullscreen mode

main.tf in VPC module
Create VPC

# Create the VPC
resource "aws_vpc" "app_vpc" {
  cidr_block           = var.vpc_cidr_block
  enable_dns_hostnames = var.enable_dns_hostnames
}
Enter fullscreen mode Exit fullscreen mode

Create internet gateway

# Create the internet gateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.app_vpc.id
}
Enter fullscreen mode Exit fullscreen mode

Create public subnet, route table and association

# Create the public subnet
resource "aws_subnet" "public_subnet" {
  vpc_id                  = aws_vpc.app_vpc.id
  cidr_block              = var.vpc_public_subnets_cidr_block
  map_public_ip_on_launch = true
  availability_zone       = var.aws_azs
}

# Create the route table
resource "aws_route_table" "public_rt" {
  vpc_id = aws_vpc.app_vpc.id

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

# Assign the public route table to the public subnet
resource "aws_route_table_association" "public_rt_asso" {
  subnet_id      = aws_subnet.public_subnet.id
  route_table_id = aws_route_table.public_rt.id
}
Enter fullscreen mode Exit fullscreen mode

Create security group which allows inbound SSH and HTTP traffic and all outbound traffic. (SSH just to connect and check EC2!)

# Create the security group
resource "aws_security_group" "sg" {
  name        = "allow_ssh_http"
  description = "Allow ssh http inbound traffic"
  vpc_id      = aws_vpc.app_vpc.id

  ingress {
    description      = "SSH from VPC"
    from_port        = 22
    to_port          = 22
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  ingress {
    description      = "HTTP from VPC"
    from_port        = 80
    to_port          = 80
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Launching an EC2 Instance with Apache Web Service

With the network components in place, we'll proceed to launch an EC2 instance. We'll use the Amazon Linux 2 AMI and configure it to run an Apache web service. We will use userdata to install apache and create an index.html which displays instance metadata.

main.tf of WEB module

# Get latest Amazon Linux 2 AMI
data "aws_ami" "amazon-linux-2" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm*"]
  }
}

# Create the Linux EC2 Web server
resource "aws_instance" "web" {
  ami             = data.aws_ami.amazon-linux-2.id
  instance_type   = var.instance_type
  key_name        = var.instance_key
  subnet_id       = var.subnet_id
  security_groups = var.security_groups


  user_data = <<-EOF
  #!/bin/bash
  yum update -y
  yum install -y httpd.x86_64
  systemctl start httpd.service
  systemctl enable httpd.service
  instanceId=$(curl http://169.254.169.254/latest/meta-data/instance-id)
  instanceAZ=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone)
  pubHostName=$(curl http://169.254.169.254/latest/meta-data/public-hostname)
  pubIPv4=$(curl http://169.254.169.254/latest/meta-data/public-ipv4)
  privHostName=$(curl http://169.254.169.254/latest/meta-data/local-hostname)
  privIPv4=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)

  echo "<font face = "Verdana" size = "5">"                               > /var/www/html/index.html
  echo "<center><h1>AWS Linux VM Deployed with Terraform</h1></center>"   >> /var/www/html/index.html
  echo "<center> <b>EC2 Instance Metadata</b> </center>"                  >> /var/www/html/index.html
  echo "<center> <b>Instance ID:</b> $instanceId </center>"                      >> /var/www/html/index.html
  echo "<center> <b>AWS Availablity Zone:</b> $instanceAZ </center>"             >> /var/www/html/index.html
  echo "<center> <b>Public Hostname:</b> $pubHostName </center>"                 >> /var/www/html/index.html
  echo "<center> <b>Public IPv4:</b> $pubIPv4 </center>"                         >> /var/www/html/index.html
  echo "<center> <b>Private Hostname:</b> $privHostName </center>"               >> /var/www/html/index.html
  echo "<center> <b>Private IPv4:</b> $privIPv4 </center>"                       >> /var/www/html/index.html
  echo "</font>"                                                          >> /var/www/html/index.html
EOF
}
Enter fullscreen mode Exit fullscreen mode

Steps to run Terraform

Now we will see how to run the above created terraform infrastructure automation

terraform init
terraform plan
terraform apply -auto-approve
Enter fullscreen mode Exit fullscreen mode

Once the terrform apply completed successfully it will show the public ipaddress of the apache server as output

Apply complete! Resources: 7 added, 0 changed, 0 destroyed.

Outputs:

instance_id = "i-0d32ce14df3987030"
public_ip = "http://54.165.140.50/"
Enter fullscreen mode Exit fullscreen mode

Running Website

And there you have it – a basic website running on an EC2 Linux instance in AWS, all orchestrated using Terraform.

Running Website

EC2 Console

Cleanup

Remember to stop AWS components to avoid large bills.

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

In our next module, we'll take it a step further and explore launching the same website on a Windows Server. Happy coding!

Resources

GitHub Link: https://github.com/chinmayto/terraform-aws-linux-webserver
EC2 Documentation: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more