BILLmanager 6
en En
es Es

Preparing templates for the Hyper-V processing module

For automatic operation of the Hyper-V processing module (pmhyperv), virtual machine (VM) templates must comply with a unified standard. This article describes the directory structure, the format of the template.json file, and the preparation of VHDX disks for Linux and Windows for automatic VM creation via the Hyper-V processing module. The instructions will help create templates that will be deployed without administrator intervention on the platform.

To prepare a template:

  1. Create a directory with a defined structure.
  2. Prepare the template description — the template.json file.
  3. Prepare a custom configuration file for the bootstrap mechanism (bootstrap config).
  4. Prepare the guest OS.
  5. Publish the template.

Directory structure

Each template is located in a separate subdirectory of the templates directory:

Example structure
D:\Templates\
├── ubuntu-24.04\
│   ├── disk.vhdx
│   ├── user-data.yaml.tpl
│   └── template.json
└── windows-server-2022\
    ├── disk.vhdx
    ├── unattend.xml.tpl
    └── template.json

The subdirectory name must match the value of intname in the file template.json. The Hyper-V processing module module:

  1. Stores the value of intname in BILLmanager.
  2. When creating a VM, creates the directory <templates_dir>\<intname>.

Format of template.json file

Required fields

FieldTypePurpose
intnameStringUnique internal name matching the directory name. For example,ubuntu-24.04
nameStringTemplate name displayed in BILLmanager.
os_familyStringOS family. Possible values: linux or windows
generationIntegerHyper-V generation. Possible values: 1 or 2.
vhdx_fileStringName of the VHDX file in the template directory.
bootstrap_fileStringRelative path to the bootstrap configuration file relative to the template directory.

Additional fields for Linux, generation 2

FieldValuesDefault value
secure_boot

Secure boot support. Possible values:

  • on, true, yes, 1 — enabled;
  • off, false, no, 0 — disabled.
on
secure_boot_template

Secure boot parameters. Possible values:

  • MicrosoftUEFICertificateAuthority;
  • MicrosoftWindows;
  • OpenSourceShieldedVM.
MicrosoftUEFICertificateAuthority.

Additional template fields used when publishing to BILLmanager

FieldTypePurpose
ncpu IntegerMinimum number of processors.
memInteger, MiBMinimum RAM amount.
discInteger, GiBMinimum virtual disk size.

Custom bootstrap configuration file

The module does not automatically configure the guest OS: DHCP, DNS, account, SSH, OOBE, partition, and file system parameters are not generated. OS configuration is performed using a bootstrap config — a file with configuration instructions. For Linux, this is a YAML file whose first line must be #cloud-config. For Windows, this is an XML file. See examples below.

The relative path to the bootstrap config is specified in the bootstrap_file field of the template.json file. The path is specified relative to the template directory. Absolute paths and directory traversal using .. are not supported.

To pass VM information, use tokens — records in the format __BILLMGR_<parameter>__. Before building the ISO image, the module will substitute the current value. Supported tokens:

TokenValue
__BILLMGR_HOSTNAME__Short hostname.
__BILLMGR_FQDN__Fully qualified domain name of the service or short name.
__BILLMGR_PASSWORD__Generated administrator password.
__BILLMGR_INSTANCE_ID__VM name vm_<item>.
__BILLMGR_PROVISIONING_TOKEN__Expected readiness marker value. The guest OS must write it to the KVP pool (Key-Value Pair) under the key BILLmanager.Provisioning

Using unknown tokens in the format __BILLMGR_*__ is prohibited.

Minimal template examples

Linux

Directory structure
D:\Templates\ubuntu-test\ 
├── disk.vhdx 
├── template.json 
└── user-data.yaml.tpl
Minimal template.json file
{
  "intname": "ubuntu-test",
  "name": "Ubuntu Test",
  "os_family": "linux",
  "generation": 2,
  "vhdx_file": "disk.vhdx",
  "bootstrap_file": "user-data.yaml.tpl"
}

If the image does not support Secure Boot, add the field "secure_boot": "off".

Minimal user-data.yaml.tpl file

The file sets the root user password and notifies the module of bootstrap completion:

Example user-data.yaml.tpl file

