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
Deploying with Terraform
Initialize:
Runterraform init
to download provider plugins.Validate:
Check your configuration withterraform validate
.Plan:
Preview changes usingterraform plan
.Apply:
Deploy resources withterraform apply
. Confirm by typingyes
.
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:
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
).
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.
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.