A data-driven Packer framework for building hardened Proxmox VE VM templates. The framework owns the Proxmox builder contract, normalization layer, and CI validation flow. Consumer repositories bring their own installer templates, Ansible content, and environment-specific values.
This repository is an organizational framework, not a turnkey image factory. Its job is to give downstream repositories a stable, reusable Proxmox/Packer contract so teams inherit:
- secure infrastructure defaults
- a shared input schema
- consistent validation and CI behavior
- a clean separation between framework logic and consumer content
The framework does not decide which packages, hardening profile, or application stack a guest OS should contain. Those decisions stay with the consumer repository.
| Layer | Owner | Default Source |
|---|---|---|
| Packer orchestration and variable contract | This framework | This repository |
| ISO lifecycle on Proxmox storage | This framework's terraform/ helper or a future external media repo |
examples/terraform/ |
| OS installer templates | Consumer repo | Shipped installer examples in examples/packer/ |
| Ansible roles, playbooks, Galaxy requirements | Consumer repo | ansible-framework |
This repository ships installer examples only. It does not ship Ansible roles, playbooks, inventories, or ansible.cfg; consumers import those from ansible-framework or an equivalent repository.
Today, the committed packer/iso/*.pkrvars.hcl files remain the bootstrap media source of truth. For the shipped Rocky, Ubuntu, and Windows families, the framework can infer those bundled media defaults automatically from packer_image.os_name and packer_image.os_version. The terraform/ helper can manage the same ISO lifecycle on Proxmox, but this repository does not yet auto-generate the Packer-side media contract from Terraform outputs. That handoff is intentionally deferred while a dedicated media-tracking repository is being prepared.
At a high level:
- Consumer
.pkrvars.hclfiles provide environment-specific inputs. packer/locals.pkr.hclnormalizes those inputs and assembles the install-template contract.packer/source.pkr.hclmaps the normalized values into the Proxmox ISO builder.packer/builds.pkr.hclruns the consumer-provided installer template, then the consumer-provided Ansible playbook.
See docs/architecture.md for design decisions and docs/template-contract.md for the template variable contract.
| OS | Version | Install Method | Example | Status |
|---|---|---|---|---|
| Rocky Linux | 9.x | Kickstart | examples/packer/rocky-linux-9/ | Validated example |
| Ubuntu Server | 24.04 LTS | Autoinstall | examples/packer/ubuntu-24-04/ | Validated bootstrap example |
| Windows Server | 2022 | Autounattend | examples/packer/windows-server-2022/ | Validated bootstrap example |
The generic install_template contract supports any guest OS that can boot from a rendered template file on a virtual CD.
Bootstrap examples are validated in CI, but consumers are still expected to:
- replace shipped media values if their environment uses different ISO locations or checksums
- point
ansible_config.*paths at consumer-owned Ansible content - decide whether example TLS and WinRM settings are acceptable bootstrap exceptions for their environment
Supported bundled media defaults are inferred for:
rocky+9ubuntu+24.04windows-server+2022
Consumers can still override those defaults explicitly with media_profile, boot_iso, or additional_iso_files.
| Tool | Version | Purpose |
|---|---|---|
| Terraform | 1.14.7 | Optional ISO lifecycle helper |
| Packer | 1.15.0 | Image builder |
| Ansible | Consumer-defined | Consumer-owned provisioning content |
| Proxmox VE | 8.x | Hypervisor target |
| pre-commit | 4.0+ | Local hook runner |
Create a dedicated API token with the minimum permissions needed for Packer and, if used, the Terraform ISO helper. The example below uses --privsep=0, which disables token privilege separation. If your environment supports token-scoped ACLs, prefer a privilege-separated token with only the required permissions.
pveum role add PackerBuilder -privs "VM.Allocate VM.Clone VM.Config.CDROM VM.Config.CPU VM.Config.Cloudinit VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.Migrate VM.Monitor VM.PowerMgmt Datastore.AllocateSpace Datastore.AllocateTemplate Datastore.Audit ISO.Download Pool.Audit Pool.Allocate SDN.Use Sys.Modify"
pveum user add packer@pve
pveum aclmod / -user packer@pve -role PackerBuilder
pveum user token add packer@pve packer-token --privsep=0git clone https://github.com/NWarila/proxmox-packer-framework.git
cd proxmox-packer-framework
cd terraform
terraform init
cd ../packer
packer init .# Packer example inputs
cp ../examples/packer/.env.example ./../.env.packer.example
cp ../examples/packer/rocky-linux-9/rocky-linux-9.pkrvars.hcl ./my-rocky.pkrvars.hcl
# Optional Terraform ISO helper inputs
cp ../examples/terraform/.env.example ../terraform/.env.terraform.example
cp ../examples/terraform/terraform.tfvars.example ../terraform/terraform.tfvarsThe .env.example files are templates for values you should export into your shell or CI environment. They are not auto-loaded by Terraform or Packer.
- export
PKR_VAR_*values for Proxmox access and deploy-user credentials from the copied Packer env example - export
TF_VAR_*values if you are using the Terraform ISO helper - edit
my-rocky.pkrvars.hclfor network, storage, hardware, and installer settings - point
install_template.template_pathandansible_config.*paths at consumer-owned content
For the shipped Rocky, Ubuntu, and Windows families, you do not need a separate ISO var file just to validate or build. The framework will resolve the bundled media defaults automatically unless you override them.
The framework also accepts PKR_VAR_proxmox_skip_tls_verify and PKR_VAR_proxmox_node as top-level CI-friendly overrides for the matching nested packer_image fields.
If you want Terraform to manage ISO lifecycle today:
cd ../terraform
terraform plan
terraform applyThat helper uploads and tracks ISO media in Proxmox, but the checked-in bundled media catalog is still the active Packer-side default until the future external media repository is in place.
# Terraform helper validation
cd ../terraform
terraform fmt -check -recursive .
terraform validate
# Packer validation and build
cd ../packer
packer validate \
-var-file="my-rocky.pkrvars.hcl" \
.
packer build -force \
-var-file="my-rocky.pkrvars.hcl" \
.vm_id values are unique cluster-wide in Proxmox. The shipped example IDs are placeholders only.
pre-commit install
pre-commit install --hook-type commit-msgproxmox-packer-framework/
|-- .config/
|-- .github/
| |-- config/gitleaks.toml
| |-- scripts/
| | |-- get_packer_version.sh
| | |-- validate_examples.sh
| | |-- validate_examples.ps1
| | |-- validate_terraform.sh
| | `-- validate_terraform.ps1
| `-- workflows/
|-- .vscode/
|-- docs/
| |-- architecture.md
| |-- template-contract.md
| `-- REQUIREMENTS.md
|-- examples/
| |-- packer/
| | |-- .env.example
| | |-- rocky-linux-9/
| | |-- ubuntu-24-04/
| | `-- windows-server-2022/
| `-- terraform/
| |-- .env.example
| `-- terraform.tfvars.example
|-- packer/
| |-- iso/
| |-- builds.pkr.hcl
| |-- data.pkr.hcl
| |-- locals.pkr.hcl
| |-- packer.pkr.hcl
| |-- source.pkr.hcl
| `-- variables.pkr.hcl
|-- terraform/
| |-- .terraform.lock.hcl
| |-- data.tf
| |-- locals.tf
| |-- outputs.tf
| |-- providers.tf
| |-- resources.tf
| |-- variables.tf
| `-- versions.tf
|-- .editorconfig
|-- .gitattributes
|-- .pre-commit.config.yaml
|-- .release-please-manifest.json
|-- release-please-config.json
|-- CHANGELOG.md
|-- CODE_OF_CONDUCT.md
|-- CONTRIBUTING.md
|-- LICENSE
|-- SECURITY.md
`-- SUPPORT.md
| Workflow | Trigger | What it does |
|---|---|---|
| Feature Push Gate | Push to any non-main branch |
Gitleaks secret scan |
| Main Validation | Push to main for packer/**, terraform/**, examples/**, .github/scripts/**, .github/workflows/**, .pre-commit.config.yaml |
Gitleaks, Terraform fmt/validate, Packer fmt/validate |
| PR Validation | PR to main for packer/**, terraform/**, examples/**, .github/scripts/**, .github/workflows/**, .pre-commit.config.yaml |
Gitleaks, Terraform fmt/validate, Packer fmt/validate |
| Security Scanning | Push/PR to main, weekly schedule |
Trivy filesystem scan plus Gitleaks |
| Release Please | Push to main |
Automated changelog generation and GitHub releases |
This framework produces Proxmox VM templates designed to be consumed by Terraform or OpenTofu. A downstream repo can check out its own consumer content next to this framework, place its .auto.pkrvars.hcl file in the framework packer/ working directory, and run packer validate . / packer build . directly as long as it supplies:
install_templatepointing at consumer-owned installer templatesansible_configpointing at consumer-owned Ansible content, withansible-playbookavailable on PATH in the runtime environmentpacker_image.os_nameandpacker_image.os_versionmatching a bundled media family, or explicit media overrides
The build timestamp in template_description can be used to trigger downstream VM replacement when a new template is published.
resource "terraform_data" "template_version" {
input = data.proxmox_virtual_environment_vm.template.description
}
resource "proxmox_virtual_environment_vm" "vm" {
lifecycle {
replace_triggered_by = [terraform_data.template_version]
}
}See CONTRIBUTING.md for guidelines. This project uses Conventional Commits and enforces them with pre-commit hooks.
This project is licensed under the MIT License.