The example does not modify the partition table or file system. The Linux image must:

  • contain network interface configuration for DHCP operation before VM deployment;
  • support NoCloud — a cloud-init data source that allows passing configuration locally without a network service;
  • run hv_kvp_daemon.

SSH server is required only for client access and is not used by the module.

Windows

Directory structure
D:\Templates\windows-test\
├── disk.vhdx
├── template.json
└── unattend.xml.tpl
Minimal template.json
{
  "intname": "windows-test",
  "name": "Windows Test",
  "os_family": "windows",
  "generation": 2,
  "vhdx_file": "disk.vhdx",
  "bootstrap_file": "unattend.xml.tpl"
}
Example minimal unattend.xml.tpl file

The system writes the marker as SYSTEM during the specialize phase. For more details, see Microsoft documentation https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/specialize?view=windows-11. The configuration does not use parameters AutoLogon (for more details see https://learn.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-autologon) and FirstLogonCommands (for more details see https://learn.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-firstlogoncommands). The configuration does not depend on the first interactive login, Windows language, or built-in administrator name. The oobeSystem phase sets the generated password for the built-in Administrator account.

The Windows image must:

  • use DHCP;
  • contain an active Hyper-V Data Exchange service;
  • apply the unattend.xml file from the attached bootable DVD disk during startup.

Before publishing, execute the command:

sysprep.exe /generalize /oobe /shutdown /mode:vm

General requirements for VHDX disk

Prepare the disk according to the following requirements:

  • use VHDX format. If your template is in VHD format, convert it to VHDX. After conversion, the file must have the .vhdx extension, and the command Get-VHD must return VhdFormat = VHDX;
    Simply renaming the file from .vhd to .vhdx is not sufficient. Use specialized software (for example, the Hyper-V Manager graphical snap-in or the Convert-VHD utility)
  • completely shut down the source virtual machine and detach the VHDX disk;
  • use a VHDX disk without checkpoints or differencing chains;
  • ensure the image does not contain user data, static addresses, or old bootstrap disks;
  • specify in the template.json file the generation that matches the system boot method:
    • BIOS for generation 1;
    • UEFI for generation 2;
  • use a VHDX disk from a VM of the same generation specified in the metadata;
  • grant the WinRM service account read access to the VHDX disk files template.json and bootstrap_file;
  • remove the "read-only" attribute, as the module executes the Resize-VHD command after copying.

Disk size

When creating a VM, the module copies the VHDX disk and sets the exact virtual size according to the tariff's disc resource. Set the template disk size no larger than the minimum value of the disc resource for all tariffs using this template.
Check the disk size using the command:

Get-VHD 'D:\Templates\ubuntu-24.04\disk.vhdx' |
    Select-Object Path, VhdFormat, VhdType, Size, MinimumSize, FileSize

The MinimumSize field shows the theoretical minimum to which Hyper-V can shrink the disk; however, the processing module does not support reducing disk size. If the template disk size exceeds the target size specified by the tariff's disc resource, VM creation will fail. It is recommended to prepare a small base disk and allow only expansion.

The module changes only the virtual size of the VHDX on the Hyper-V host. It does not modify the partition table or file system inside the guest operating system (Linux or Windows). If you need to expand the guest partition, use the bootstrap config or OS administration tools.

Linux template

Guest OS requirements

Required:

  • cloud-init with NoCloud data source;
  • hv_kvp_daemon for:
    • Data Exchange integration services;
    • passing the DHCP address and readiness marker to the node;
  • default DHCP configuration;
  • obtaining an IP address via DHCP.

You can perform installation from:

  • official cloud image;
  • ISO image.

Perform disk preparation on a temporary VM in Hyper-V. After completing configuration and cleanup, delete this VM, keeping only the VHDX disk file for the template.

OS installation

Option 1: official cloud image

If you have no experience configuring Linux templates, it is recommended to start with the official cloud image (cloud image) of the distribution. If the image is provided in qcow2 or raw format, convert it on a Linux VM using the utility qemu-img :

Example conversion command
qemu-img info ubuntu-24.04-server-cloudimg-amd64.img
qemu-img convert -p -f qcow2 -O vhdx -o subformat=dynamic \
  ubuntu-24.04-server-cloudimg-amd64.img disk.vhdx

In the qemu-img convert command, specify the actual format of the source file from the output of the qemu-img info command. For example, qcow2. After conversion, copy the VHDX disk to the Hyper-V node.

Create a temporary VM of the same generation that will be specified in the template.json file:

  • for Linux generation 2, use the secure boot template MicrosoftUEFICertificateAuthority;
  • if the image does not support secure boot, disable Secure Boot. In the template.json file, specify "secure_boot": "off".

Option 2: installation from ISO image

Perform the following steps:

  1. Create a temporary VM with a VHDX disk of at least 20 GB.
  2. Install the OS without a static IP address.
  3. Install cloud-init and Hyper-V integration services.
  4. Verify NoCloud data source operation.
  5. Clear the VM identifier and cloud-init state.
  6. Shut down the VM and transfer the VHDX disk to the template directory.

OS configuration

Debian or Ubuntu

Example package installation for Debian or Ubuntu
sudo apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \
  cloud-init hyperv-daemons

In Ubuntu, the data exchange service (KVP daemon) may be included in the linux-cloud-tools-virtual package. If after installing hyperv-daemons the hv-kvp-daemon service does not start, install the linux-cloud-tools-virtual and linux-cloud-tools-$(uname -r) packages:

Installing linux-cloud-tools-virtual
sudo apt-get install -y linux-cloud-tools-virtual linux-cloud-tools-$(uname -r)

Verify the actual service name:

systemctl list-unit-files | grep -Ei 'hv.*kvp|kvp.*daemon'
pgrep -a hv_kvp_daemon
More details

The required result is working cloud-init and hv_kvp_daemon.

RHEL (AlmaLinux)

sudo dnf install -y cloud-init hyperv-daemons

Required result — working cloud-init and hv_kvp_daemon.

Verifying cloud-init

cloud-init --version
cloud-init status --long
pgrep -a hv_kvp_daemon

Ensure that the file /etc/cloud/cloud-init.disabled does not exist and that the NoCloud data source is not prohibited in the datasource_list setting. S

Cleanup before publishing

Execute inside the temporary VM:

sudo cloud-init clean --logs --machine-id
sudo rm -rf /var/lib/cloud/seed/nocloud /var/lib/cloud/seed/nocloud-net
sudo rm -f /etc/ssh/ssh_host_*
sudo sync
sudo poweroff

Standard cloud-init will create new SSH host keys on first boot. If the distribution has been modified, verify this before publishing.

After shutdown:

  1. Delete the temporary VM from Hyper-V without deleting the VHDX disk.
  2. Move the disk to the template directory.

Content of Linux bootstrap disk

The seed.iso disk with label cidata is a temporary ISO image that the processing module automatically creates when deploying a VM. The seed.iso disk contains the files:

  • user-data — custom cloud-init configuration file after substituting supported tokens;
  • meta-data — unique instance identifier (instance-id) and local-hostname;

The custom user-data file must write BILLmanager.Provisioning=vm_<item> to KVP pool 1. The module waits until the VM transitions to the Running state and the readiness marker appears in the KVP pool. The wait time is up to 360 seconds. The module does not connect to the guest operating system over the network.

After successful verification, the seed.iso disk and the original bootstrap directory are deleted from the Hyper-V node.

Windows template

Supported guest systems

Windows Server 2016 and newer are recommended. The guest system must support:

  • running the system preparation process (Sysprep) and the Out-of-Box Experience wizard (OOBE);
  • built-in local Administrator account;
  • obtaining a network address via DHCP;
  • applying the unattend.xml file from an attached DVD disk.

Preparing a temporary VM

  1. Create a temporary VM of the required generation with a dynamic VHDX disk.
  2. Install Windows OS and all updates.
  3. Configure the network adapter to obtain an address via DHCP.
  4. Do not join the machine to a domain.
  5. Activate the built-in Administrator account. You can find the account by its relative identifier (RID) -500:
    $administrator = Get-LocalUser | Where-Object { $_.SID.Value -match '-500$' }
    Enable-LocalUser -InputObject $administrator
  6. Delete temporary data, auxiliary services (agents), and unique credentials.
  7. Configure automatic system partition expansion. For more details, see section Expanding C: drive on first boot.
  8. Execute the Sysprep command and wait for the VM to shut down completely. For more details, see section System preparation (Sysprep).

The temporary machine password is not used for created services. The unattend.xml file sets a new random password. Use the value $administrator.Name in the custom unattend.xml file.

Expanding C: drive on first boot

The pmhyperv module increases the VHDX disk size but does not invoke the Resize-Partition command inside the guest Windows. Add a task to the template for automatic partition expansion on startup, or leave this action to the user's discretion.

  1. Create inside the temporary VM the file C:\ProgramData\BILLmanager\Expand-SystemVolume.ps1:
    $ErrorActionPreference = 'Stop'
    $partition = Get-Partition -DriveLetter C
    $supported = Get-PartitionSupportedSize -DriveLetter C
    if ($supported.SizeMax -gt $partition.Size) {
        Resize-Partition -DriveLetter C -Size $supported.SizeMax
    }
    Unregister-ScheduledTask -TaskName 'BILLmanager Expand System Volume' `
        -Confirm:$false -ErrorAction SilentlyContinue
  2. Register the task to run as the system account:
    $action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument (
        '-NoProfile -ExecutionPolicy Bypass ' +
        '-File "C:\ProgramData\BILLmanager\Expand-SystemVolume.ps1"'
    )
    $trigger = New-ScheduledTaskTrigger -AtStartup
    Register-ScheduledTask -TaskName 'BILLmanager Expand System Volume' `
        -Action $action -Trigger $trigger -User 'SYSTEM' -RunLevel Highest -Force
  3. Test the task on a copy of the VM: after increasing the VHDX disk size, the Get-Partition -DriveLetter C command should show the new size.

System preparation (Sysprep)

Run from PowerShell or command prompt:

& "$env:WINDIR\System32\Sysprep\Sysprep.exe" `
    /generalize /oobe /shutdown /mode:vm

After executing the command, the VM will shut down. Do not start the temporary VM after shutdown: the next startup must occur on the created instance with the attached unattend.iso disk.

After shutdown, delete the temporary machine from Hyper-V without deleting the VHDX disk and move the disk to the template directory.

Content of Windows bootstrap disk

The unattend.iso disk is a temporary ISO image that the pmhyperv module automatically creates during deployment. The unattend.iso disk contains the custom unattend.xml file after token substitution. The module does not add network, DNS, or OOBE(Out-of-Box Experience) settings. The custom configuration file must ensure:

  • DHCP;
  • password installation;
  • writing BILLmanager.Provisioning=vm_<item> to the guest KVP (Key-Value Pair) branch.

The module expects the VM Running state and the readiness marker via VMbus, after which it deletes unattend.iso and the original directory.

Template publishing

  1. Ensure the source VM is shut down.
  2. Verify the VHDX disk using the Get-VHD command. The output must display actual disk data: path, VHDX format, type, size. If the command returns an error or empty result, the disk is corrupted or does not exist.
  3. Create a directory whose name matches the intname value.
  4. Copy the VHDX disk and add the template.json file.
  5. Verify JSON correctness and path conformity in the metadata.
  6. Start synchronization of processing module settings in BILLmanager.
  7. Select the appeared template (ostempl) in a test tariff plan.
  8. Create a test service in an isolated network to verify operability.
  9. Wait for the VM to transition to the Running state and for the readiness marker to appear in the KVP pool.
  10. Verify operation inside the test VM:
    Get-NetRoute -DestinationPrefix '0.0.0.0/0' |
        Select-Object InterfaceAlias, NextHop, RouteMetric
Example template verification

Template update

To update a template:

  1. Prepare a new directory with a temporary name.
  2. Fully prepare and verify the VHDX disk.
    Do not modify the VHDX disk while it is being copied to create a new VM. Modifying the source file during this operation corrupts the copy and causes VM creation errors.
  3. Shut down the source VM.
  4. Replace the template directory during a maintenance window.
  5. Resynchronize processing module settings.
  6. Create a test service to verify operability.

Changes to the template VHDX disk affect only new VMs. Previously created VMs use their own disk.vhdx file in the VM directory and do not depend on template updates.

Useful tips

Related topics: