diff --git a/.gitignore b/.gitignore index 312de7e..852f36f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ +# OpenTofu / Terraform +.terraform/ +.tofu/ +.terraform.lock.hcl +.tofu.lock.hcl +terraform.tfstate +terraform.tfstate.* +crash.log +*.tfvars +*.tfvars.json + inventory.ini venv/* .env diff --git a/tofu/nodito/README.md b/tofu/nodito/README.md new file mode 100644 index 0000000..a6762a5 --- /dev/null +++ b/tofu/nodito/README.md @@ -0,0 +1,66 @@ +## Nodito VMs with OpenTofu (Proxmox) + +This directory lets you declare VMs on the `nodito` Proxmox node and apply with OpenTofu. It clones the Ansible-built template `debian-13-cloud-init` and places disks on the ZFS pool `proxmox-tank-1`. + +### Prereqs +- Proxmox API token with VM privileges. Example: user `root@pam`, token name `tofu`. +- OpenTofu installed. + ``` + sudo apt-get update + sudo apt-get install -y apt-transport-https ca-certificates curl gnupg + + sudo install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://get.opentofu.org/opentofu.gpg | sudo tee /etc/apt/keyrings/opentofu.gpg >/dev/null + curl -fsSL https://packages.opentofu.org/opentofu/tofu/gpgkey | sudo gpg --no-tty --batch --dearmor -o /etc/apt/keyrings/opentofu-repo.gpg >/dev/null + sudo chmod a+r /etc/apt/keyrings/opentofu.gpg /etc/apt/keyrings/opentofu-repo.gpg + + echo \ + "deb [signed-by=/etc/apt/keyrings/opentofu.gpg,/etc/apt/keyrings/opentofu-repo.gpg] https://packages.opentofu.org/opentofu/tofu/any/ any main + deb-src [signed-by=/etc/apt/keyrings/opentofu.gpg,/etc/apt/keyrings/opentofu-repo.gpg] https://packages.opentofu.org/opentofu/tofu/any/ any main" | \ + sudo tee /etc/apt/sources.list.d/opentofu.list > /dev/null + sudo chmod a+r /etc/apt/sources.list.d/opentofu.list + + sudo apt-get update + sudo apt-get install -y tofu + tofu version + ``` +- The Ansible template exists: `debian-13-cloud-init` (VMID 9001 by default). + +### Provider Auth +Create a `terraform.tfvars` (copy from `terraform.tfvars.example`) and set: +- `proxmox_api_url` (e.g. `https://nodito:8006/api2/json`) +- `proxmox_api_token_id` (e.g. `root@pam!tofu`) +- `proxmox_api_token_secret` +- `ssh_authorized_keys` (your public key content) + +Alternatively, you can export env vars and reference them in a tfvars file. + +### Declare VMs +Edit `terraform.tfvars` and fill the `vms` map. Example entry: +``` +vms = { + web1 = { + name = "web1" + cores = 2 + memory_mb = 2048 + disk_size_gb = 20 + ipconfig0 = "ip=dhcp" # or "ip=192.168.1.50/24,gw=192.168.1.1" + } +} +``` + +All VM disks are created on `zfs_storage_name` (defaults to `proxmox-tank-1`). Network attaches to `vmbr0`. VLAN can be set per-VM with `vlan_tag`. + +### Usage +``` +tofu init +tofu plan -var-file=terraform.tfvars +tofu apply -var-file=terraform.tfvars +``` + +### Notes +- Clones are full clones by default (`full_clone = true`). +- Cloud-init injects `cloud_init_user` and `ssh_authorized_keys`. +- Disks use `scsi0` on ZFS with `discard` enabled. + + diff --git a/tofu/nodito/main.tf b/tofu/nodito/main.tf new file mode 100644 index 0000000..a4ce792 --- /dev/null +++ b/tofu/nodito/main.tf @@ -0,0 +1,67 @@ +locals { + default_ipconfig0 = "ip=dhcp" +} + +resource "proxmox_vm_qemu" "vm" { + for_each = var.vms + + name = each.value.name + target_node = var.proxmox_node + vmid = try(each.value.vmid, null) + + onboot = true + agent = 1 + clone = var.template_name + full_clone = true + vga { + type = "serial0" + } + + cpu { + sockets = 1 + cores = each.value.cores + type = "host" + } + memory = each.value.memory_mb + + scsihw = "virtio-scsi-pci" + boot = "c" + bootdisk = "scsi0" + + serial { + id = 0 + type = "socket" + } + + # Network: bridge vmbr0, optional VLAN tag + network { + id = 0 + model = "virtio" + bridge = "vmbr0" + tag = try(each.value.vlan_tag, 0) + } + + # Cloud-init: user, ssh keys, IP, and custom snippet for qemu-guest-agent + ciuser = var.cloud_init_user + sshkeys = var.ssh_authorized_keys + ipconfig0 = try(each.value.ipconfig0, local.default_ipconfig0) + cicustom = "user=local:snippets/user-data-qemu-agent.yaml" + + # Disk on ZFS storage + disk { + slot = "scsi0" + type = "disk" + storage = var.zfs_storage_name + size = "${each.value.disk_size_gb}G" + # optional flags like iothread/ssd/discard differ by provider versions; keep minimal + } + + # Cloud-init CD-ROM so ipconfig0/sshkeys apply + disk { + slot = "ide2" + type = "cloudinit" + storage = var.zfs_storage_name + } +} + + diff --git a/tofu/nodito/provider.tf b/tofu/nodito/provider.tf new file mode 100644 index 0000000..f907fb9 --- /dev/null +++ b/tofu/nodito/provider.tf @@ -0,0 +1,8 @@ +provider "proxmox" { + pm_api_url = var.proxmox_api_url + pm_api_token_id = var.proxmox_api_token_id + pm_api_token_secret = var.proxmox_api_token_secret + pm_tls_insecure = true +} + + diff --git a/tofu/nodito/terraform.tfvars.example b/tofu/nodito/terraform.tfvars.example new file mode 100644 index 0000000..cc88b3f --- /dev/null +++ b/tofu/nodito/terraform.tfvars.example @@ -0,0 +1,35 @@ +proxmox_api_url = "https://nodito:8006/api2/json" +proxmox_api_token_id = "root@pam!tofu" +proxmox_api_token_secret = "REPLACE_ME" + +proxmox_node = "nodito" +zfs_storage_name = "proxmox-tank-1" +template_name = "debian-13-cloud-init" +cloud_init_user = "counterweight" + +# paste your ~/.ssh/id_ed25519.pub or similar +ssh_authorized_keys = <