Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions hertzner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Hetzner Cloud CAF - tinycaf

Hetzner Cloud Adoption Framework modules for tinycaf. This is a parallel implementation to the Azure CAF modules in `src/`, fully independent and isolated.

## Why separate from `src/`?

`src/` is reserved for Azure CAF resources. The Hetzner implementation lives under `hertzner/` to maintain clean separation between cloud providers while sharing the same CAF philosophy and patterns.

## Architecture

```
hertzner/
├── _provider.tf # hcloud provider configuration
├── _variables.tf # Core variables (token, global_settings, landingzone)
├── _variables.resources.tf # Resource type variable declarations
├── _locals.tf # Global settings merge
├── _outputs.tf # All module outputs
├── *.tf # Root composition files (one per resource group)
├── modules/ # Reusable CAF modules
│ ├── network/
│ ├── server/
│ ├── firewall/
│ ├── load_balancer/
│ └── ...
└── examples/ # Example .tfvars files
```

## Provider Authentication

Set the Hetzner Cloud API token via environment variable:

```bash
export HCLOUD_TOKEN="your-api-token"
```

Or pass it explicitly:

```hcl
hcloud_token = "your-api-token"
```

## Supported Modules

### Networking
| Module | Resource | Labels |
|--------|----------|--------|
| `network` | `hcloud_network` | Yes |
| `network_subnet` | `hcloud_network_subnet` | No |
| `network_route` | `hcloud_network_route` | No |

### Compute
| Module | Resource | Labels |
|--------|----------|--------|
| `server` | `hcloud_server` | Yes |
| `server_network` | `hcloud_server_network` | No |
| `placement_group` | `hcloud_placement_group` | Yes |

### Storage
| Module | Resource | Labels |
|--------|----------|--------|
| `volume` | `hcloud_volume` | Yes |
| `volume_attachment` | `hcloud_volume_attachment` | No |
| `snapshot` | `hcloud_snapshot` | Yes |
| `storage_box` | `hcloud_storage_box` | Yes |
| `storage_box_snapshot` | `hcloud_storage_box_snapshot` | Yes |
| `storage_box_subaccount` | `hcloud_storage_box_subaccount` | Yes |

### Security
| Module | Resource | Labels |
|--------|----------|--------|
| `firewall` | `hcloud_firewall` | Yes |
| `firewall_attachment` | `hcloud_firewall_attachment` | No |
| `ssh_key` | `hcloud_ssh_key` | Yes |

### IP Management
| Module | Resource | Labels |
|--------|----------|--------|
| `primary_ip` | `hcloud_primary_ip` | Yes |
| `floating_ip` | `hcloud_floating_ip` | Yes |
| `floating_ip_assignment` | `hcloud_floating_ip_assignment` | No |
| `rdns` | `hcloud_rdns` | No |

### Load Balancing
| Module | Resource | Labels |
|--------|----------|--------|
| `load_balancer` | `hcloud_load_balancer` | Yes |
| `load_balancer_service` | `hcloud_load_balancer_service` | No |
| `load_balancer_target` | `hcloud_load_balancer_target` | No |
| `load_balancer_network` | `hcloud_load_balancer_network` | No |

### Certificates
| Module | Resource | Labels |
|--------|----------|--------|
| `managed_certificate` | `hcloud_managed_certificate` | Yes |
| `uploaded_certificate` | `hcloud_uploaded_certificate` | Yes |

### DNS
| Module | Resource | Labels |
|--------|----------|--------|
| `zone` | `hcloud_zone` | Yes |
| `zone_record` | `hcloud_zone_record` | No |
| `zone_rrset` | `hcloud_zone_rrset` | Yes |

## Usage

Deploy using tfvars files (same as Azure CAF):

```bash
terraform init
terraform plan -var-file=examples/complete-foundation.tfvars
terraform apply -var-file=examples/complete-foundation.tfvars
```

## Labels Strategy

Labels are merged in two layers:
1. **Global labels** from `global_settings.labels`
2. **Resource-specific labels** from each resource's `labels` field

Resource-specific labels override global labels for the same key.

## Cross-Module References

Resources reference each other using `_ref` keys:

```hcl
network_subnets = {
subnet_web = {
network_ref = "net_main" # references networks.net_main
type = "cloud"
ip_range = "10.0.1.0/24"
network_zone = "eu-central"
}
}
```

For cross-landing-zone references, use `_lz_key`:

```hcl
network_subnets = {
subnet_web = {
network_ref = "net_main"
network_lz_key = "other_landingzone"
# ...
}
}
```

## Key Differences from Azure CAF

| Aspect | Azure CAF (`src/`) | Hetzner CAF (`hertzner/`) |
|--------|-------------------|--------------------------|
| Provider | azurerm, azapi | hcloud |
| Auth | OIDC (client_id, tenant_id) | API token (HCLOUD_TOKEN) |
| Resource grouping | Resource groups | None (flat per-project) |
| Metadata | Tags | Labels |
| Tag inheritance | Global → RG → resource | Global → resource |
| Regions | Azure regions | Hetzner locations (fsn1, nbg1, hel1, ash, hil, sin) |

