# Terraform LXC Deployment for Iron Legion — PRD **Status:** Deployed | **Author:** Artemis | **Date:** 2026-06-04 > **Phase 1 validation:** Single LXC plan/build/destroy completed successfully on MK33 (pve-swarm). All open questions resolved. Phase 2 (batch) in separate PRD. ## 1. Objective Deploy Proxmox LXC containers via Terraform using the `bpg/proxmox` provider, running inside a custom Docker container (lazy automator pattern). Support runtime parameterization for bulk LXC creation with auto-incrementing VMID, IPv4, and naming. ## 2. Architecture ### 2.1 Docker Image **Base:** `hashicorp/terraform:latest` with `bpg/proxmox` provider downloaded at container init **Provider:** `bpg/proxmox` v0.70.0 **Pattern:** Lazy automator — local workspace mounted into container, credentials via `terraform.auto.tfvars` ```dockerfile FROM hashicorp/terraform:latest WORKDIR /workspace COPY run.sh /usr/local/bin/run RUN chmod +x /usr/local/bin/run ENTRYPOINT ["bash"] ``` ### 2.2 Credential Model Native Terraform variable loading via `terraform.auto.tfvars` (no Docker env-file mapping): ```hcl # terraform/terraform.auto.tfvars pm_api_url = "https://192.168.7.33:8006/api2/json" pm_api_token_id = "root@pam!terraform" pm_api_token_secret = "" ``` PVE API token created on MK33: `root@pam!terraform`. Token stored in fleet credential store. ### 2.3 Runtime Parameterization (Phase 2) | Parameter | Example | Effect | |-----------|---------|--------| | `count` | `4` | Number of LXCs to create | | `vmid_base` | `5050` | Starting VMID | Auto-derived per LXC (index `i` from 0 to `count-1`): - **VMID:** `vmid_base + i` - **Name:** `lxc-${vmid}` - **IPv4:** `192.168.${first2digits(vmid)}.${last2digits(vmid)}/18` ### 2.4 LXC Configuration (Validated) - **OS:** Debian 12 (`debian-12-standard_12.2-1_amd64.tar.zst`) - **CPU:** 1 vCPU - **RAM:** 2048 MB - **Storage:** 8GB rootfs on `local` directory (test phase) - **Network:** Static IPv4, gateway `192.168.18.1`, subnet `/18` - **DNS:** `192.168.7.7`, `192.168.18.1`, `1.1.1.1` - **Privilege:** Unprivileged (`unprivileged = true`) - **Features:** Nesting enabled (`features { nesting = true }`) ### 2.5 User / SSH (Tested) ```hcl initialization { user_account { username = "jarvis" password = "" # Required for console login verification keys = [file("artemis_key.pub")] } } ``` ## 3. Phase Breakdown ### Phase 1 — Single LXC (Plan/Build/Destroy) ✅ COMPLETE **Completed:** 2026-06-04 on MK33 (pve-swarm, cluster node 33) **Results:** - `Dockerfile` — simplified to official `hashicorp/terraform:latest` image - `docker-compose.yml` — workspace mount, no env-file credential mapping - `run.sh` — wrapper for `terraform plan/apply/destroy` - `terraform/providers.tf` — `bpg/proxmox` v0.70.0 - `terraform/main.tf` — single LXC resource (VMID 5050) - `terraform/terraform.auto.tfvars` — native Terraform credential loading **Validated:** ```bash ./run.sh plan # ✅ Validated ./run.sh apply # ✅ Created lxc-5050 (debian-12, 192.168.50.50/18) ./run.sh destroy # ✅ Clean teardown ``` **Key fixes discovered during testing:** - Storage pool: `local-lvm` missing → used `local` (Directory) - Template path: `nas-ct-stor:vztmpl/` (NFS shared templates) - Unprivileged required: `unprivileged = true` + `features { nesting = true }` - Password injection: `user_account.password` required for console login verification ### Phase 2 — Modular + Bulk Creation **Goal:** Add `count`, `vmid_base`, and auto-derived naming/IP. **Deliverables:** - `modules/lxc/` — reusable LXC module - `locals.tf` — VMID/IP/name calculation logic - `main.tf` — uses module with `count = var.lxc_count` **Example execution:** ```bash TF_VAR_lxc_count=4 TF_VAR_vmid_base=5050 ./run.sh apply # Creates: lxc-5050, lxc-5051, lxc-5052, lxc-5053 ``` ## 4. File Structure ``` ~/docker/terraform-pve/ ├── Dockerfile ├── docker-compose.yml ├── run.sh ├── terraform/ │ ├── .terraform/ │ ├── main.tf │ ├── providers.tf │ ├── terraform.auto.tfvars # Credentials (not committed) │ ├── terraform.tfstate │ ├── variables.tf │ └── artemis_key.pub ``` ## 5. Resolved Decisions | Decision | Chosen | Notes | |----------|--------|-------| | Debian template | **12** | `debian-12-standard_12.2-1_amd64.tar.zst` on `nas-ct-stor` | | Gateway | **192.168.18.1** | Router IP for 192.168.0.0/18 subnet | | DNS | **192.168.7.7, 192.168.18.1, 1.1.1.1** | Technitium primary + fallback | | SSH key | **artemis_key.pub** | Already registered fleet-wide | | Storage (Phase 1) | **local** | `local-lvm` missing on nodes; migrate to `truenas-nfs` in Phase 2 | | Privilege | **Unprivileged** | `unprivileged = true` with `nesting = true` for systemd 252 | | Credential loading | **terraform.auto.tfvars** | Native Terraform pattern; no Docker env-file complexity | ## 6. Fleet Notes - PVE API token: `root@pam!terraform` (Secret: fleet credential store) - PVE root password: `proxmox12` (fleet credential store) - Cluster: `pve-swarm` (MK33, MK34, MK39) - Template storage: `nas-ct-stor` (NFS from TrueNAS) - Disk storage (test): `local` - **Code location:** `~/docker/terraform-pve/` — local only, not in any Gitea repo