Deploying a Windows machine on Incus with OpenTofu

Hello everyone !

My question would be about generating infra with Open-Tofu on Incus, it’s not a problem for linux machines but for Windows I’ve never really succeeded, is there anyone who has tried and could show me their code?

Thank you a lot

I’ve done it before but don’t have an example I can share.

Basically the way I’d recommend you do it is:

  • Manually create a working VM
    • Convert ISO with distrobuilder
    • Upload ISO as custom volume
    • Create empty VM
    • Tweak CPU, memory, storage, maybe add TPM, … to the VM
    • Attach ISO volume
    • Perform installation
    • Tweak anything you want to tweak
    • Run sysprep to tell Windows the VM is going to be cloned
    • Stop the VM
    • Detach installation media
    • Create a snapshot for the VM, say clean for example
  • Now With OpenTofu, you can create new VMs from it by using the source_instance option, pointing to the existing VM and its clean snapshot

An alternative is to use incus publish --alias windows VM-NAME to turn the VM into a local image which you can then use with source_image, but that usually results in a very large image taking a bunch more disk space and slowing down handling a bit compared to the much lighter weight copy mechanism used by source_instance.

2 Likes

thank you a lot will try this today
I will post my config if it works

Ok, first of all thank you a lot but I have a new issue,

So I did like you said, and by following the Blog Simos tutorial for Windows

  1. I started by install distrobuilder and everything i need :
sudo snap install distrobuilder --classic
sudo apt install -y libguestfs-tools wimtools rsync
  1. I initialised my image :
sudo distrobuilder repack-windows --windows-arch=amd64 Win10_22H2_English_x64v2.iso Win10.iso

  1. I Created the Windows VM Base Template :
incus init win10vm --empty --vm
incus config device override win10vm root size=55GiB
incus config set win10vm limits.cpu=4 limits.memory=6GiB
incus config device add win10vm vtpm tpm path=/dev/tpm0
incus config set win10vm raw.qemu -- "-device intel-hda -device hda-duplex -audio spice"
incus config device add win10vm install disk source=/path/to/Win10incus.iso boot.priority=10
  1. Booted the VM and Install Windows :
incus start win10vm --console=vga

you need to touch keys on your keyboard to have a clean boot

  1. Followed the regular Windows installation steps via the VGA console. After installation completes:
incus config device remove win10vm install
  1. Sysprep and Snapshot the VM :

Inside the Windows VM, run

C:\Windows\System32\Sysprep\Sysprep.exe

Choosed “Enter System Out-of-Box Experience (OOBE)”, tick “Generalize”, and set shutdown.

  1. After shutdown did a snapshot :
incus snapshot create win10vm clean

I keep the instances i created in order to keep the snapshot obviously

  1. I Try my OpenTofu script
resource "incus_instance" "windows" {
  name   = "Windows"
  type   = "virtual-machine"
  running = true

  source_instance = {
    project   = "default"
    name   = "windows"
    snapshot   = "windows-clean"
  }

  device {
    name      = "eth0"
    type      = "nic"
    properties = {
      "nictype" = "bridged"
      "parent"  = incus_network.lan.name
    }
  }
}

But here is my error when i try to relaunch the VM (which was working perfectly before i removed the install device :


sorry it’s in french but i think you’ll understand whats is going on, if not tell me

One first quick note. If running with the Zabbly packages, you can now use the incus-extra package which includes distrobuilder, saves you from dealing with snaps :wink:

Can you show the full incus config show --expanded Windows?

I’d generally expect Windows to behave that way if the storage driver changed somehow or if there’s something odd going on with the devices.

1 Like

Here is the command sir

$ incus config show --expanded Windows
architecture: x86_64
config:
  limits.cpu: "4"
  limits.memory: 6GiB
  raw.qemu: -device intel-hda -device hda-duplex -audio spice
  volatile.cloud-init.instance-id: 3e66bb0a-05b1-4f15-a183-d24e6485cbb2
  volatile.eth0.hwaddr: 00:16:3e:09:9b:74
  volatile.last_state.power: STOPPED
  volatile.last_state.ready: "false"
  volatile.uuid: df93222e-88e9-4fb7-aec4-e7828eacd726
  volatile.uuid.generation: df93222e-88e9-4fb7-aec4-e7828eacd726
  volatile.vm.definition: pc-q35-9.0
  volatile.vsock_id: "1747483538"
devices:
  eth0:
    nictype: bridged
    parent: incus-lan
    type: nic
  root:
    path: /
    pool: incus-storage
    size: 55GiB
    type: disk
  vtpm:
    path: /dev/tpm0
    type: tpm
ephemeral: false
profiles:
- default
stateful: false
description: ""

Okay, so that looks like a correct copy, nothing got dropped/changed as far as I can tell.

What storage backend are you using?

And what version of Incus on what distro would be useful too.
We’ve had some issues with copies and handling of sparse data that could explain this.

Right now im on :

  • Ubuntu 24
  • Incus 6.10.1

And my storage backend ? you mean on Incus ?

i use a dir

Okay, using dir, it shouldn’t be the sparse storage issue and 6.10.1 on 24.04 would have had the fix for that anyway.

I found the error !

Here :

Inside the Windows VM, run

C:\Windows\System32\Sysprep\Sysprep.exe

Choosed “Enter System Out-of-Box Experience (OOBE)”, tick “Generalize”, and set shutdown.

You need to untick the “Generalize” option

Sysprep with “Generalize” can sometimes strip drivers or break the boot config if the Windows install didn’t fully complete setup. Some folks recommend not generalizing in certain VM environments. Try a test VM without Generalize (just OOBE + Shutdown).

After this you still need to configure your Windows setup and you have some errors in the terminal of opentofu that i need to look, but it work just fine

1 Like

Ah, interesting, so it basically broke itself by removing required drivers, oops :wink: