Terraform: Infrastructure as Code
Provision your infrastructure declaratively with Terraform. Multi-cloud (AWS, Azure, GCP, OVH, Hetzner, Scaleway), remote state, reusable modules. The IaC reference since 2014.
Introduction
Terraform lets you:
- Describe infra in HCL (HashiCorp Configuration Language)
- Provision servers, network, DNS, certificates on all clouds
- Manage state (who owns what)
- Plan changes before applying
- Version infra in Git
Use cases: create 100 identical VPS, K8s cluster on AWS, manage Cloudflare DNS as code.
Note: since 2023, Terraform is under BSL license (commercial). Community fork OpenTofu keeps the open-source MPL license. This tutorial uses terraform but everything works the same with tofu.
Prerequisites
- Linux VPS or workstation Linux / macOS / Windows
- Cloud provider account (AWS, Hetzner, OVH...)
- Root access (to install)
Step 1: Installation
Debian / Ubuntu
wget -O- https://apt.releases.hashicorp.com/gpg | sudo 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" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo apt install -y terraform
terraform version
Or OpenTofu
curl -fsSL https://get.opentofu.org/install-opentofu.sh | sudo sh -s -- --install-method deb
tofu version
Step 2: First project (Hetzner Cloud)
mkdir ~/infra-hetzner && cd ~/infra-hetzner
main.tf:
terraform {
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = "~> 1.45"
}
}
}
provider "hcloud" {
token = var.hcloud_token
}
variable "hcloud_token" {
type = string
sensitive = true
}
resource "hcloud_server" "web1" {
name = "web1"
image = "debian-12"
server_type = "cx22"
location = "fsn1"
ssh_keys = [hcloud_ssh_key.default.id]
}
resource "hcloud_ssh_key" "default" {
name = "my-key"
public_key = file("~/.ssh/id_ed25519.pub")
}
output "server_ip" {
value = hcloud_server.web1.ipv4_address
}
Step 3: Initialize
terraform init
Step 4: Plan
export TF_VAR_hcloud_token="your-hetzner-token"
terraform plan
Always read the plan carefully before applying.
Step 5: Apply
terraform apply
Type yes. Terraform creates the Hetzner server.
Outputs:
server_ip = "5.78.123.45"
ssh [email protected]
Step 6: State
State is in terraform.tfstate. Contains current infra state.
⚠️ Never commit it to Git:
echo "*.tfstate*" >> .gitignore
echo ".terraform/" >> .gitignore
For team work, use a remote backend.
Step 7: Remote backend (S3)
backend.tf:
terraform {
backend "s3" {
bucket = "myteam-terraform-state"
key = "infra-hetzner/terraform.tfstate"
region = "eu-west-3"
encrypt = true
}
}
terraform init -migrate-state
State now on S3, shared across team. Use DynamoDB for locking (avoid concurrent applies).
Alternatives: Terraform Cloud (managed), Scalr, Spacelift, custom HTTP backend.
Step 8: Variables and environments
variables.tf:
variable "environment" {
type = string
default = "dev"
}
variable "server_type" {
type = string
default = "cx22"
}
locals {
common_tags = {
Environment = var.environment
ManagedBy = "terraform"
}
}
Usage:
resource "hcloud_server" "web" {
name = "${var.environment}-web"
server_type = var.server_type
labels = local.common_tags
}
Variables via .tfvars file:
prod.tfvars:
environment = "prod"
server_type = "ccx33"
terraform apply -var-file=prod.tfvars
Step 9: Modules
modules/
webapp/
main.tf
variables.tf
outputs.tf
modules/webapp/main.tf:
resource "hcloud_server" "web" {
name = var.name
image = "debian-12"
server_type = var.server_type
location = var.location
ssh_keys = var.ssh_keys
}
Usage:
module "web1" {
source = "./modules/webapp"
name = "web1"
server_type = "cx22"
location = "fsn1"
ssh_keys = [hcloud_ssh_key.default.id]
}
module "web2" {
source = "./modules/webapp"
name = "web2"
server_type = "cx32"
location = "nbg1"
ssh_keys = [hcloud_ssh_key.default.id]
}
Community modules: https://registry.terraform.io
Step 10: Provisioning (cloud-init)
resource "hcloud_server" "web" {
name = "web"
image = "debian-12"
server_type = "cx22"
location = "fsn1"
user_data = <<-EOT
#cloud-config
package_update: true
packages:
- nginx
- certbot
runcmd:
- systemctl enable nginx
- echo "Hello from terraform" > /var/www/html/index.html
EOT
}
For complex configs, delegate to Ansible after Terraform provisioning.
Step 11: Destroy
terraform destroy
⚠️ Destroys all infra managed by this state.
Destroy a single resource:
terraform destroy -target=hcloud_server.web1
Step 12: Pro workflow
git pull
terraform fmt -recursive
terraform validate
terraform plan -out=tfplan
terraform show tfplan
terraform apply tfplan
git add . && git commit -m "Add web1" && git push
In CI, use Atlantis or Terraform Cloud to automate plan + apply via PR.
Troubleshooting
"Error: Provider not found"
terraform init -upgrade
State lock
terraform force-unlock LOCK_ID
Check first that no one is applying.
"resource already exists"
You created the resource outside terraform. Import:
terraform import hcloud_server.web1 12345
Drift (resource modified manually)
terraform plan
Shows drift. apply realigns. Or adjust code to match reality.
"Provider produced inconsistent final plan"
Provider bug. Update:
terraform init -upgrade
Useful commands
terraform init
terraform plan
terraform apply
terraform destroy
terraform fmt -recursive
terraform validate
terraform show
terraform state list
terraform state show <resource>
terraform state rm <resource>
terraform state mv <from> <to>
terraform import <resource> <id>
terraform output
terraform output -json
terraform workspace list
terraform workspace new staging
terraform workspace select staging
Conclusion
Terraform gives you:
- Declarative and versioned infrastructure
- Multi-cloud
- State management for team work
- Reusable modules
Going further:
- Use Atlantis or Terraform Cloud for CI/CD workflow
- Combine with Ansible for post-provisioning config
- For Kubernetes, check Pulumi or Crossplane (k8s-native)
- To stay open-source, migrate to OpenTofu
Resources
- Official docs: https://developer.hashicorp.com/terraform
- Provider registry: https://registry.terraform.io
- OpenTofu: https://opentofu.org
- Atlantis: https://www.runatlantis.io

















