Step-by-step tutorial for beginners
Prerequisites
- Terraform Installed: Download from terraform.io and ensure terraform version works.
- 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.)
- Faxter Token: You’ll need a valid token from Faxter to authenticate.
- 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
- Verify Your Token: If you get a 401 or 403 error, check your token validity.
- Check Project Funding: you are required to top-up/fund the project account before resource creation.