Step-by-step tutorial for beginners

Prerequisites

  1. Terraform Installed: Download from terraform.io and ensure terraform version works.
  2. Faxter Terraform Provider: Download the binary. Place it in your local plugins directory, for example:
mkdir -p ~/.terraform.d/plugins/local/faxter/faxter/0.1/darwin_amd64
mv terraform-provider-faxter ~/.terraform.d/plugins/local/faxter/faxter/0.1/darwin_amd64/

(Adjust OS/architecture accordingly.)

  1. Faxter Token: You’ll need a valid token from Faxter to authenticate.
  2. Basic Knowledge of Terraform: This tutorial will be easier if you already know Terraform basics (providers, resources, terraform init/plan/apply).

1. Configuring the Provider

In a new folder, create a file named main.tf:

terraform {
  required_providers {
    faxter = {
      source  = "local/faxter/faxter"
      version = "0.1"
    }
  }
}

provider "faxter" {
  token = "YOUR_FAXTER_TOKEN"
}

Run:

terraform init

2. Getting Started with the Default Project

The Default Project is a pre-configured environment with minimal overhead. It only supports: - Servers: Virtual machines - SSH Keys: For secure access to servers

This is perfect for quick tests or basic setups without custom networks and routers.

Example

# Create an SSH key
resource "faxter_ssh_key" "default_proj_key" {
  project    = "default"             # The default project
  name       = "my-quick-ssh-key"
  public_key = "ssh-rsa AAAAB3NzaC1yc2E..."
}

# Create a server in the default project
resource "faxter_server" "default_proj_server" {
  project  = "default"
  name     = "test-server"
  key_name = faxter_ssh_key.default_proj_key.name
  flavor   = "copper"
  image    = "Ubuntu2204"
}

Then

terraform plan
terraform apply

3. Creating a Full Project

To manage resources beyond a simple server (like networks, routers, volumes, etc.), you’ll want your own project. Let’s create one:

resource "faxter_project" "example_project" {
  name = "my-cool-project"
}

This gives you an isolated environment for more advanced use cases.

4. Managing SSH Keys

SSH keys are used to securely connect to servers:

resource "faxter_ssh_key" "my_key" {
  project    = faxter_project.example_project.name
  name       = "my-ssh-key"
  public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ..."
}

Now you can reference faxter_ssh_key.my_key.name whenever you create a server in this project.

5. Creating Networks and Routers

Networks are private networks, each containing subnets. Routers connect these networks to an external gateway or other networks, enabling internet access or cross-network communication.

Create a Network

resource "faxter_network" "private_net" {
  project = faxter_project.example_project.name
  name    = "my-private-network"

  subnets {
    name = "subnet1"
    cidr = "192.168.10.0/24"
  }
  subnets {
    name = "subnet2"
    cidr = "192.168.11.0/24"
  }
}

This network has two subnets (subnet1 and subnet2), each with their own CIDR.

Create a Router

resource "faxter_router" "my_router" {
  project          = faxter_project.example_project.name
  name             = "my-router"
  connect_external = true
  subnets          = [
    # Attach one or more subnets from your network
    # Typically referencing their name
    faxter_network.private_net.subnets[0].name,
    faxter_network.private_net.subnets[1].name
  ]
}

The router is now connected to my-private-network’s subnets and can route traffic to the internet (because connect_external = true).

6. Using Security Groups

Security Groups act like firewalls for your servers. You create rules to allow or block certain protocols/ports.

resource "faxter_security_group" "web_sg" {
  project = faxter_project.example_project.name
  name    = "web-sg"

  rules {
    protocol         = "tcp"
    port_range_min   = 80
    port_range_max   = 80
    direction        = "ingress"
    remote_ip_prefix = "0.0.0.0/0"  # Allow HTTP from anywhere
  }

  rules {
    protocol         = "tcp"
    port_range_min   = 443
    port_range_max   = 443
    direction        = "ingress"
    remote_ip_prefix = "0.0.0.0/0"  # Allow HTTPS from anywhere
  }
}

Example: A second group for MySQL:

resource "faxter_security_group" "db_sg" {
  project = faxter_project.example_project.name
  name    = "db-sg"

  rules {
    protocol         = "tcp"
    port_range_min   = 3306
    port_range_max   = 3306
    direction        = "ingress"
    remote_group_id  = faxter_security_group.web_sg.name
  }
}

This only allows inbound MySQL from servers in the web_sg group.

7. Creating Servers

Servers are virtual machines. You specify the project, name, flavor (size), image, key, networks, volumes, security groups, and optional cloud-init for boot configuration.

resource "faxter_server" "web_server" {
  project   = faxter_project.example_project.name
  name      = "web1"
  key_name  = faxter_ssh_key.my_key.name
  flavor    = "copper"
  image     = "Ubuntu2204"
  networks  = [faxter_network.private_net.name]  # attach to your private network
  security_groups = [faxter_security_group.web_sg.name]

  # Optionally pass a cloud-init file
  cloud_init = file("${path.module}/web_cloud_init.yaml")
}

Attaching Volumes

resource "faxter_volume" "db_volume" {
  project = faxter_project.example_project.name
  name    = "db-data"
  storage = 100  # in GB
}

resource "faxter_server" "db_server" {
  project   = faxter_project.example_project.name
  name      = "db1"
  key_name  = faxter_ssh_key.my_key.name
  flavor    = "copper"
  image     = "Ubuntu2204"
  networks  = [faxter_network.private_net.name]
  security_groups = [faxter_security_group.db_sg.name]

  # Attach volumes
  volumes = [faxter_volume.db_volume.name]

  # Optional cloud-init
  cloud_init = file("${path.module}/db_cloud_init.yaml")
}

Result: Two servers—web_server and db_server—communicating on a private network, protected by security groups.

