Description
Describe the bug
When importing an existing VM that was originally created with a disk.file_id reference (e.g., pointing to a downloaded image), the provider does not populate the file_id field during the import. As a result, a subsequent terraform plan triggers a forced replacement of the VM, even though no actual configuration has changed.
To Reproduce
Steps to reproduce the behavior:
-
Create a VM using proxmox_virtual_environment_vm with a disk.file_id referring to a proxmox_virtual_environment_download_file.
-
Run terraform apply to create the VM. -> VM will be created and its
file_id
populated. -
Run terraform
state rm proxmox_virtual_environment_vm.centos_vm
to remove the VM from the Terraform state. -
Import the VM again using
terraform import proxmox_virtual_environment_vm.centos_vm <node>/<vmid>
. ->file_id
field won't be populated. -
Run terraform plan.
-
See that Terraform proposes to destroy and recreate the VM due to file_id mismatch (even though nothing has changed on the VM).
Please also provide a minimal Terraform configuration that reproduces the issue.
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "0.78.1"
}
}
}
provider "proxmox" {
endpoint = var.virtual_environment_endpoint
username = "root@pam"
password = var.virtual_environment_password
insecure = true
ssh {
agent = true
username = var.virtual_environment_ssh_username
}
}
resource "proxmox_virtual_environment_vm" "centos_vm" {
name = "test-centos"
node_name = var.proxmox_node_name
# should be true if qemu agent is not installed / enabled on the VM
stop_on_destroy = true
initialization {
datastore_id = "local"
user_account {
# do not use this in production, configure your own ssh key instead!
username = "xxxxx"
password = "yyyyy"
}
}
disk {
datastore_id = "local"
file_id = proxmox_virtual_environment_download_file.centos_cloud_image.id
interface = "virtio0"
iothread = true
discard = "on"
size = 20
}
}
resource "proxmox_virtual_environment_download_file" "centos_cloud_image" {
content_type = "iso"
datastore_id = "local"
node_name = var.proxmox_node_name
url = "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-latest.x86_64.qcow2"
file_name = "centos8.img"
}
and the output of terraform|tofu apply
initially launched.
terraform apply ─╯
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# proxmox_virtual_environment_download_file.centos_cloud_image will be created
+ resource "proxmox_virtual_environment_download_file" "centos_cloud_image" {
+ content_type = "iso"
+ datastore_id = "local"
+ file_name = "centos8.img"
+ id = (known after apply)
+ node_name = "bootstrap"
+ overwrite = true
+ overwrite_unmanaged = false
+ size = (known after apply)
+ upload_timeout = 600
+ url = "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-latest.x86_64.qcow2"
+ verify = true
}
# proxmox_virtual_environment_vm.centos_vm will be created
+ resource "proxmox_virtual_environment_vm" "centos_vm" {
+ acpi = true
+ bios = "seabios"
+ id = (known after apply)
+ ipv4_addresses = (known after apply)
+ ipv6_addresses = (known after apply)
+ keyboard_layout = "en-us"
+ mac_addresses = (known after apply)
+ migrate = false
+ name = "test-centos-remy"
+ network_interface_names = (known after apply)
+ node_name = "bootstrap"
+ on_boot = true
+ protection = false
+ reboot = false
+ reboot_after_update = true
+ scsi_hardware = "virtio-scsi-pci"
+ started = true
+ stop_on_destroy = true
+ tablet_device = true
+ template = false
+ timeout_clone = 1800
+ timeout_create = 1800
+ timeout_migrate = 1800
+ timeout_move_disk = 1800
+ timeout_reboot = 1800
+ timeout_shutdown_vm = 1800
+ timeout_start_vm = 1800
+ timeout_stop_vm = 300
+ vm_id = (known after apply)
+ disk {
+ aio = "io_uring"
+ backup = true
+ cache = "none"
+ datastore_id = "local"
+ discard = "on"
+ file_format = (known after apply)
+ file_id = (known after apply)
+ interface = "virtio0"
+ iothread = true
+ path_in_datastore = (known after apply)
+ replicate = true
+ size = 20
+ ssd = false
}
+ initialization {
+ datastore_id = "local"
+ meta_data_file_id = (known after apply)
+ network_data_file_id = (known after apply)
+ type = (known after apply)
+ user_data_file_id = (known after apply)
+ vendor_data_file_id = (known after apply)
+ user_account {
+ password = (sensitive value)
+ username = "user"
}
}
+ network_device (known after apply)
+ vga (known after apply)
}
Plan: 2 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
proxmox_virtual_environment_download_file.centos_cloud_image: Creating...
proxmox_virtual_environment_download_file.centos_cloud_image: Still creating... [00m10s elapsed]
proxmox_virtual_environment_download_file.centos_cloud_image: Still creating... [00m20s elapsed]
proxmox_virtual_environment_download_file.centos_cloud_image: Creation complete after 22s [id=local:iso/centos8.img]
proxmox_virtual_environment_vm.centos_vm: Creating...
proxmox_virtual_environment_vm.centos_vm: Still creating... [00m10s elapsed]
proxmox_virtual_environment_vm.centos_vm: Still creating... [00m20s elapsed]
proxmox_virtual_environment_vm.centos_vm: Still creating... [00m30s elapsed]
proxmox_virtual_environment_vm.centos_vm: Still creating... [00m40s elapsed]
proxmox_virtual_environment_vm.centos_vm: Still creating... [00m50s elapsed]
proxmox_virtual_environment_vm.centos_vm: Still creating... [01m00s elapsed]
proxmox_virtual_environment_vm.centos_vm: Still creating... [01m10s elapsed]
proxmox_virtual_environment_vm.centos_vm: Still creating... [01m20s elapsed]
proxmox_virtual_environment_vm.centos_vm: Still creating... [01m30s elapsed]
proxmox_virtual_environment_vm.centos_vm: Creation complete after 1m38s [id=100]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
the output of terraform|tofu plan
after re-import:
terraform plan ─╯
proxmox_virtual_environment_download_file.centos_cloud_image: Refreshing state... [id=local:iso/centos8.img]
proxmox_virtual_environment_vm.centos_vm: Refreshing state... [id=100]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# proxmox_virtual_environment_vm.centos_vm must be replaced
-/+ resource "proxmox_virtual_environment_vm" "centos_vm" {
~ id = "100" -> (known after apply)
~ ipv4_addresses = [] -> (known after apply)
~ ipv6_addresses = [] -> (known after apply)
~ mac_addresses = [] -> (known after apply)
+ migrate = false
name = "test-centos-remy"
~ network_interface_names = [] -> (known after apply)
+ on_boot = true
+ reboot = false
+ reboot_after_update = true
+ stop_on_destroy = true
- tags = [] -> null
+ timeout_clone = 1800
+ timeout_create = 1800
+ timeout_migrate = 1800
+ timeout_move_disk = 1800
+ timeout_reboot = 1800
+ timeout_shutdown_vm = 1800
+ timeout_start_vm = 1800
+ timeout_stop_vm = 300
~ vm_id = 100 -> (known after apply)
# (12 unchanged attributes hidden)
~ disk {
~ file_format = "qcow2" -> (known after apply)
+ file_id = "local:iso/centos8.img" # forces replacement
~ path_in_datastore = "100/vm-100-disk-0.qcow2" -> (known after apply)
# (11 unchanged attributes hidden)
}
~ initialization {
- interface = "ide2" -> null
+ meta_data_file_id = (known after apply)
+ network_data_file_id = (known after apply)
+ type = (known after apply)
+ user_data_file_id = (known after apply)
+ vendor_data_file_id = (known after apply)
# (1 unchanged attribute hidden)
~ user_account {
- keys = [] -> null
~ password = (sensitive value)
# (1 unchanged attribute hidden)
}
}
~ network_device (known after apply)
~ vga (known after apply)
}
Plan: 1 to add, 0 to change, 1 to destroy.
Expected behavior
After importing the VM, Terraform should detect that the actual state of the VM matches the configuration. It should not try to replace the VM just because file_id is missing from state — ideally, the provider should populate it during the import/read phase, or at least not trigger a diff if it cannot.
Additional context
My guess is the provider's Read
logic does actually not populate the disk.file_id
field after import.
I'd gladly try to fix but as I didn't participate to the vm logic in the provider I'd be scared of breaking something 😆
- Proxmox version: 8.4.1
- Provider version (ideally it should be the latest version): 0.78.1
- Terraform/OpenTofu version: v1.12.2
- OS (where you run Terraform/OpenTofu from): Ubuntu 24.04.2
Metadata
Metadata
Assignees
Labels
Projects
Status