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

Today we’re taking Terraform from concept to action. It’s time to roll up your sleeves, write some configuration files, and watch as your first server comes to life in the cloud.

If you’ve already installed Terraform and configured your environment, let’s dive into the magical world of Infrastructure as Code (IaC) and see how Terraform simplifies cloud resource management.

The Core Concepts

Provider Block

Before Terraform can create resources, it needs to know which cloud provider to use and how to structure those resources.

A Terraform provider links you to the platform that houses your infrastructure, whether it's AWS, Azure, GCP, or another one. Consider it the link that connects Terraform to the cloud service.

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

Here, we declare AWS as our provider and specify the region (us-east-1). This ensures that Terraform understands where to deploy resources. You can adjust the region based on your preferences or vicinity.

Variables block

Variables make your setup reusable, adaptable, and easier to maintain. Consider deploying the same setup in many settings (e.g., development, testing, and production); variables make this easier.
Here's what we're defining.

variable "cidr" {
  description = "CIDR block for the VPC"
  default     = "10.0.0.0/16"
}

variable "tag" {
  description = "A common tag prefix for resources"
  default     = "webserver"
}

variable "inbound_ports" {
  description = "List of inbound ports for security group rules"
  type        = list(string)
  default     = ["80"]
}
  • The cidr variable defines the private IP range for the VPC, ensuring no overlap with other networks.

  • The tag variable helps name resources for better identification.

  • The inbound_ports variable lists open ports, making it easy to modify rules in one place.

Resource Block

A resource is the precise piece of infrastructure that you wish to build, such as a virtual machine, database, or network. In Terraform, resources are the foundation of your infrastructure.

resource "aws_vpc" "vpc" {
  cidr_block = var.cidr
  instance_tenancy = "default"
  tags = {
    Name = "${var.tag}-vpc"
  }
}

Building the Network: VPC and Subnets

main.tf: Creating a Virtual Private Cloud (VPC)

resource "aws_vpc" "vpc" {
  cidr_block = var.cidr
  tags = {
    Name = "${var.tag}-vpc"
  }
}

The VPC functions as a container for all of our resources. It provides an isolated environment in which we may configure subnets, gateways, and other components.

Creating Subnets

resource "aws_subnet" "public_subnets" {
  count                   = var.public_subnets_no
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = cidrsubnet(var.cidr, 8, count.index + 100)
  map_public_ip_on_launch = true
  tags = {
    Name = "${var.tag}-public-subnet-${count.index}"
  }
}

We create public subnets for resources that need internet access, like our web server.

  • Public subnets host resources accessible from the internet (e.g., a web server).

  • Private subnets host internal resources (e.g., databases) that should not be publicly exposed.

Adding Internet Connectivity

resource "aws_internet_gateway" "internet_gateway" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${var.tag}-igw"
  }
}

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

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

The internet gateway connects public resources to the internet. A route table manages traffic to and from the internet.

Securing the Network: Security Groups

resource "aws_security_group" "web_sg" {
  name        = "web_sg"
  vpc_id      = aws_vpc.vpc.id
  description = "Allow HTTP traffic"
}

resource "aws_vpc_security_group_ingress_rule" "allow_http" {
  for_each          = { for idx, port in var.inbound_ports : idx => port } 
  security_group_id = aws_security_group.web_sg.id
  cidr_ipv4         = "0.0.0.0/0"
  from_port         = each.value
  to_port           = each.value
  ip_protocol       = "tcp"
}

resource "aws_vpc_security_group_egress_rule" "app_allow_all_outbound" {
  security_group_id = aws_security_group.web_sg.id
  cidr_ipv4         = "0.0.0.0/0"  
  ip_protocol       = "-1"         
}

Security groups act like firewalls, defining what traffic can enter or leave a resource. Here, we allow HTTP traffic (port 80).

Deploying the Web Server

Find the Right AMI

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "ubuntu/images/hvm-ssd/ubuntu-*"
    values = ["*20.04-amd64-server-*"]
  }
  owners = ["099720109477"]
}

We use an Amazon Machine Image (AMI) for our server. This image includes the operating system and preconfigured settings.

Provisioning the Server

resource "aws_instance" "web_server" {
  instance_type          = "t2.micro"
  ami                    = data.aws_ami.ubuntu.id
  vpc_security_group_ids = [aws_security_group.web_sg.id]
  subnet_id              = aws_subnet.public_subnets[0].id

  user_data = <<-EOT
    #!/bin/bash
    sudo apt update -y
    sudo apt install -y nginx
    echo "<h1>Hello from Terraform</h1>" | sudo tee /var/www/html/index.html
  EOT
}

This block sets up an EC2 instance running Ubuntu. It installs Nginx and creates a web page with the words "Hello from Terraform."

Visualizing the Architecture

Architecture Diagram

Deploying with Terraform

  1. Initialize:
    Run terraform init to download provider plugins.

  2. Validate:
    Check your configuration with terraform validate.

  3. Plan:
    Preview changes using terraform plan.

  4. Apply:
    Deploy resources with terraform apply. Confirm by typing yes.

Verify Your Deployment

After running terraform apply, you’ll see the outputs in your terminal, including details about the deployed server. To verify that your server is live:

  1. Log in to AWS Management Console

    • Navigate to the EC2 Dashboard under the AWS console. (same region as declared in your terraform script)

    • Locate the instance you deployed. It should have a name or tag matching your tag variable (e.g., webserver).

  2. Copy the Public IP Address

    • Select your instance, and in the details panel, find the public IPv4 address.

    • Copy this IP address for the next step.

  3. Access Your Server

    • Open your web browser and paste the public IP address into the address bar (e.g., http://<your-public-ip>).

    • If everything was configured correctly, you should see the Nginx default page or the custom message:

Troubleshooting Tips

  • If the page doesn’t load, ensure your security group allows HTTP traffic on port 80.

  • Verify the EC2 instance is running and has a public IP assigned.