chore(infra): simplify Terraform config and update docs for auto-install ISO

- Remove ISO upload and answer-file resources (pre-provisioned manually)
- Remove null_resource wait/token steps (token created out-of-band)
- Switch iso_file to iso_file_id referencing an existing Proxmox file ID
- Add lifecycle ignore_changes for started and cdrom
- Update README with proxmox-auto-install-assistant ISO prep instructions
- Update default variables to match homelab environment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Clint Branham
2026-03-18 12:57:32 -05:00
parent 8ea983c0fc
commit 79fae0bb37
4 changed files with 32 additions and 91 deletions
+1
View File
@@ -4,3 +4,4 @@
!terraform.tfvars.example
.terraform.lock.hcl
.api-token
.build/
+19
View File
@@ -53,6 +53,25 @@ This Terraform configuration provisions a throwaway nested Proxmox VE virtual ma
terraform output -raw pve_test_api_token
```
## Preparing the Installer ISO (one-time per PVE version)
The standard PVE installer ISO has no automated install mode. Before first use — and again whenever you upgrade to a new PVE release — run the following on the Proxmox host to produce a modified ISO that tells the installer to fetch its answer file from an attached FAT partition labeled `PROXMOX-AIS`:
```bash
ssh root@<proxmox-host>
# Paths are examples — adjust to match your NAS mount points
ORIGINAL_ISO=/mnt/pve/nas-nfs/template/iso/proxmox-ve_9.1-1.iso
MODIFIED_ISO=/mnt/pve/nas-nfs/template/iso/proxmox-ve_9.1-1-auto.iso
proxmox-auto-install-assistant prepare-iso "$ORIGINAL_ISO" \
--fetch-from partition \
--partition-label proxmox-ais \
--output "$MODIFIED_ISO"
```
Update `iso_file_id` in `terraform.tfvars` to point to the new `*-auto.iso` file. The original ISO is left untouched.
## How It Works
### Answer File
+7 -76
View File
@@ -14,45 +14,15 @@ provider "proxmox" {
insecure = var.proxmox_insecure
ssh {
agent = true
agent = true
username = "root"
}
}
# Upload PVE ISO to the target node
resource "proxmox_virtual_environment_file" "pve_iso" {
content_type = "iso"
datastore_id = var.iso_storage
node_name = var.target_node
source_file {
path = var.iso_file
}
}
# Answer file for unattended PVE installation
resource "proxmox_virtual_environment_file" "answer_file" {
content_type = "snippets"
datastore_id = var.answer_file_storage
node_name = var.target_node
source_raw {
data = templatefile("${path.module}/answer.toml.tftpl", {
root_password = var.test_vm_password
cidr = "${var.test_vm_ip}/${var.test_vm_netmask_bits}"
dns = var.test_vm_dns
gateway = var.test_vm_gateway
})
file_name = "pve-test-answer.toml"
}
}
# Nested PVE VM
resource "proxmox_virtual_environment_vm" "nested_pve" {
name = var.vm_name
node_name = var.target_node
vm_id = var.vm_id
description = "Automated nested PVE test instance - safe to destroy"
tags = ["test", "nested-pve", "auto-managed"]
name = var.vm_name
node_name = var.target_node
vm_id = var.vm_id
machine = "q35"
bios = "ovmf"
@@ -80,8 +50,7 @@ resource "proxmox_virtual_environment_vm" "nested_pve" {
}
cdrom {
enabled = true
file_id = proxmox_virtual_environment_file.pve_iso.id
file_id = var.iso_file_id
interface = "ide2"
}
@@ -90,51 +59,13 @@ resource "proxmox_virtual_environment_vm" "nested_pve" {
model = "virtio"
}
boot_order = ["scsi0", "ide2"]
operating_system {
type = "l26"
}
on_boot = true
started = true
lifecycle {
ignore_changes = [cdrom]
}
}
# Wait for PVE API to become responsive after installation
resource "null_resource" "wait_for_api" {
depends_on = [proxmox_virtual_environment_vm.nested_pve]
provisioner "local-exec" {
command = "${path.module}/scripts/wait-for-api.sh ${var.test_vm_ip} 8006 600"
}
}
# Create a test API token on the nested PVE for integration tests
resource "null_resource" "create_api_token" {
depends_on = [null_resource.wait_for_api]
provisioner "local-exec" {
command = <<-EOT
# Get a ticket first
TICKET_DATA=$(curl -sk -d "username=root@pam&password=${var.test_vm_password}" \
"https://${var.test_vm_ip}:8006/api2/json/access/ticket")
TICKET=$(echo "$TICKET_DATA" | jq -r '.data.ticket')
CSRF=$(echo "$TICKET_DATA" | jq -r '.data.CSRFPreventionToken')
# Create API token
TOKEN_DATA=$(curl -sk -X POST \
-H "Cookie: PVEAuthCookie=$TICKET" \
-H "CSRFPreventionToken: $CSRF" \
-d "tokenid=integration" \
-d "privsep=0" \
"https://${var.test_vm_ip}:8006/api2/json/access/users/root@pam/token/integration")
TOKEN_VALUE=$(echo "$TOKEN_DATA" | jq -r '.data.value')
echo "root@pam!integration=$TOKEN_VALUE" > ${path.module}/.api-token
EOT
ignore_changes = [started, cdrom]
}
}
+5 -15
View File
@@ -53,24 +53,19 @@ variable "disk_size" {
variable "disk_storage" {
description = "Proxmox storage pool for VM disks (must support raw format)"
type = string
default = "local-lvm"
default = "nas-iscsi-lvm"
}
variable "iso_storage" {
description = "Proxmox storage pool for uploading the PVE ISO"
type = string
default = "local"
}
variable "iso_file" {
description = "Local path to the Proxmox VE installation ISO file"
variable "iso_file_id" {
description = "Proxmox file ID of the PVE installation ISO (e.g. nas-nfs:iso/proxmox-ve_9.1-1.iso)"
type = string
default = "nas-nfs:iso/proxmox-ve_9.1-1.iso"
}
variable "network_bridge" {
description = "Network bridge on the host to attach the nested PVE VM to"
type = string
default = "vmbr0"
default = "Core"
}
variable "test_vm_ip" {
@@ -102,8 +97,3 @@ variable "test_vm_password" {
default = "Testpass123!"
}
variable "answer_file_storage" {
description = "Proxmox storage pool for the answer file snippet (must support snippets content type)"
type = string
default = "local"
}