Terraform Infrastructure Setup
Terraform is an Infrastructure as Code tool from HashiCorp. Describes cloud resources in HCL, manages their lifecycle: creation, modification, deletion. Supports AWS, GCP, Azure, Hetzner, DigitalOcean and 1000+ providers.
Project structure
infra/
├── main.tf # main resources
├── variables.tf # input variables
├── outputs.tf # output values
├── versions.tf # provider versions
├── backend.tf # state storage
├── modules/
│ ├── app-server/
│ ├── database/
│ └── networking/
└── environments/
├── staging/
│ └── terraform.tfvars
└── production/
└── terraform.tfvars
versions.tf
terraform {
required_version = ">= 1.6"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
backend "s3" {
bucket = "myapp-terraform-state"
key = "production/terraform.tfstate"
region = "eu-west-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
Typical AWS infrastructure
# networking.tf
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = { Name = "myapp-vpc" }
}
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
}
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 10}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
}
# ECS Cluster
resource "aws_ecs_cluster" "main" {
name = "myapp-cluster"
setting {
name = "containerInsights"
value = "enabled"
}
}
# RDS PostgreSQL
resource "aws_db_instance" "main" {
identifier = "myapp-db"
engine = "postgres"
engine_version = "16.1"
instance_class = "db.t3.medium"
allocated_storage = 100
storage_type = "gp3"
storage_encrypted = true
db_name = "myapp"
username = "myapp"
password = var.db_password
vpc_security_group_ids = [aws_security_group.db.id]
db_subnet_group_name = aws_db_subnet_group.main.name
backup_retention_period = 7
skip_final_snapshot = false
final_snapshot_identifier = "myapp-final-snapshot"
performance_insights_enabled = true
tags = local.common_tags
}
# ElastiCache Redis
resource "aws_elasticache_cluster" "redis" {
cluster_id = "myapp-redis"
engine = "redis"
node_type = "cache.t3.micro"
num_cache_nodes = 1
parameter_group_name = "default.redis7"
port = 6379
subnet_group_name = aws_elasticache_subnet_group.main.name
security_group_ids = [aws_security_group.redis.id]
}
# Application Load Balancer
resource "aws_lb" "main" {
name = "myapp-alb"
internal = false
load_balancer_type = "application"
subnets = aws_subnet.public[*].id
security_groups = [aws_security_group.alb.id]
access_logs {
bucket = aws_s3_bucket.logs.bucket
enabled = true
}
}
Variables and environments
# variables.tf
variable "environment" {
description = "Environment name (staging/production)"
type = string
}
variable "db_password" {
description = "Database password"
type = string
sensitive = true
}
variable "app_instance_type" {
type = string
default = "t3.medium"
}
# environments/production/terraform.tfvars
environment = "production"
app_instance_type = "c5.xlarge"
App server module
# modules/app-server/main.tf
variable "name" {}
variable "instance_type" {}
variable "vpc_id" {}
variable "subnet_ids" { type = list(string) }
resource "aws_launch_template" "app" {
name_prefix = "${var.name}-"
image_id = data.aws_ami.ubuntu.id
instance_type = var.instance_type
# ...
}
resource "aws_autoscaling_group" "app" {
name = var.name
min_size = 2
max_size = 10
desired_capacity = 2
vpc_zone_identifier = var.subnet_ids
# ...
}
output "asg_name" {
value = aws_autoscaling_group.app.name
}
Working with Terraform
# Initialization
terraform init
# Planning
terraform plan -var-file=environments/production/terraform.tfvars
# Apply
terraform apply -var-file=environments/production/terraform.tfvars
# Only specific resource
terraform apply -target=aws_db_instance.main
# Destroy (be careful!)
terraform destroy -var-file=environments/staging/terraform.tfvars
Timeline
Basic AWS infrastructure (VPC, ECS, RDS, ALB): 5–7 days. Full infrastructure with modules, staging/prod: 10–14 days.







