Reusable Terraform module for provisioning installer ISO-based VMs on Proxmox VE with the bpg/proxmox provider.
This module is designed to be consumed as a standalone public module:
- No dependency on private/local helper modules
- All deployment-specific settings are exposed as input variables
- Provider configuration is expected in the caller (root module)
terraform {
required_version = ">= 1.0"
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "~> 0.97.1"
}
}
}
provider "proxmox" {
endpoint = var.proxmox_endpoint
api_token = var.proxmox_api_token
insecure = false
}
module "vm" {
source = "<org>/proxmox-vm-iso/proxmox"
vm_name = "win-01"
cluster_nodes = ["pve1", "pve2"]
iso_reference = {
node_name = "pve1"
datastore_id = "local"
content_type = "iso"
file_name = "WinServer2025.iso"
}
os_type = "win11"
bios_type = "ovmf"
cdrom_interface = "ide2"
disk_datastore = "local-lvm"
network_bridge = "vmbr0"
efi_disk = {
datastore_id = "local-lvm"
}
}- Attaches installer ISO as CD-ROM on
cdrom_interface - Uses
boot_order(or computed default) to prioritize installer media - Ignores day-2 drift on ISO file ID to prevent unnecessary VM churn when media rotates
Important: This module has been updated to support bpg/proxmox provider version 0.97.1 with expanded VM configuration options.
See MIGRATION-GUIDE.md for:
- List of variables with changed defaults
- Migration strategies for existing deployments
- Details on the new
strict_provider_defaultscompatibility toggle
Quick summary: By default, legacy module defaults are preserved (strict_provider_defaults = false). No immediate action is required for existing deployments, but explicitly setting values is recommended for production workloads.
This module now supports the full feature set of the proxmox_virtual_environment_vm resource, including:
- Advanced CPU options: architecture, flags, hotplug, NUMA, affinity
- Advanced memory options: shared memory, hugepages
- Advanced disk options: AIO mode, cache policy, speed limits, backup control
- Additional disks: attach multiple disks with per-disk configuration
- Additional network devices: multi-NIC VMs with VLAN tagging and rate limiting
- Hardware passthrough: PCIe devices, USB devices, virtiofs mappings
- Security: TPM state device, AMD SEV encryption
- System devices: audio, VGA, watchdog, RNG
- NUMA topology: explicit NUMA node configuration
- Lifecycle control: startup order, reboot behavior, protection flags, clone support
- Timeouts: per-operation timeout customization
Refer to the inputs table below for the complete list of available options.
The module supports a primary disk (disk_interface) and optional additional
disks (additional_disks). Disk interfaces are handled with the following
rules.
- Primary disk accepts
scsi,sata, orvirtiowith optional index.- Bus-only values are normalized to index
0. - Examples:
scsi->scsi0,virtio->virtio0,sata2stayssata2.
- Bus-only values are normalized to index
- Additional disks accept
ide,sata,scsi, orvirtiowith optional index.interfacemay be omitted (or set to empty string) for auto-assignment.- Bus-only values are normalized to index
0(for examplevirtio->virtio0). datastore_idis optional; when omitted, the disk inheritsdisk_datastore.
When an additional disk omits interface, the module auto-assigns the next
available index on the same bus family as the primary disk interface.
- Primary disk
virtio0with additional disks omitting interface will assignvirtio1,virtio2, and so on, skipping any already-explicitly-used indexes. - If no free index is available on that bus, planning fails with a clear error.
Before apply, the module enforces:
- Every resolved disk interface must match
ideN,sataN,scsiN, orvirtioN. - Interface names must be unique across primary and additional disks.
- Auto-assignment must have enough free indexes for all disks with omitted interfaces.
- Primary disk always uses
disk_datastore. - Additional disks use
additional_disks[*].datastore_idwhen provided. - If
additional_disks[*].datastore_idis omitted, it defaults todisk_datastore.
module "vm" {
# ...
disk_interface = "virtio0"
additional_disks = [
{
size = 50
# interface omitted -> auto-assigned (virtio1)
},
{
interface = "virtio3"
size = 100
},
{
size = 200
# interface omitted -> auto-assigned (virtio2)
}
]
}Use these defaults as a safe baseline for most deployments.
- Set the primary disk explicitly to index
0(for examplevirtio0orscsi0). - Prefer omitting
additional_disks[*].interfaceunless you need deterministic device naming. - Keep all additional disks on one bus family per VM unless you have a specific hardware/guest requirement.
- Set
disk_datastoreexplicitly and only override per-diskdatastore_idwhen required. - Use explicit disk sizes and avoid relying on implied defaults in production.
module "vm" {
# ...
disk_datastore = "local-lvm"
disk_interface = "virtio0"
disk_size_gb = 40
disk_discard = "on"
disk_iothread = true
disk_ssd = true
additional_disks = [
{
size = 100
},
{
size = 200
}
]
}Set explicit additional_disks[*].interface only when you need stable,
predefined device addresses for guest-level automation, monitoring, or strict
OS mount mapping expectations.
- Setting primary disk to a non-zero index without a clear reason.
- Manually assigning additional disk indexes that collide with primary or other additional disks.
- Mixing auto-assigned and manually assigned interfaces without checking index gaps and intent.
- Using bus-only values expecting non-zero indexes (for example
virtioalways normalizes tovirtio0).
This repository uses terraform-docs to keep variable and output references accurate.
- Config file:
.terraform-docs.yml - Injection markers are present near the end of this README
Preferred commands from repository root:
make docs
make docs-checkEquivalent script commands:
./scripts/terraform-docs-generate.sh
./scripts/terraform-docs-check.shUnderlying Podman invocation:
podman run --rm \
-v "$PWD:/terraform-docs:Z" \
-w /terraform-docs \
quay.io/terraform-docs/terraform-docs:latest \
markdown table --config /terraform-docs/.terraform-docs.yml .| Name | Version |
|---|---|
| terraform | >= 1.0 |
| proxmox | ~> 0.97.1 |
| Name | Version |
|---|---|
| proxmox | ~> 0.97.1 |
| random | n/a |
No modules.
| Name | Type |
|---|---|
| proxmox_virtual_environment_vm.vm | resource |
| random_shuffle.node_selection | resource |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| acpi | Whether to enable ACPI | bool |
true |
no |
| additional_disks | Additional disk blocks | list(object({ |
[] |
no |
| additional_network_devices | Additional network_device blocks | list(object({ |
[] |
no |
| agent_enabled | Enable QEMU guest agent | bool |
null |
no |
| agent_timeout | How long to wait for QEMU guest agent during create/update | string |
null |
no |
| agent_trim | Enable QEMU agent FSTRIM | bool |
false |
no |
| agent_type | QEMU agent interface type | string |
"virtio" |
no |
| agent_wait_for_ipv4 | Wait for at least one non-link-local IPv4 address from guest agent | bool |
false |
no |
| agent_wait_for_ipv6 | Wait for at least one non-link-local IPv6 address from guest agent | bool |
false |
no |
| amd_sev | Optional AMD SEV configuration | object({ |
null |
no |
| assigned_memory | Assigned memory in MB | number |
null |
no |
| audio_device | Optional audio device configuration | object({ |
null |
no |
| bios_type | BIOS type (seabios or ovmf) | string |
null |
no |
| boot_order | Optional explicit boot order list (for example ["ide2", "scsi0"]). Use 'cdrom' to reference cdrom_interface. | list(string) |
null |
no |
| cdrom_interface | Hardware interface for attached installer ISO | string |
"ide3" |
no |
| clone | Optional clone configuration | object({ |
null |
no |
| cluster_nodes | List of cluster nodes for random selection (required if node_name is null) | list(string) |
[] |
no |
| cpu_additional | Additional CPU options | object({ |
null |
no |
| cpu_cores | Number of CPU cores | number |
null |
no |
| cpu_type | CPU type | string |
null |
no |
| delete_unreferenced_disks_on_destroy | Delete unreferenced disks on destroy | bool |
true |
no |
| description | VM description | string |
"" |
no |
| disk_advanced | Advanced settings for the primary disk | object({ |
null |
no |
| disk_datastore | Datastore for VM disks | string |
n/a | yes |
| disk_discard | Enable discard/TRIM | string |
null |
no |
| disk_interface | Primary disk interface (indexed form like scsi0/virtio1, or bus-only scsi/sata/virtio which normalizes to index 0) | string |
"scsi0" |
no |
| disk_iothread | Enable iothread | bool |
null |
no |
| disk_size_gb | Disk size in GB | number |
20 |
no |
| disk_ssd | Mark disk as SSD | bool |
null |
no |
| efi_disk | EFI disk configuration for OVMF BIOS | object({ |
null |
no |
| hook_script_file_id | Optional hook script file ID | string |
null |
no |
| hostpci | Optional host PCI passthrough mappings | list(object({ |
[] |
no |
| hotplug | Hotplug feature string (for example 'network,disk,usb', '1', or '0') | string |
null |
no |
| iso_lookup_enabled | Whether to resolve ISO by filename on each plan/apply. Set to false after initial create to avoid lookup failures when ISO files are removed from datastore. | bool |
true |
no |
| iso_reference | Reference to ISO file used for installer boot | object({ |
n/a | yes |
| keyboard_layout | Keyboard layout | string |
"en-us" |
no |
| kvm_arguments | Arbitrary arguments passed to KVM | string |
null |
no |
| machine | VM machine type | string |
"pc" |
no |
| memory_additional | Additional memory options | object({ |
null |
no |
| migrate | Allow VM migration | bool |
null |
no |
| minimum_memory | Minimum memory in MB (defaults to assigned_memory when null) | number |
null |
no |
| network_advanced | Advanced settings for the primary network device | object({ |
null |
no |
| network_bridge | Network bridge | string |
n/a | yes |
| network_model | Network device model | string |
"virtio" |
no |
| node_name | Proxmox node to deploy to (null for random selection from cluster) | string |
null |
no |
| numa | Optional NUMA topology configuration | list(object({ |
[] |
no |
| on_boot | Start VM on host boot | bool |
null |
no |
| os_type | Operating system type | string |
"l26" |
no |
| pool_id | Optional Proxmox pool ID to assign the VM | string |
null |
no |
| protection | Sets the protection flag for the VM and disables the remove VM and remove disk operations | bool |
false |
no |
| purge_on_destroy | Purge VM from backup jobs on destroy | bool |
true |
no |
| reboot | Reboot VM after initial creation | bool |
false |
no |
| reboot_after_update | Reboot VM after update if needed | bool |
true |
no |
| rng | Optional random number generator configuration | object({ |
null |
no |
| scsi_hardware | SCSI hardware type | string |
"virtio-scsi-pci" |
no |
| serial_devices | Serial devices. Empty list keeps a single default socket serial device for backwards compatibility. | list(object({ |
[] |
no |
| smbios | Optional SMBIOS type1 configuration | object({ |
null |
no |
| started | Whether to start VM | bool |
true |
no |
| startup | Optional startup/shutdown behavior | object({ |
null |
no |
| stop_on_destroy | Stop VM on destroy instead of shutdown | bool |
null |
no |
| strict_provider_defaults | When true, unset module inputs resolve to documented bpg/proxmox provider defaults instead of legacy module defaults | bool |
false |
no |
| tablet_device | Enable USB tablet device | bool |
true |
no |
| tags | VM tags | list(string) |
[] |
no |
| tags_environment | Environment tags merged before tags | list(string) |
[] |
no |
| tags_global | Global tags merged before tags | list(string) |
[ |
no |
| tags_instance | Instance-specific tags merged before tags | list(string) |
[] |
no |
| tags_role | Role tags merged before tags | list(string) |
[] |
no |
| template | Whether to create as VM template | bool |
false |
no |
| timeout_clone | Timeout for cloning VM in seconds | number |
1800 |
no |
| timeout_create | Timeout for creating VM in seconds | number |
1800 |
no |
| timeout_migrate | Timeout for migrating VM in seconds | number |
1800 |
no |
| timeout_reboot | Timeout for rebooting VM in seconds | number |
1800 |
no |
| timeout_shutdown_vm | Timeout for shutting down VM in seconds | number |
1800 |
no |
| timeout_start_vm | Timeout for starting VM in seconds | number |
1800 |
no |
| timeout_stop_vm | Timeout for stopping VM in seconds | number |
300 |
no |
| tpm_state | Optional TPM state device | object({ |
null |
no |
| usb | Optional USB passthrough mappings | list(object({ |
[] |
no |
| vga | Optional VGA configuration | object({ |
null |
no |
| virtiofs | Optional virtiofs mappings | list(object({ |
[] |
no |
| vm_id | VM ID (null for auto-assignment) | number |
null |
no |
| vm_name | Name of the VM in Proxmox (will be used as default hostname if hostname not specified) | string |
n/a | yes |
| watchdog | Optional watchdog configuration | object({ |
null |
no |
| Name | Description |
|---|---|
| ipv4_addresses | IPv4 addresses of the VM |
| ipv6_addresses | IPv6 addresses of the VM |
| mac_addresses | MAC addresses of the VM |
| node_name | The Proxmox node where the VM is deployed |
| vm_id | The ID of the created VM |
| vm_name | The name of the created VM |