Introduction
Infrastructure as Code (IaC) is one of those concepts that sounds complicated until you actually try it. Instead of clicking through cloud consoles like a robot, you write code that does the clicking for you. Today, I'm sharing my experience from Day 3 of the 30-Day Terraform Challenge, where I deployed my first web server on AWS using Terraform.
By the end of this guide, you'll have provisioned real infrastructure without touching a single console button. Well, maybe just your keyboard.
What You'll Need
- Terraform installed (v1.0+)
- An AWS account (free tier works)
- AWS access keys (keep these safe!)
- Basic terminal skills
Provider Block vs Resource Block
Before we dive in, let's clarify the two most important Terraform concepts:
Provider Block:
provider "aws" {
region = "eu-north-1"
}
The provider block tells Terraform which cloud platform to use. Think of it as introducing Terraform to your cloud provider and saying "we'll be working together."
Resource Block:
resource "aws_instance" "web_server" {
ami = "ami-0c48e6b25760f0b7d"
instance_type = "t3.micro"
}
Resource blocks define the actual infrastructure components. Each one creates something realβa server, a database, a network. The provider handles the connection; resources handle the creation.
Step-by-Step Deployment
Step 1: Set Up Your Project
mkdir terraform-day3-server
cd terraform-day3-server
Step 2: Create main.tf
Create a file called main.tf with this configuration:
# Provider configuration
provider "aws" {
region = "eu-north-1" # Stockholm region
}
# Security group - controls traffic to your server
resource "aws_security_group" "web_sg" {
name = "day3-web-sg"
description = "Allow HTTP traffic"
# Allow HTTP from anywhere
ingress {
description = "HTTP from internet"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Allow all outbound traffic
egress {
description = "All outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "day3-web-sg"
}
}
# Get the latest Amazon Linux 2 AMI
data "aws_ami" "amazon_linux_2" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
# EC2 instance with web server
resource "aws_instance" "web_server" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = "t3.micro" # Free tier eligible in eu-north-1
vpc_security_group_ids = [aws_security_group.web_sg.id]
associate_public_ip_address = true
# Script runs when instance starts
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<html>
<head>
<title>Terraform Day 3 Challenge</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; background: #f0f2f5; }
h1 { color: #333; }
.success { color: #00a86b; }
</style>
</head>
<body>
<h1>β
Day 3 Challenge Success!</h1>
<p>Your first Terraform-deployed server is running</p>
<p class='success'>Deployed on: $(date)</p>
<p>Server IP: $(curl -s http://169.254.169.254/latest/meta-data/public-ipv4)</p>
</body>
</html>" > /var/www/html/index.html
EOF
tags = {
Name = "day3-terraform-server"
}
}
# Output useful information
output "public_ip" {
value = aws_instance.web_server.public_ip
}
output "website_url" {
value = "http://${aws_instance.web_server.public_ip}"
}
Step 3: Deploy!
Run these commands in order:
# Initialize Terraform (downloads AWS provider)
terraform init
# See what will be created
terraform plan
# Create the infrastructure
terraform apply
Type yes when prompted. After a minute or two, you'll see:
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
public_ip = "13.48.12.34"
website_url = "http://13.48.12.34"
Step 4: Verify It Works
Copy the URL from the output and paste it in your browser. You should see your custom webpage with the deployment date and server IP.
Step 5: Clean Up (Seriously, Do This)
terraform destroy
Type yes when prompted. This prevents unexpected charges on your AWS bill.
Architecture Diagram
Understanding the Terraform Workflow
| Command | What It Does |
|---|---|
terraform init |
Downloads provider plugins, sets up backend |
terraform plan |
Shows what will change without actually changing anything |
terraform apply |
Creates or updates infrastructure |
terraform destroy |
Removes all resources |
Think of plan as a preview and apply as the final execution. Always review the plan before applying!
Key Takeaways
- Provider blocks configure which cloud platform to use
- Resource blocks define actual infrastructure components
- Security groups are like firewall rules for your instances
- User data automates software installation and configuration
- Always destroy resources after learning to avoid costs
- terraform plan is your safety net - use it!

Top comments (0)