## Not Included and Why

| Provider Resource | Reason |
|-------------------|--------|
| `hcloud_certificate` | Deprecated alias for `hcloud_uploaded_certificate` |
| `hcloud_server_poweroff` | Imperative action resource, not declarative infrastructure |
| `hcloud_server_poweron` | Imperative action resource, not declarative infrastructure |
| `hcloud_server_reboot` | Imperative action resource, not declarative infrastructure |
| `hcloud_server_reset` | Imperative action resource, not declarative infrastructure |

## Known Limitations

- No remote state pattern implemented yet (can be added following the Azure CAF pattern with `module.remote_states`)
- Landing zone cross-references work within the same state; multi-state references require the remote state pattern
- Provider version pinned to 1.60.1; update in `_provider.tf` as needed
3 changes: 3 additions & 0 deletions hertzner/_locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
locals {
global_settings = var.global_settings
}
114 changes: 114 additions & 0 deletions hertzner/_outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
output "networks" {
value = module.networks
}

output "network_subnets" {
value = module.network_subnets
}

output "network_routes" {
value = module.network_routes
}

output "servers" {
value = module.servers
}

output "server_networks" {
value = module.server_networks
}

output "volumes" {
value = module.volumes
}

output "volume_attachments" {
value = module.volume_attachments
}

output "firewalls" {
value = module.firewalls
}

output "firewall_attachments" {
value = module.firewall_attachments
}

output "ssh_keys" {
value = module.ssh_keys
}

output "placement_groups" {
value = module.placement_groups
}

output "primary_ips" {
value = module.primary_ips
}

output "rdns_records" {
value = module.rdns_records
}

output "floating_ips" {
value = module.floating_ips
}

output "floating_ip_assignments" {
value = module.floating_ip_assignments
}

output "load_balancers" {
value = module.load_balancers
}

output "load_balancer_services" {
value = module.load_balancer_services
}

output "load_balancer_targets" {
value = module.load_balancer_targets
}

output "load_balancer_networks" {
value = module.load_balancer_networks
}

output "managed_certificates" {
value = module.managed_certificates
}

output "uploaded_certificates" {
value = module.uploaded_certificates
sensitive = true
}

output "snapshots" {
value = module.snapshots
}

output "storage_boxes" {
value = module.storage_boxes
sensitive = true
}

output "storage_box_snapshots" {
value = module.storage_box_snapshots
}

output "storage_box_subaccounts" {
value = module.storage_box_subaccounts
sensitive = true
}

output "zones" {
value = module.zones
}

output "zone_records" {
value = module.zone_records
}

output "zone_rrsets" {
value = module.zone_rrsets
}
12 changes: 12 additions & 0 deletions hertzner/_provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
terraform {
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = "1.60.1"
}
}
}

provider "hcloud" {
token = var.hcloud_token
}
55 changes: 55 additions & 0 deletions hertzner/_variables.resources.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
variable "networks" { default = {} }

variable "network_subnets" { default = {} }

variable "network_routes" { default = {} }

variable "servers" { default = {} }

variable "server_networks" { default = {} }

variable "volumes" { default = {} }

variable "volume_attachments" { default = {} }

variable "firewalls" { default = {} }

variable "firewall_attachments" { default = {} }

variable "ssh_keys" { default = {} }

variable "placement_groups" { default = {} }

variable "primary_ips" { default = {} }

variable "rdns_records" { default = {} }

variable "floating_ips" { default = {} }

variable "floating_ip_assignments" { default = {} }

variable "load_balancers" { default = {} }

variable "load_balancer_services" { default = {} }

variable "load_balancer_targets" { default = {} }

variable "load_balancer_networks" { default = {} }

variable "managed_certificates" { default = {} }

variable "uploaded_certificates" { default = {} }

variable "snapshots" { default = {} }

variable "storage_boxes" { default = {} }

variable "storage_box_snapshots" { default = {} }

variable "storage_box_subaccounts" { default = {} }

variable "zones" { default = {} }

variable "zone_records" { default = {} }

variable "zone_rrsets" { default = {} }
28 changes: 28 additions & 0 deletions hertzner/_variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
variable "hcloud_token" {
description = "Hetzner Cloud API token. Can also be set via HCLOUD_TOKEN env var."
type = string
sensitive = true
default = null
}

variable "global_settings" {
description = "Global settings for tinycaf Hetzner CAF"
type = object({
labels = optional(map(string), {})
})

default = {
labels = {}
}
}

variable "landingzone" {
description = "Landing zone metadata and tfstate dependencies"
type = object({
backend_type = string
key = string
tfstates = optional(map(object({
tfstate = string
})))
})
}
Loading
Loading