← Back to Blog
TerraformInfrastructure

Infrastructure as Code: Terraform with RAW Cloud

Clicking through dashboards to provision servers does not scale. Infrastructure as Code (IaC) lets you define servers, networks, and configurations in version-controlled files — reproducible, auditable, and automated. Here is how to use Terraform with RAW Cloud to manage bare metal infrastructure programmatically.

Why Terraform for Bare Metal

Terraform treats infrastructure as declarative code. You describe the desired state, and Terraform figures out what to create, update, or destroy. For bare metal on RAW, this means:

  • Spin up 10 servers with one command
  • Version-control your entire infrastructure in Git
  • Reproduce identical environments for staging and production
  • Destroy and rebuild infrastructure in seconds
  • Integrate server provisioning into CI/CD pipelines

Step 1: Install Terraform

# macOS
brew install terraform

# Linux (Ubuntu/Debian)
wget -O - https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list
apt update && apt install terraform

# Verify
terraform version

Step 2: Configure the RAW Provider

RAW exposes a REST API for server management. Terraform talks to this API through a provider. Create a main.tf file:

terraform {
  required_providers {
    raw = {
      source  = "rawhq/raw"
      version = "~> 1.0"
    }
  }
}

provider "raw" {
  api_key = var.raw_api_key
}

variable "raw_api_key" {
  type      = string
  sensitive = true
}

Set your API key as an environment variable instead of hardcoding it:

export TF_VAR_raw_api_key="your-api-key-here"

Step 3: Define Server Resources

Create a servers.tf file to define your infrastructure:

# Production web server
resource "raw_server" "web" {
  name     = "web-prod-1"
  plan     = "bm-4c-8g"
  region   = "eu-fsn"
  image    = "ubuntu-24.04"
  ssh_keys = [var.ssh_key_id]

  tags = {
    environment = "production"
    role        = "web"
  }
}

# Database server with more resources
resource "raw_server" "db" {
  name     = "db-prod-1"
  plan     = "bm-8c-32g"
  region   = "eu-fsn"
  image    = "ubuntu-24.04"
  ssh_keys = [var.ssh_key_id]

  tags = {
    environment = "production"
    role        = "database"
  }
}

# Output the server IPs
output "web_ip" {
  value = raw_server.web.ipv4_address
}

output "db_ip" {
  value = raw_server.db.ipv4_address
}

Step 4: Plan and Apply

# Initialize the provider
terraform init

# Preview what will be created
terraform plan

# Apply the changes (creates the servers)
terraform apply

# View current state
terraform show

Terraform shows you exactly what it will create before making any changes. The plan step is your safety net — review it before every apply.

State Management

Terraform stores the current state of your infrastructure in a terraform.tfstate file. For team environments, store state remotely:

terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "raw/production.tfstate"
    region = "eu-central-1"
  }
}

Remote state enables team collaboration, state locking (prevents concurrent modifications), and versioned history of every infrastructure change. Never commit terraform.tfstate to Git — it may contain sensitive values like IP addresses and resource IDs.

Multi-Environment Setup

Use Terraform workspaces or variable files to manage staging and production from the same codebase:

# Using tfvars files per environment
# production.tfvars
server_plan   = "bm-8c-32g"
server_count  = 3
environment   = "production"

# staging.tfvars
server_plan   = "bm-4c-8g"
server_count  = 1
environment   = "staging"
# Deploy to staging
terraform apply -var-file="staging.tfvars"

# Deploy to production
terraform apply -var-file="production.tfvars"

This pattern keeps your infrastructure DRY. One set of Terraform files, multiple environments, each with appropriate sizing and cost.

Scaling with Count

Provision multiple identical servers with a single resource block:

variable "web_server_count" {
  default = 3
}

resource "raw_server" "web_cluster" {
  count    = var.web_server_count
  name     = format("web-prod-%d", count.index + 1)
  plan     = "bm-4c-8g"
  region   = "eu-fsn"
  image    = "ubuntu-24.04"
  ssh_keys = [var.ssh_key_id]
}

output "web_cluster_ips" {
  value = raw_server.web_cluster[*].ipv4_address
}

Change web_server_count from 3 to 5, run terraform apply, and two new servers appear. Scale down and Terraform destroys the extras. Predictable, repeatable, and version-controlled.

Why RAW for Terraform Workflows

Cloud providers like AWS have hundreds of resource types and complex networking. RAW keeps it simple: servers, SSH keys, and an API. Terraform configs stay clean and readable. Provisioning a server takes seconds, not minutes. And at $6/mo per server, you can afford proper staging environments without burning budget on idle cloud VMs.