8. Managing Load Balancers

Load Balancers distribute traffic across multiple backend servers.

resource "faxter_loadbalancer" "lb_example" {
  project           = faxter_project.example_project.name
  name              = "frontend-lb"
  port              = 80
  networks          = ["public1"]  # attach to public network for outside traffic
  key_name          = "my-ssh-key" # optional if LB is VM-based
  request_floating_ip = true
  ssl_enabled       = false
  security_groups   = ["default", faxter_security_group.web_sg.name]

  # servers could be IP:Port combos or references, depending on your environment
  servers = [
    "192.168.10.10:8080",
    "192.168.10.11:8080"
  ]
}

Now traffic hitting frontend-lb on port 80 is distributed across the two servers listening on 8080.

9. Putting It All Together: A Simple Two-Tier Example

Goal: Build a web server in a public network, a DB server in a private network, with a router for internet access, security groups for web traffic, and a volume for persistent DB storage.

# main.tf

terraform {
  required_providers {
    faxter = {
      source  = "local/faxter/faxter"
      version = "0.1"
    }
  }
}

provider "faxter" {
  token = "YOUR_TOKEN_HERE"
}

# 1. Project
resource "faxter_project" "my_app" {
  name = "my-app-project"
}

# 2. SSH Key
resource "faxter_ssh_key" "mykey" {
  project    = faxter_project.my_app.name
  name       = "my-app-key"
  public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ..."
}

# 3. Networks + Router
resource "faxter_network" "public_net" {
  project = faxter_project.my_app.name
  name    = "public-network"

  subnets {
    name = "pub-subnet1"
    cidr = "10.0.1.0/24"
  }
}

resource "faxter_network" "private_net" {
  project = faxter_project.my_app.name
  name    = "private-network"

  subnets {
    name = "priv-subnet1"
    cidr = "10.0.2.0/24"
  }
}

resource "faxter_router" "my_router" {
  project          = faxter_project.my_app.name
  name             = "my-router"
  connect_external = true
  subnets = [
    faxter_network.public_net.subnets[0].name,
    faxter_network.private_net.subnets[0].name,
  ]
}

# 4. Security Groups
resource "faxter_security_group" "web_sg" {
  project = faxter_project.my_app.name
  name    = "web-sg"

  rules {
    protocol         = "tcp"
    port_range_min   = 80
    port_range_max   = 80
    direction        = "ingress"
    remote_ip_prefix = "0.0.0.0/0"
  }

  rules {
    protocol         = "tcp"
    port_range_min   = 443
    port_range_max   = 443
    direction        = "ingress"
    remote_ip_prefix = "0.0.0.0/0"
  }
}

resource "faxter_security_group" "db_sg" {
  project = faxter_project.my_app.name
  name    = "db-sg"

  rules {
    protocol         = "tcp"
    port_range_min   = 3306
    port_range_max   = 3306
    direction        = "ingress"
    remote_group_id  = faxter_security_group.web_sg.name
  }
}

# 5. Servers
resource "faxter_server" "web_server" {
  project   = faxter_project.my_app.name
  name      = "web1"
  key_name  = faxter_ssh_key.mykey.name
  flavor    = "copper"
  image     = "Ubuntu2204"
  networks  = [faxter_network.public_net.name]
  security_groups = [faxter_security_group.web_sg.name]

  # OPTIONAL: if you want to install a webserver on boot
  cloud_init = file("${path.module}/web_cloud_init.yaml")
}

resource "faxter_volume" "db_volume" {
  project = faxter_project.my_app.name
  name    = "db-storage"
  storage = 50
}

resource "faxter_server" "db_server" {
  project   = faxter_project.my_app.name
  name      = "db1"
  key_name  = faxter_ssh_key.mykey.name
  flavor    = "copper"
  image     = "Ubuntu2204"
  networks  = [faxter_network.private_net.name]
  security_groups = [faxter_security_group.db_sg.name]
  volumes   = [faxter_volume.db_volume.name]

  # OPTIONAL: if you want to install MySQL on boot
  cloud_init = file("${path.module}/db_cloud_init.yaml")
}

# 6. (Optional) A Load Balancer
resource "faxter_loadbalancer" "my_lb" {
  project           = faxter_project.my_app.name
  name              = "frontend-lb"
  port              = 80
  networks          = [faxter_network.public_net.name]
  key_name          = faxter_ssh_key.mykey.name
  request_floating_ip = true
  ssl_enabled       = false
  security_groups   = [faxter_security_group.web_sg.name]

  servers = [
    "${faxter_server.web_server.name}:80",  # if your environment uses name:port
    # or "10.0.1.10:80" style IP addresses
  ]
}

Applying your configuration

terraform init
terraform plan
terraform apply

Terraform will: 1. Create a new project my-app-project. 2. Create two networks (public-network, private-network) with subnets. 3. Create a router connecting them to the outside world (my-router). 4. Create security groups for web (HTTP/HTTPS) and db (MySQL). 5. Create two servers (web_server in the public subnet and db_server in the private subnet). 6. Attach a volume (db_volume) to the database server. 7. Optionally create a load balancer.

10. Additional Use Cases

  • Rapid Dev Environment: Use the default project, create an SSH key + server. Done.
  • Isolated Testing: Create a new project for each test environment, spin up servers, networks, and security groups, tear them down after testing.
  • Multi-Tier Apps: As shown above, separate front-end and back-end servers via different security groups, networks, and a router.
  • Load Balancing: Put multiple servers behind a load balancer.
  • Persistent Data: Attach volumes to keep state/data across server lifecycles.

Troubleshooting Tips

  1. Verify Your Token: If you get a 401 or 403 error, check your token validity.
  2. Check Project Funding: you are required to top-up/fund the project account before resource creation.