Welcome,
I write a simple code that use Proxmox API for use as VDI Server for Windows VM.
First i create a VM with windows 11 and add the machine to main Master Domain Server and install all software that i need as Administrator for All User. I leave the ethernet adapter in DHCP.
Second i convert the VM as a Template.
In any Windows PC of my domain i have installed my tool. When i launch this tool i can press a button and i a few second i have my VDI ready and connected.
My tool connect with Proxmox API, search in a range a number free for a new VM and clone the template. Run the VM and connct with my credential to the Domain, so i have all software preinstalled and ready and my home shared with domain for save document and all shared active directory from Master Domain. When you close the Connection to VM, it automatically stop VM in Proxmox and delete IT.
I write my tool with AUTOIT, is simple and intuitive. It have a graphic interface for configuration and save the config in a ini file for autoload on next startup.
It need "curl.exe" and "libcurl.dll" in the same folder of the files attached to this thread to compile. This can be download from https://curl.se/windows/latest.cgi?p=win32-mingw.zip
Also need AUTOIT to open .au3 file and compile it to .exe and can be download from https://www.autoitscript.com/site/autoit-script-editor/downloads/
Simple open PROXMOX_VDI_Launcher.au3 with AUTOIT Editor and hit F7, it will create the executable file. Remember that curl and libcurl must exist in the same folder of .au3 file.
This is my tool in action, easy to configure and use. In this example with this configuration i give a slot of 5 VDI running simultaneously (801-805) and my template ID of the windows 11 client is 901.
This is my code of "PROXMOX_VDI_Launcher.au3"
Sorry for my bad english. This is a first release of this tool.
I hope it helps someone because I wasted some time looking for how to create VDI with proxmox and in the end I decided to write this tool.
I could not attach the executable of the tool directly in this thread because the size of the zip file is almost 6 MB. If someone fails or has problems compiling it, write me that maybe I'll put an external link on the drive where you can download it.
I write a simple code that use Proxmox API for use as VDI Server for Windows VM.
First i create a VM with windows 11 and add the machine to main Master Domain Server and install all software that i need as Administrator for All User. I leave the ethernet adapter in DHCP.
Second i convert the VM as a Template.
In any Windows PC of my domain i have installed my tool. When i launch this tool i can press a button and i a few second i have my VDI ready and connected.
My tool connect with Proxmox API, search in a range a number free for a new VM and clone the template. Run the VM and connct with my credential to the Domain, so i have all software preinstalled and ready and my home shared with domain for save document and all shared active directory from Master Domain. When you close the Connection to VM, it automatically stop VM in Proxmox and delete IT.
I write my tool with AUTOIT, is simple and intuitive. It have a graphic interface for configuration and save the config in a ini file for autoload on next startup.
It need "curl.exe" and "libcurl.dll" in the same folder of the files attached to this thread to compile. This can be download from https://curl.se/windows/latest.cgi?p=win32-mingw.zip
Also need AUTOIT to open .au3 file and compile it to .exe and can be download from https://www.autoitscript.com/site/autoit-script-editor/downloads/
Simple open PROXMOX_VDI_Launcher.au3 with AUTOIT Editor and hit F7, it will create the executable file. Remember that curl and libcurl must exist in the same folder of .au3 file.
This is my tool in action, easy to configure and use. In this example with this configuration i give a slot of 5 VDI running simultaneously (801-805) and my template ID of the windows 11 client is 901.
This is my code of "PROXMOX_VDI_Launcher.au3"
Code:
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Icon=PROXMOX_VDI_Launcher.ico
#AutoIt3Wrapper_Outfile_x64=PROXMOX_VDI_Launcher.Exe
#AutoIt3Wrapper_Compression=4
#AutoIt3Wrapper_Run_Tidy=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <File.au3>
#include <Array.au3>
#include <String.au3>
#include <WinAPIFiles.au3>
#include <WinNet.au3>
#include <Constants.au3>
#include <FileConstants.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <AutoItConstants.au3>
#include <InetConstants.au3>
#include <MsgBoxConstants.au3>
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
Global $CFG_SERVER = "192.168.1.235"
Global $CFG_USER = "root@pam"
Global $CFG_PASSWORD = "password"
Global $CFG_VMID = 901
Global $CFG_VDI_START = 801
Global $CFG_VDI_END = 805
Global $CFG_NODE = "prx"
Global $CFG_VDI_NET = ""
Global $CFG_VDI_IP = ""
Global $CFG_TICKET = ""
Global $CFG_TOKEN = ""
Global $CFG_STATUS = ""
Global $CFG_VDI_TOT = $CFG_VDI_END - $CFG_VDI_START
Global $CFG_VDI_SEL = $CFG_VMID
Global $CFG_VDI_ARR[$CFG_VDI_TOT + 1]
If Not FileExists(@ScriptDir & "\proxmox_logo.jpg") Then
FileInstall(".\curl.exe", @ScriptDir & "\proxmox_logo.jpg", 1)
EndIf
If Not FileExists(@ScriptDir & "\curl.exe") Then
FileInstall(".\curl.exe", @ScriptDir & "\curl.exe", 1)
EndIf
If Not FileExists(@ScriptDir & "\libcurl.dll") Then
FileInstall(".\libcurl.dll", @ScriptDir & "\libcurl.dll", 1)
EndIf
If FileExists(@ScriptDir & "\config.ini") Then
$CFG_SERVER = IniRead(@ScriptDir & "\config.ini", "config", "server_ip", "127.0.0.1")
$CFG_USER = IniRead(@ScriptDir & "\config.ini", "config", "username", "user")
$CFG_PASSWORD = IniRead(@ScriptDir & "\config.ini", "config", "password", "password")
$CFG_NODE = IniRead(@ScriptDir & "\config.ini", "config", "node", "prx01")
$CFG_VMID = IniRead(@ScriptDir & "\config.ini", "config", "vmid_template", "901")
$CFG_VDI_START = IniRead(@ScriptDir & "\config.ini", "config", "vmid_start", "801")
$CFG_VDI_END = IniRead(@ScriptDir & "\config.ini", "config", "vmid_end", "805")
EndIf
Opt("GUIOnEventMode", 1)
$Form1_1 = GUICreate("PROXMOX VDI Launcher", 577, 278, -1, -1)
GUISetOnEvent($GUI_EVENT_CLOSE, "ExitProg")
$Pic1 = GUICtrlCreatePic(@ScriptDir & "\proxmox_logo.jpg", 96, 8, 380, 60)
$Graphic1 = GUICtrlCreateGraphic(0, 0, 576, 82)
GUICtrlSetColor($Graphic1, 0xFFFFFF)
GUICtrlSetBkColor($Graphic1, 0xFFFFFF)
GUICtrlSetGraphic($Graphic1, $GUI_GR_COLOR, 0x000000, 0xFFFFFF)
GUICtrlSetGraphic($Graphic1, $GUI_GR_RECT, 0, 0, 576, 82)
$INP_SERVER = GUICtrlCreateInput($CFG_SERVER, 104, 96, 121, 21)
$Label1 = GUICtrlCreateLabel("IP Server ProxMox", 8, 99, 92, 17)
$INP_USER = GUICtrlCreateInput($CFG_USER, 104, 128, 121, 21)
$Label2 = GUICtrlCreateLabel("Username", 8, 131, 52, 17)
$Label3 = GUICtrlCreateLabel("Password", 8, 163, 50, 17)
$INP_PASSWORD = GUICtrlCreateInput($CFG_PASSWORD, 104, 160, 121, 21)
$Label4 = GUICtrlCreateLabel("Node Name", 8, 195, 61, 17)
$INP_NODE = GUICtrlCreateInput($CFG_NODE, 104, 192, 121, 21)
$Label5 = GUICtrlCreateLabel("VMID Template", 328, 99, 78, 17)
$Label6 = GUICtrlCreateLabel("VMID Pool Start", 328, 131, 80, 17)
$Label7 = GUICtrlCreateLabel("VMID Pool End", 328, 163, 77, 17)
$INP_VMID = GUICtrlCreateInput($CFG_VMID, 440, 96, 121, 21)
$INP_VDI_START = GUICtrlCreateInput($CFG_VDI_START, 440, 128, 121, 21)
$INP_VDI_END = GUICtrlCreateInput($CFG_VDI_END, 440, 160, 121, 21)
$Btn_Save = GUICtrlCreateButton("Save Configuration", 328, 192, 235, 25)
GUICtrlSetOnEvent($Btn_Save, "Btn_SaveClick")
$Btn_Start = GUICtrlCreateButton("LAUNCH VDI", 8, 224, 555, 41)
GUICtrlSetOnEvent($Btn_Start, "Btn_StartClick")
GUISetState(@SW_SHOW)
While 1
Sleep(100)
WEnd
Func Btn_SaveClick()
$CFG_SERVER = GUICtrlRead($INP_SERVER)
$CFG_USER = GUICtrlRead($INP_USER)
$CFG_PASSWORD = GUICtrlRead($INP_PASSWORD)
$CFG_VMID = GUICtrlRead($INP_VMID)
$CFG_VDI_START = GUICtrlRead($INP_VDI_START)
$CFG_VDI_END = GUICtrlRead($INP_VDI_END)
$CFG_NODE = GUICtrlRead($INP_NODE)
IniWrite(@ScriptDir & "\config.ini", "config", "server_ip", $CFG_SERVER)
IniWrite(@ScriptDir & "\config.ini", "config", "username", $CFG_USER)
IniWrite(@ScriptDir & "\config.ini", "config", "password", $CFG_PASSWORD)
IniWrite(@ScriptDir & "\config.ini", "config", "node", $CFG_NODE)
IniWrite(@ScriptDir & "\config.ini", "config", "vmid_template", $CFG_VMID)
IniWrite(@ScriptDir & "\config.ini", "config", "vmid_start", $CFG_VDI_START)
IniWrite(@ScriptDir & "\config.ini", "config", "vmid_end", $CFG_VDI_END)
EndFunc ;==>Btn_SaveClick
Func Btn_StartClick()
GUISetState(@SW_HIDE)
SplashImageOn("Wait ProxMox VDI Cloning and Launching...", @ScriptDir & "\proxmox_logo.jpg", "400", "60", "-1", "-1", 1)
Btn_SaveClick()
Local $IpMask = _StringExplode($CFG_SERVER, ".")
$CFG_VDI_NET = $IpMask[0] & "." & $IpMask[1] & "." & $IpMask[2] & "."
Local $vdi = "["
For $i = 0 To $CFG_VDI_TOT Step 1
$CFG_VDI_ARR[$i] = $CFG_VDI_START + $i
Next
Local $sExecute = 'curl.exe -f -s -S -k --insecure --data-urlencode "username=' & $CFG_USER & '" --data-urlencode "password=' & $CFG_PASSWORD & '" "https://' & $CFG_SERVER & ':8006/api2/json/access/ticket"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
Local $sResponse = ''
While 1
$sResponse &= StdoutRead($sOutput)
If @error Then
ExitLoop
EndIf
WEnd
Local $aArray1 = StringRegExp($sResponse, '"([^"]*)":"([^"]*)"', 3)
Local $aFinal1 = _Array_Resize($aArray1, 2)
For $i = 0 To 3 Step 1
If $aFinal1[$i][0] == "ticket" Then $CFG_TICKET = $aFinal1[$i][1]
If $aFinal1[$i][0] == "CSRFPreventionToken" Then $CFG_TOKEN = $aFinal1[$i][1]
Next
Local $sExecute = 'curl.exe -f -s -S -k --insecure -b "PVEAuthCookie=' & $CFG_TICKET & '" -H "CSRFPreventionToken: ' & $CFG_TOKEN & '" "https://' & $CFG_SERVER & ':8006/api2/json/nodes/' & $CFG_NODE & '/qemu"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
Local $sResponse = ''
While 1
$sResponse &= StdoutRead($sOutput)
If @error Then
ExitLoop
EndIf
WEnd
Local $aArray2 = StringRegExp($sResponse, '"vmid":(...)', 3)
For $i = 0 To UBound($aArray2) - 1
If _ArraySearch($CFG_VDI_ARR, $aArray2[$i]) > -1 Then
_ArrayDelete($CFG_VDI_ARR, _ArraySearch($CFG_VDI_ARR, $aArray2[$i]))
EndIf
Next
If UBound($CFG_VDI_ARR) < 1 Then
SplashOff()
MsgBox(48, "ERROR", "Not Free VDI ID For Clone VM.")
ExitProg()
EndIf
$CFG_VDI_SEL = $CFG_VDI_ARR[0]
Local $sExecute = 'curl.exe -f -s -S -k --insecure -b "PVEAuthCookie=' & $CFG_TICKET & '" -H "CSRFPreventionToken: ' & $CFG_TOKEN & '" -X POST --data-binary "newid=' & $CFG_VDI_SEL & '&target=' & $CFG_NODE & '&name=VDI-' & $CFG_VDI_SEL & '" "https://' & $CFG_SERVER & ':8006/api2/json/nodes/' & $CFG_NODE & '/qemu/' & $CFG_VMID & '/clone"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
Local $sResponse = ''
While 1
$sResponse &= StdoutRead($sOutput)
If @error Then
ExitLoop
EndIf
WEnd
$CFG_STATUS = "ERROR"
Local $Retry = 0
While $CFG_STATUS == "ERROR" And $Retry < 15
Sleep(1000)
Local $sExecute = 'curl.exe -f -s -S -k --insecure -b "PVEAuthCookie=' & $CFG_TICKET & '" -H "CSRFPreventionToken: ' & $CFG_TOKEN & '" "https://' & $CFG_SERVER & ':8006/api2/json/nodes/' & $CFG_NODE & '/qemu/' & $CFG_VDI_SEL & '/status/current"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
Local $sResponse = ''
While 1
$sResponse &= StdoutRead($sOutput)
If @error Then
ExitLoop
EndIf
WEnd
If StringInStr($sResponse, "stopped") > 0 Then $CFG_STATUS = "stopped"
If StringInStr($sResponse, "running") > 0 Then $CFG_STATUS = "running"
If StringInStr($sResponse, "suspend") > 0 Then $CFG_STATUS = "suspend"
$Retry += 1
WEnd
If $CFG_STATUS == "ERROR" Then
SplashOff()
MsgBox(48, "ERROR", "VM Template NOT Cloned.")
ExitProg()
EndIf
If $CFG_STATUS == "stopped" Then
Local $sExecute = 'curl.exe -f -s -S -k --insecure -b "PVEAuthCookie=' & $CFG_TICKET & '" -H "CSRFPreventionToken: ' & $CFG_TOKEN & '" -X POST "https://' & $CFG_SERVER & ':8006/api2/json/nodes/' & $CFG_NODE & '/qemu/' & $CFG_VDI_SEL & '/status/start"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
EndIf
If $CFG_STATUS == "suspend" Then
Local $sExecute = 'curl.exe -f -s -S -k --insecure -b "PVEAuthCookie=' & $CFG_TICKET & '" -H "CSRFPreventionToken: ' & $CFG_TOKEN & '" -X POST "https://' & $CFG_SERVER & ':8006/api2/json/nodes/' & $CFG_NODE & '/qemu/' & $CFG_VDI_SEL & '/status/resume"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
EndIf
While $CFG_STATUS <> "running"
Local $sExecute = 'curl.exe -f -s -S -k --insecure -b "PVEAuthCookie=' & $CFG_TICKET & '" -H "CSRFPreventionToken: ' & $CFG_TOKEN & '" "https://' & $CFG_SERVER & ':8006/api2/json/nodes/' & $CFG_NODE & '/qemu/' & $CFG_VDI_SEL & '/status/current"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
Local $sResponse = ''
While 1
$sResponse &= StdoutRead($sOutput)
If @error Then
ExitLoop
EndIf
WEnd
$CFG_STATUS = "stopped"
If StringInStr($sResponse, "running") > 0 Then $CFG_STATUS = "running"
WEnd
$CFG_VDI_IP = ""
Local $Retry = 0
While StringLen($CFG_VDI_IP) < 7 And $Retry < 15
Sleep(2000)
Local $sExecute = 'curl.exe -f -s -S -k --insecure -b "PVEAuthCookie=' & $CFG_TICKET & '" -H "CSRFPreventionToken: ' & $CFG_TOKEN & '" "https://' & $CFG_SERVER & ':8006/api2/json/nodes/' & $CFG_NODE & '/qemu/' & $CFG_VDI_SEL & '/agent/network-get-interfaces"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
Local $sResponse = ''
While 1
$sResponse &= StdoutRead($sOutput)
If @error Then
ExitLoop
EndIf
WEnd
Local $aArray3 = StringRegExp($sResponse, '"ip-address":"((?:25[0-5]|2[0-4]\d|[0-1]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[0-1]?\d{1,2})){3})"', 3)
For $i = 0 To UBound($aArray3) - 1
If StringInStr($aArray3[$i], $CFG_VDI_NET) > 0 Then $CFG_VDI_IP = $aArray3[$i]
Next
$Retry += 1
WEnd
If StringLen($CFG_VDI_IP) < 7 Then
SplashOff()
MsgBox(48, "ERROR", "Impossible Detect VM IP Addres.")
ExitProg()
EndIf
SplashOff()
RunWait("mstsc /v:" & $CFG_VDI_IP & " /f /prompt", @ScriptDir, @SW_SHOW)
Sleep(5000)
While ProcessExists("mstsc.exe")
Sleep(500)
WEnd
SplashImageOn("Wait ProxMox VDI Stop and Delete...", @ScriptDir & "\proxmox_logo.jpg", "400", "60", "-1", "-1", 1)
Local $sExecute = 'curl.exe -f -s -S -k --insecure -b "PVEAuthCookie=' & $CFG_TICKET & '" -H "CSRFPreventionToken: ' & $CFG_TOKEN & '" -X POST "https://' & $CFG_SERVER & ':8006/api2/json/nodes/' & $CFG_NODE & '/qemu/' & $CFG_VDI_SEL & '/status/stop"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
While $CFG_STATUS <> "stopped"
Local $sExecute = 'curl.exe -f -s -S -k --insecure -b "PVEAuthCookie=' & $CFG_TICKET & '" -H "CSRFPreventionToken: ' & $CFG_TOKEN & '" "https://' & $CFG_SERVER & ':8006/api2/json/nodes/' & $CFG_NODE & '/qemu/' & $CFG_VDI_SEL & '/status/current"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
Local $sResponse = ''
While 1
$sResponse &= StdoutRead($sOutput)
If @error Then
ExitLoop
EndIf
WEnd
$CFG_STATUS = "running"
If StringInStr($sResponse, "stopped") > 0 Then $CFG_STATUS = "stopped"
WEnd
Local $sExecute = 'curl.exe -f -s -S -k --insecure -b "PVEAuthCookie=' & $CFG_TICKET & '" -H "CSRFPreventionToken: ' & $CFG_TOKEN & '" -X DELETE "https://' & $CFG_SERVER & ':8006/api2/json/nodes/' & $CFG_NODE & '/qemu/' & $CFG_VDI_SEL & '"'
Local $sOutput = Run($sExecute, @TempDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
SplashOff()
ExitProg()
EndFunc ;==>Btn_StartClick
Func ExitProg()
Exit (0)
EndFunc ;==>ExitProg
Func _Array_Resize(ByRef $aArray1d, $iColumns, $iStart = Default)
If IsKeyword($iStart) Then $iStart = 0
Local $iElements = UBound($aArray1d) - $iStart
If Mod($iElements, $iColumns) <> 0 Then Return SetError(1, 0, False)
Local $aArray2d[$iElements / $iColumns][$iColumns]
For $i = 0 To $iElements - 1
$aArray2d[Floor($i / $iColumns)][Mod($i, $iColumns)] = $aArray1d[$i + $iStart]
Next
Return $aArray2d
EndFunc ;==>_Array_Resize
I hope it helps someone because I wasted some time looking for how to create VDI with proxmox and in the end I decided to write this tool.
I could not attach the executable of the tool directly in this thread because the size of the zip file is almost 6 MB. If someone fails or has problems compiling it, write me that maybe I'll put an external link on the drive where you can download it.