commit cf5357b43cf2de827b5289b093062b4500c22bf6 Author: 0x1d <0x1d@dcentral.systems> Date: Sun Feb 23 14:40:45 2025 +0100 initial commit 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 + }] +}