From cf5357b43cf2de827b5289b093062b4500c22bf6 Mon Sep 17 00:00:00 2001 From: 0x1d <0x1d@dcentral.systems> Date: Sun, 23 Feb 2025 14:40:45 +0100 Subject: [PATCH] initial commit --- .env.example | 3 ++ .gitignore | 5 +++ .terraform.lock.hcl | 48 ++++++++++++++++++++ README.md | 24 ++++++++++ cluster.tf | 59 +++++++++++++++++++++++++ files.tf | 11 +++++ machines.tf | 104 ++++++++++++++++++++++++++++++++++++++++++++ main.tf | 3 ++ outputs.tf | 9 ++++ providers.tf | 12 +++++ variables.tf | 102 +++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 380 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .terraform.lock.hcl create mode 100644 README.md create mode 100644 cluster.tf create mode 100644 files.tf create mode 100644 machines.tf create mode 100644 main.tf create mode 100644 outputs.tf create mode 100644 providers.tf create mode 100644 variables.tf diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1daba33 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +export PROXMOX_VE_USERNAME="root@pam" +export PROXMOX_VE_PASSWORD="super-secure-password" +export PROXMOX_VE_ENDPOINT="https://:8006/" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7974b09 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +target/ +.terraform/ +*.tfstate +*.tfstate.backup diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl new file mode 100644 index 0000000..9328287 --- /dev/null +++ b/.terraform.lock.hcl @@ -0,0 +1,48 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/bpg/proxmox" { + version = "0.72.0" + constraints = "0.72.0" + hashes = [ + "h1:LCNKZG6lVHdf9LTkHgM8CPUbiFxLI8k208Tz9ajz46c=", + "zh:031d0ade16874fe111055b9417b4f9c73efe7c755ba39aa28bd697ee77dc5e0e", + "zh:095320d9cfb1e1f1b42d0d31f7aef5380323ab5e0d428606c43c9a30bf3b40db", + "zh:11b9ccfc249e150a174f1aa0dd63b8f96296fcb94353902e807da2da20035822", + "zh:24aa2cb7362db5ffebdcc45b0f53897fdd102f322ec7d9e0e4ef60a87955c182", + "zh:334d6d6c2c12803b530ca7fcafe25def317333582dca531ae889bdc1dcbf966a", + "zh:383376b3ce17877f78168270f14a4401093cfee464adf85dd88214d09951e6a2", + "zh:762d16fefdf4af471fe11ba315c7a0a3e5ff04c4f6e8431cd541b2f78cd518ae", + "zh:7c455e70d262e26c3fda8859ed67b0118d12f72416397fc8fbf5b5b90f2f02c3", + "zh:8401a38d10e1aacc7c3f75ae41f42c88647ab7e0974010c616b69095c7a719c1", + "zh:b7bdc53cdd6a21f208fc15bbbd0502fd39bee268801fd2b9ce89e18b38138bc0", + "zh:c3741939ceb5fbd4c00f9aa541a3e9cb68222c39890ca5ed3602a0ca3fa98a53", + "zh:d0d49355b2d1dc847028c96328f8e0ffc4ce39c3641940f9136684a7177d008f", + "zh:ed137c25a20912962413ea1972aa15931f54dcb922a9c4451d08237b6cad2037", + "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", + "zh:f3e38e9c63ef9b295c7e4d2e302d85700f2e8dbff49285e364457b999b927a72", + ] +} + +provider "registry.terraform.io/siderolabs/talos" { + version = "0.7.1" + constraints = "0.7.1" + hashes = [ + "h1:tzxgHnsmjdkgn82pO+LZAmIOyw6AnbyDyqIB+nl22hY=", + "zh:0fa82a384b25a58b65523e0ea4768fa1212b1f5cfc0c9379d31162454fedcc9d", + "zh:162436bf80a53c4bb0e3cd592699129264092c47e2abf01e05cbccfb66ac86de", + "zh:435b0b7e1dddb51fa40acce72f52ca7d4602ad1995912c51028542a5609bb511", + "zh:4566884b49adbb94e7b234b572215266eb6807ec668cd49fbe0840b138046bd2", + "zh:4abf5e1ab2a25740f4ff4e3ecfa57d3fe5c31ed15b4b1d0365227d1f1d32aa40", + "zh:59884c612645ecd74c25abc83ea87d34a32f737fd261ae291f6ef83c22254bd1", + "zh:6d3d44db2e87f5b55c321f4b2434d1280df98ce80127ef534a14b28c5e20c54a", + "zh:73aea54a4283828016463fd8324f8f8fcb0b65e637a80839c719cb3805801062", + "zh:7f6bcdf202e573a7fd3ff28bce0d02c0d1286db9e23985c3ef476b2580bf5339", + "zh:8a5fcfe56265be6d996b112dee7d909ae54cf39e8f50b701bc649a37fe546f6b", + "zh:985113de5713922aab4a1a85edf563669b41c916fdd4c90a7c52f6b3abedb761", + "zh:9c101adc33fc05042029cb113feb7be811938deefe9dd3db58709f8715084a5e", + "zh:bdeb34b2d6b6704a2dd8d04321887502f71aeb6c5673ff30f6e8c039b5797630", + "zh:ce674170f89481e44e20e3f442595104f809cecca666a9703515b28dbcbfc099", + "zh:f8efb1b8abd656c4a4043bed64c09b8b31698319ce49a187a79bdbb7aef051f3", + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..127760e --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Terraform Proxmox Talos + +This Terraform module provisions a [Talos Linux](https://www.talos.dev/) cluster on a [Proxmox Virtual Environment](https://www.proxmox.com/). +It is is based on a greate article by [Olav S. Thoresen: Talos cluster on Proxmox with Terraform](https://olav.ninja/talos-cluster-on-proxmox-with-terraform). +In addition to the configuration provided by the article, this module enables you to provision a HA control plane and any number of worker nodes. + +## Setup + +Configure `.env`: +```shell +export PROXMOX_VE_USERNAME="root@pam" +export PROXMOX_VE_PASSWORD="super-secure-password" +export PROXMOX_VE_ENDPOINT="https://:8006/" +``` + +Apply configuration: +``` +terraform apply +``` + +Write Kubeconfig (caution, this will override you existing Kubeconfig): +``` +terraform output -raw kubeconfig > ~/.kube/config +``` \ No newline at end of file diff --git a/cluster.tf b/cluster.tf new file mode 100644 index 0000000..55c7067 --- /dev/null +++ b/cluster.tf @@ -0,0 +1,59 @@ +resource "talos_machine_secrets" "machine_secrets" {} + +data "talos_client_configuration" "talosconfig" { + cluster_name = var.cluster_name + client_configuration = talos_machine_secrets.machine_secrets.client_configuration + endpoints = [for config in var.talos_controlplane_config : config.ip] +} + +data "talos_machine_configuration" "machineconfig_cp" { + for_each = { for machine in var.talos_controlplane_config : machine.name => machine } + cluster_name = var.cluster_name + cluster_endpoint = "https://${each.value.ip}:6443" + machine_type = "controlplane" + machine_secrets = talos_machine_secrets.machine_secrets.machine_secrets +} + +resource "talos_machine_configuration_apply" "cp_config_apply" { + for_each = { for machine in var.talos_controlplane_config : machine.name => machine } + depends_on = [proxmox_virtual_environment_vm.talos_cp] + client_configuration = talos_machine_secrets.machine_secrets.client_configuration + machine_configuration_input = data.talos_machine_configuration.machineconfig_cp[each.value.name].machine_configuration + node = each.value.ip +} + +data "talos_machine_configuration" "machineconfig_worker" { + for_each = { for machine in var.talos_worker_config : machine.name => machine } + cluster_name = var.cluster_name + cluster_endpoint = "https://${var.talos_controlplane_config[0].ip}:6443" + machine_type = "worker" + machine_secrets = talos_machine_secrets.machine_secrets.machine_secrets +} + +resource "talos_machine_configuration_apply" "worker_config_apply" { + for_each = { for machine in var.talos_worker_config : machine.name => machine } + depends_on = [proxmox_virtual_environment_vm.talos_worker] + client_configuration = talos_machine_secrets.machine_secrets.client_configuration + machine_configuration_input = data.talos_machine_configuration.machineconfig_worker[each.value.name].machine_configuration + node = each.value.ip +} + +resource "talos_machine_bootstrap" "bootstrap" { + depends_on = [talos_machine_configuration_apply.cp_config_apply] + client_configuration = talos_machine_secrets.machine_secrets.client_configuration + node = var.talos_controlplane_config[0].ip +} + +data "talos_cluster_health" "health" { + depends_on = [talos_machine_configuration_apply.cp_config_apply, talos_machine_configuration_apply.worker_config_apply] + client_configuration = data.talos_client_configuration.talosconfig.client_configuration + control_plane_nodes = [for config in var.talos_controlplane_config : config.ip] + worker_nodes = [for config in var.talos_worker_config : config.ip] + endpoints = data.talos_client_configuration.talosconfig.endpoints +} + +resource "talos_cluster_kubeconfig" "kubeconfig" { + depends_on = [talos_machine_bootstrap.bootstrap, data.talos_cluster_health.health] + client_configuration = talos_machine_secrets.machine_secrets.client_configuration + node = var.talos_controlplane_config[0].ip +} diff --git a/files.tf b/files.tf new file mode 100644 index 0000000..3698506 --- /dev/null +++ b/files.tf @@ -0,0 +1,11 @@ +# The image is generated on https://factory.talos.dev/ with QEMU guest addon enabled +resource "proxmox_virtual_environment_download_file" "talos_nocloud_image" { + for_each = toset(var.proxmox_nodes) + content_type = "iso" + datastore_id = "local" + node_name = each.value + file_name = "talos-v${var.talos_version}-nocloud-amd64.img" + url = "https://factory.talos.dev/image/ce4c980550dd2ab1b17bbf2b08801c7eb59418eafe8f279833297925d67c7515/v${var.talos_version}/nocloud-amd64.raw.gz" + decompression_algorithm = "gz" + overwrite = false +} diff --git a/machines.tf b/machines.tf new file mode 100644 index 0000000..3a9b3e7 --- /dev/null +++ b/machines.tf @@ -0,0 +1,104 @@ +resource "proxmox_virtual_environment_vm" "talos_cp" { + for_each = { for machine in var.talos_controlplane_config : machine.name => machine } + name = each.value.name + node_name = each.value.node + vm_id = each.value.id + description = "Managed by Terraform" + tags = ["terraform"] + on_boot = true + + cpu { + cores = each.value.cpu_cores + type = "x86-64-v2-AES" + } + + memory { + dedicated = each.value.memory + } + + agent { + enabled = true + } + + network_device { + bridge = "vmbr0" + } + + disk { + datastore_id = "local-lvm" + file_id = proxmox_virtual_environment_download_file.talos_nocloud_image[each.value.node].id + file_format = "raw" + interface = "virtio0" + size = each.value.disk_size + } + + operating_system { + type = "l26" # Linux Kernel 2.6 - 5.X. + } + + initialization { + datastore_id = "local-lvm" + ip_config { + ipv4 { + address = "${each.value.ip}/24" + gateway = var.default_gateway + } + ipv6 { + address = "dhcp" + } + } + } +} + +resource "proxmox_virtual_environment_vm" "talos_worker" { + for_each = { for machine in var.talos_worker_config : machine.name => machine } + depends_on = [proxmox_virtual_environment_vm.talos_cp] + name = each.value.name + node_name = each.value.node + vm_id = each.value.id + description = "Managed by Terraform" + tags = ["terraform"] + on_boot = true + + cpu { + cores = each.value.cpu_cores + type = "x86-64-v2-AES" + } + + memory { + dedicated = each.value.memory + } + + agent { + enabled = true + } + + network_device { + bridge = "vmbr0" + } + + disk { + datastore_id = "local-lvm" + file_id = proxmox_virtual_environment_download_file.talos_nocloud_image[each.value.node].id + file_format = "raw" + interface = "virtio0" + size = each.value.disk_size + } + + operating_system { + type = "l26" # Linux Kernel 2.6 - 5.X. + } + + initialization { + datastore_id = "local-lvm" + ip_config { + ipv4 { + address = "${each.value.ip}/24" + gateway = var.default_gateway + } + ipv6 { + address = "dhcp" + } + } + } +} diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..8916220 --- /dev/null +++ b/main.tf @@ -0,0 +1,3 @@ +provider "proxmox" { + insecure = true +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..a40e4a3 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,9 @@ +output "talosconfig" { + value = data.talos_client_configuration.talosconfig.talos_config + sensitive = true +} + +output "kubeconfig" { + value = talos_cluster_kubeconfig.kubeconfig.kubeconfig_raw + sensitive = true +} diff --git a/providers.tf b/providers.tf new file mode 100644 index 0000000..cac2b31 --- /dev/null +++ b/providers.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + proxmox = { + source = "bpg/proxmox" + version = "0.72.0" + } + talos = { + source = "siderolabs/talos" + version = "0.7.1" + } + } +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..b04dba1 --- /dev/null +++ b/variables.tf @@ -0,0 +1,102 @@ +variable "cluster_name" { + type = string + default = "homelab" +} +variable "talos_version" { + type = string + default = "1.9.4" +} +variable "default_gateway" { + type = string + default = "192.168.1.1" +} + +variable "proxmox_nodes" { + description = "Names of the Proxmox nodes, used to download and reference node images" + type = list(string) + default = ["ms-01"] +} + +variable "talos_controlplane_config" { + description = "Machine configuration of control-plane nodes" + type = list(object({ + id = number + ip = string + name = string + node = string + cpu_cores = number + memory = number + disk_size = number + })) + default = [{ + id = 101 + name = "talos-cp-01" + ip = "192.168.1.181" + node = "ms-01" + cpu_cores = 2 + memory = 2048 + disk_size = 20 + }, { + id = 102 + name = "talos-cp-02" + ip = "192.168.1.182" + node = "ms-01" + cpu_cores = 2 + memory = 2048 + disk_size = 20 + }, { + id = 103 + name = "talos-cp-03" + ip = "192.168.1.183" + node = "ms-01" + cpu_cores = 2 + memory = 2048 + disk_size = 20 + }] +} + +variable "talos_worker_config" { + description = "Machine configuration of worker nodes" + type = list(object({ + id = number + ip = string + name = string + node = string + cpu_cores = number + memory = number + disk_size = number + })) + default = [{ + id = 111 + name = "talos-worker-01" + ip = "192.168.1.191" + node = "ms-01" + cpu_cores = 4 + memory = 4096 + disk_size = 100 + }, { + id = 112 + name = "talos-worker-02" + ip = "192.168.1.192" + node = "ms-01" + cpu_cores = 4 + memory = 4096 + disk_size = 100 + }, { + id = 113 + name = "talos-worker-03" + ip = "192.168.1.193" + node = "ms-01" + cpu_cores = 4 + memory = 4096 + disk_size = 100 + }, { + id = 114 + name = "talos-worker-04" + ip = "192.168.1.194" + node = "ms-01" + cpu_cores = 4 + memory = 4096 + disk_size = 100 + }] +}