Proxmox VE as VDI Server - PROXMOX_VDI_Launcher

cranties76

New Member
Jan 9, 2023
1
0
1
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.

screen_1.png
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
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.
 

Attachments

Thanks for your post, please email to office@proxmox.com (you need to make changes to comply with our trademark policy)
 

About

The Proxmox community has been around for many years and offers help and support for Proxmox VE, Proxmox Backup Server, and Proxmox Mail Gateway.
We think our community is one of the best thanks to people like you!

Get your subscription!

The Proxmox team works very hard to make sure you are running the best software and getting stable updates and security enhancements, as well as quick enterprise support. Tens of thousands of happy customers have a Proxmox subscription. Get yours easily in our online shop.

Buy now!