Create a Windows-based image
In Cloud Desktop, you can create desktops from pre-installed system images or your own custom images. Custom images may be Linux or Windows-based.
Creating a custom Windows image requires a Windows image licensed to you. Before installing, you should check with Microsoft to see if your license is suitable for cloud environments. The licensing relationship exists solely between you as a client and Microsoft as a license vendor. You are fully responsible for complying with the Microsoft license terms and conditions
Note
The information in this article is not legally binding and is provided for reference only.
To create an image in Cloud Desktop:
- Get your equipment ready.
- Install and configure a Windows image for your users.
- Configure your Windows image to operate in the cloud.
- Install the cloud agent.
- Optionally, install Cloudbase-Init.
- Optionally, generalize your image.
- Add the image to Compute Cloud.
- Create an image in Cloud Desktop.
Prerequisites
To use an image with a Windows OS in Cloud Desktop, you will create and configure it using the QEMU
To configure an image, you will need:
-
Computer with x86-64 CPU.
-
Linux OS When using other operating systems:
- For macOS, you can use
hvfas a QEMU accelerator. - For Windows, to work with QEMU:
- Enable virtualization in BIOS/UEFI.
- Enable Hyper-V. For server OSes, install Virtual Machine Platform.
- Use the
whpxaccelerator.
- For macOS, you can use
-
Windows installation image (ISO
file).
Warning
Make sure the OS in the image you are creating supports remote desktop connections over Remote Desktop Protocol
Installing and configuring a Windows image
-
Create a file for the boot disk image on your computer by running this command:
qemu-img create -f qcow2 image.qcow2 20480MWhere:
-
image.qcow2: Name of the boot disk image file. -
20480M: OS boot disk size in the image, MB.Note
We recommend that you specify a size of at least
16384M. Otherwise, your boot disk may not have enough space when installing or later configuring the OS.
-
-
Start a VM to install and configure Windows by running this command:
qemu-kvm \ -cpu "qemu64,hv-relaxed,hv-vapic,hv-spinlocks=0x1fff,hv-time" \ -name win-image \ -device "virtio-net,netdev=user.0" \ -netdev user,id=user.0 \ -drive "file=image.qcow2,if=virtio,cache=writeback,discard=ignore,format=qcow2" \ -drive "file=windows.iso,media=cdrom" \ -drive "file=virtio-win.iso,media=cdrom" \ -parallel none -smp "cpus=2" \ -boot "once=d" \ -machine "type=q35,accel=kvm" \ -m "4096M" \ -nic "none" \ -device qemu-xhci \ -device usb-tablet \ -vnc "0.0.0.0:85"Where:
file=image.qcow2: Path to the boot disk image file you created earlier.file=windows.iso: Path to the ISO file with the Windows installation image.file=virtio-win.iso: Path to the ISO file withWindows VirtIOdrivers.-vnc "0.0.0.0:85": Optional parameter. Use it if your QEMU build does not support graphical VM control.
Note
If configuring your image on macOS, replace
type=q35,accel=kvmwithtype=q35,accel=hvf; for Windows, usetype=q35,accel=whpx.If you need to specify the full path to the QEMU startup file, please keep in mind that the file name may vary across operating systems and QEMU builds. The most common file names are
qemu-x86_64,qemu-system-x86_64,qemu-system-x86_64w,qemu-gtk, andqemu.If your QEMU build does not support graphical VM control, you can connect to your VM over VNC
using local porttcp/5985(localhost:5985). To connect, use a VNC client of your choice, e.g.,RealVNC,Remmina, orultraVNC. In macOS, you can use its pre-installed VNC client, Screen Sharing. -
Follow the on-screen instructions to install Windows from the ISO.
When prompted to select a storage for OS installation, select Load driver to install the
virtio-storagedriver.Tip
The driver is located in the
viostordirectory on the virtual CD-ROM where the ISO file withWindows VirtIOdrivers is mounted. In theviostordirectory, select a subdirectory that corresponds to your OS version. -
Once the OS installation is complete, log in and install the required hardware drivers by running
virtio-win-guest-tools.exefrom the root of the mounted CD withWindows VirtIOdrivers. -
Make sure your OS has Remote desktop connection (RDP) installed and running.
-
Configure Windows and install software to best suit the needs of your users.
Configuring an image to work in Yandex Cloud
-
Run
cmdas an administrator. -
Activate
serial consolefor the OS bootloader.bcdedit /ems "{current}" on bcdedit /emssettings EMSPORT:2 EMSBAUDRATE:115200 -
Disable power saving settings:
powercfg -setactive "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c" powercfg -change -monitor-timeout-ac 0 powercfg -change -standby-timeout-ac 0 powercfg -change -hibernate-timeout-ac 0 -
Run
PowerShellas an administrator. -
For virtualized hardware clocks, set the time format to UTC:
#ps1 Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" -Name "RealTimeIsUniversal" -Value 1 -Type DWord -Force -
Disable automatic local IPv4 addressing (APIPA) for network interfaces that got no IP address assigned:
#ps1 Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" -Name "IPAutoconfigurationEnabled" -Value 0 -Type DWord -Force -
Allow OS shutdown if there are no active user sessions:
#ps1 Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "ShutdownWithoutLogon" -Value 1 -
Set the minimum OS shutdown warning display time when there are active user processes:
#ps1 Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" -Name "ShutdownWarningDialogTimeout" -Value 1 -
Disable automatic disk optimization (defragmentation/TRIM):
#ps1 Get-ScheduledTask -TaskName "ScheduledDefrag" | Disable-ScheduledTask -
Allow ICMPv4 traffic (if Windows Firewall is not disabled):
#ps1 Get-NetFirewallRule -Name "vm-monitoring-icmpv4" | Enable-NetFirewallRule -
Optionally, enable RDP connections requiring user authentication:
Note
The commands below enable connections to a built-in RDP server in a Windows OS. If the RDP server is unavailable in your OS, the command will return an error.
In Russian versions of Windows, the
Remote Desktopfirewall rule group may have a localized name. If the command with the-DisplayGroup "Remote Desktop"parameter fails, specify the localized group name.#ps1 Get-CimInstance -ClassName Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices | Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{ UserAuthenticationRequired = 1 } Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" -Value 0 Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
Installing the cloud agent
-
Run
PowerShellas an administrator. -
Install the cloud agent:
Invoke-WebRequest 'https://storage.yandexcloud.net/yandexcloud-vdi-agent/install.ps1' -OutFile "$($Env:temp)\install.ps1" cd $($Env:temp) Powershell.exe -ExecutionPolicy Bypass -File .\install.ps1 mkdir "C:\Program Files\Yandex.Cloud\Cloud Desktop\" cp .\desktopagentInstall\desktopagent.exe 'C:\Program Files\Yandex.Cloud\Cloud Desktop\' # Create Desktop Agent service $ServiceName = "cloud-desktop-agent" $ServicePort = 5050 $ServicePath = "C:\Program Files\Yandex.Cloud\Cloud Desktop\desktopagent.exe" $p = @{ Name = $ServiceName DisplayName = "Yandex Desktop Agent" BinaryPathName = "$ServicePath start --address :$ServicePort" Description = "Yandex Desktop Agent, automates actions from Yandex Desktop control plane service." StartupType = "Automatic" } New-Service @p & sc.exe config $ServiceName start= delayed-auto & sc.exe failure $ServiceName reset= 86400 actions= restart/1000/restart/1000/restart/1000 ### The agent startup is commented because starting the agent outside the Cloud Desktop will fail. Do not start the agent during the image preparation stage. # Start-Service $ServiceName # Create firewall rule for Desktop Agent if ($Rule = Get-NetFirewallRule -Name "DESKTOP-AGENT-HTTPS-In-TCP" -ErrorAction SilentlyContinue) { $Rule | Remove-NetFirewallRule } New-NetFirewallRule ` -Group "Yandex Desktop Agent" ` -DisplayName "Yandex Desktop Agent (HTTPS-In)" ` -Name "DESKTOP-AGENT-HTTPS-In-TCP" ` -LocalPort $ServicePort ` -Action "Allow" ` -Protocol "TCP" ` -Program "$ServicePath" -
Create a task for the VM to run correctly after it is first started:
& mkdir "C:\Scripts" & schtasks /Create /TN "SetNetSettings" /RU System /SC ONSTART /RL HIGHEST /TR "Powershell -NoProfile -ExecutionPolicy Bypass -File \`"C:\Scripts\StartupSettings.ps1`"" | Out-Null -
In the
C:\Scriptsfolder, create a file namedStartupSettings.ps1with the following contents:# Find the default gateway to route metadata service to it $output = Get-WmiObject -Class Win32_IP4RouteTable | Where-Object { $_.Destination -eq '0.0.0.0' -and $_.Mask -eq '0.0.0.0' } | Sort-Object Metric1 | Select-Object -ExpandProperty NextHop if (-not $output) { # Practically unreachable Write-Error "Default gateway was not found!" exit } # If a route for the metadata address already exists, we will replace it $routeToRemove = Get-WmiObject -Class Win32_IP4RouteTable | Where-Object { $_.Destination -eq '169.254.169.254' } | Select-Object -ExpandProperty Destination if ($routeToRemove) { Write-Output "Route for 169.254.169.254 already exists" $routeToRemove | ForEach-Object { route delete $routeToRemove } Write-Output "Deleted old route for metadata address" } route -p add 169.254.169.254 mask 255.255.255.255 $output metric 1 # Getting IPv6 Net Adapter $IPv6Adapter = Get-NetAdapter | where {$_.Linklayeraddress -like "D0-1D*"} if($IPv6Adapter) { if((Get-NetAdapterBinding -Name $IPv6Adapter.Name -ComponentID ms_tcpip).Enabled -eq "True") { $outNull = Set-NetAdapterBinding -Name $IPv6Adapter.Name -ComponentID ms_tcpip -Enabled $false -Confirm:$false Start-Sleep 5 } } if(!$(Get-Service -DisplayName "Yandex Desktop Agent") -or !$(Get-Item -Path "C:\Program Files\Yandex.Cloud\Cloud Desktop\desktopagent.exe" -ErrorAction SilentlyContinue)) { Invoke-WebRequest 'https://storage.yandexcloud.net/yandexcloud-vdi-agent/install.ps1' -OutFile "$($Env:temp)\install.ps1" cd $($Env:temp) Powershell.exe -ExecutionPolicy Bypass -File .\install.ps1 mkdir "C:\Program Files\Yandex.Cloud\Cloud Desktop\" cp .\desktopagentInstall\desktopagent.exe 'C:\Program Files\Yandex.Cloud\Cloud Desktop\' # Create Desktop Agent service $ServiceName = "cloud-desktop-agent" $ServicePort = 5050 $ServicePath = "C:\Program Files\Yandex.Cloud\Cloud Desktop\desktopagent.exe" $p = @{ Name = $ServiceName DisplayName = "Yandex Desktop Agent" BinaryPathName = "$ServicePath start --address :$ServicePort" Description = "Yandex Desktop Agent, automates actions from Yandex Desktop control plane service." StartupType = "Automatic" } New-Service @p & sc.exe config $ServiceName start= delayed-auto & sc.exe failure $ServiceName reset= 86400 actions= restart/1000/restart/1000/restart/1000 Start-Service $ServiceName # Create firewall rule for Desktop Agent if ($Rule = Get-NetFirewallRule -Name "DESKTOP-AGENT-HTTPS-In-TCP" -ErrorAction SilentlyContinue) { $Rule | Remove-NetFirewallRule } New-NetFirewallRule ` -Group "Yandex Desktop Agent" ` -DisplayName "Yandex Desktop Agent (HTTPS-In)" ` -Name "DESKTOP-AGENT-HTTPS-In-TCP" ` -LocalPort $ServicePort ` -Action "Allow" ` -Protocol "TCP" ` -Program "$ServicePath" } # Stores Windows RE (WinRE) configuration info $nfo string [string]$nfo = reagentc /info # Checks if Windows RE (WinRE) is enabled and extracts its disk/partition info # If enabled, disables WinRE via reagentc and deletes its partition using DiskPart if($nfo -match ".*Windows RE status:.*Enabled.*"){ # Locate the disk number it is on $nfo -match ".*Windows RE location.*harddisk(\d+)" | Out-Null $disk = $Matches[1] # Locate the partition it is on $nfo -match ".*Windows RE location.*partition(\d+)" | Out-Null $partition = $Matches[1] $WinREInfo = New-Object -TypeName psobject -Property $([ordered]@{Enabled='True';Disk=$disk;Partition=$partition;Resizable=(((Get-Disk -Number $disk | Get-Partition).PartitionNumber | Measure-Object -Maximum).Maximum -eq $partition);CurrentSize=([string]((Get-Disk -Number $disk | Get-Partition | Where-Object PartitionNumber -eq $partition).Size / 1MB) +'MB');A1_Key=[System.GUID]::NewGuid()}) } else { $WinREInfo = New-Object -TypeName psobject -Property $([ordered]@{Enabled='False';Disk='N/A';Partition='N/A';Resizable='N/A';CurrentSize='N/A';A1_Key=[System.GUID]::NewGuid()}) } if($WinREInfo.Enabled -eq "True" -and $WinREInfo.Partition -ne "N/A") { & reagentc /disable $outScript = "select disk $($WinREInfo.Disk) select partition $($WinREInfo.Partition) delete partition override exit " $outScript | Out-File C:\Scripts\winre.txt -Encoding ascii & diskpart /s C:\Scripts\winre.txt $outNull = Remove-Item "C:\Scripts\winre.txt" -Force -Confirm:$false } # Force extend partition $DiskSpace = Get-PartitionSupportedSize -DriveLetter "C" if((Get-Partition -DriveLetter "C").Size -lt $DiskSpace.SizeMax) { $outNull = Resize-Partition -DriveLetter "C" -Size $DiskSpace.SizeMax } # Format RAW disks $RAWDisks = Get-Disk | where {$_.PartitionStyle -eq "RAW"} if($RAWDisks) { foreach($RAWDisk in $RAWDisks) { $outNull = Initialize-Disk -Number $RAWDisk.Number -PartitionStyle GPT -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel "UserData" -Confirm:$false } } if ($LASTEXITCODE -eq 0) { Write-Output "Successfully create route for metadata service" } else { Write-Error "Error on creating route for metadata service! Exit code: $LASTEXITCODE" }The above script contains settings for startup and operation of the Cloud Desktop cloud agent’s system service as well as settings of the network route to the metadata service available from inside the VM.
Warning
The metadata service is available at
http://169.254.169.254.Do not restrict network access to this address from your OS. Cloud Desktop requires access to VM metadata to work correctly.
Installing Cloudbase-Init
You can install Cloudbase-Init
To install Cloudbase-Init, run the following commands in PowerShell as an administrator:
# Install Cloudbase-Init
$WorkDirectory = "C:\Scripts"
$outNull = Start-BITSTransfer -Source "https://www.cloudbase.it/downloads/CloudbaseInitSetup_Stable_x64.msi" -Destination $WorkDirectory
$outNull = Start-Process -FilePath 'msiexec.exe' -ArgumentList "/i $WorkDirectory\CloudbaseInitSetup_Stable_x64.msi /qn" -Wait
Start-Sleep 60
$outScript = "[DEFAULT]
username=Admin
groups=Administrators
inject_user_password=true
config_drive_raw_hhd=true
config_drive_cdrom=true
config_drive_vfat=true
bsdtar_path=C:\Program Files\Cloudbase Solutions\Cloudbase-Init\bin\bsdtar.exe
mtools_path=C:\Program Files\Cloudbase Solutions\Cloudbase-Init\bin\
verbose=true
debug=true
logdir=C:\Program Files\Cloudbase Solutions\Cloudbase-Init\log\
logfile=cloudbase-init-unattend.log
default_log_levels=comtypes=INFO,suds=INFO,iso8601=WARN,requests=WARN
logging_serial_port_settings=COM1,115200,N,8
mtu_use_dhcp_config=true
ntp_use_dhcp_config=true
local_scripts_path=C:\Program Files\Cloudbase Solutions\Cloudbase-Init\LocalScripts\
metadata_services=cloudbaseinit.metadata.services.ec2service.EC2Service
plugins=cloudbaseinit.plugins.common.mtu.MTUPlugin,cloudbaseinit.plugins.common.sethostname.SetHostNamePlugin,cloudbaseinit.plugins.windows.extendvolumes.ExtendVolumesPlugin,cloudbaseinit.plugins.common.userdata.UserDataPlugin
allow_reboot=false
stop_service_on_exit=false
check_latest_version=false"
$outScript | Out-File -FilePath "C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf\cloudbase-init.conf" -Confirm:$false -Force
$outScript | Out-File -FilePath "C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf\cloudbase-init-unattend.conf" -Confirm:$false -Force
Finalizing the image
After you are done with configuration, we recommend you to generalize your imageSysrep utility. This will prepare Windows for cloning and for later use on other computers.
To generalize the image, start PowerShell as an administrator and run the following commands:
# Global vars
$WorkDirectory = "C:\sysprep"
# Download the unattend.xml file for Sysprep
New-Item -Path $WorkDirectory -ItemType Directory
Start-BitsTransfer https://storage.yandexcloud.net/cloudbase/sysprepunattend-cloudbase-init.xml -Destination $WorkDirectory\unattend.xml
# Start Sysprep
& $env:SystemRoot\System32\Sysprep\Sysprep.exe /oobe /generalize /quiet /quit /unattend:"$WorkDirectory\unattend.xml"
# Wait for correct system state
do {
Start-Sleep -s 5
$SetupState = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\State"
$ImageState = $SetupState | Select-Object -ExpandProperty ImageState
} while ($ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE')
Remove-Item $WorkDirectory -Recurse -Force
Remove-Item "C:\Windows\Setup\Scripts\*" -Force -ErrorAction SilentlyContinue
# Wait for Sysprep tag
while (-not (Test-Path 'C:\Windows\System32\Sysprep\Sysprep_succeeded.tag') ) {
Start-Sleep -s 1
}
Stop-Computer -Force
Upon completion, the script’s final command will stop the VM and save the VM boot image locally to the previously created image.qcow2 file, in QCOW2
Adding an image to Compute Cloud
-
Upload the image to Yandex Object Storage.
-
Get a link to the uploaded image.
-
Import the image to Yandex Compute Cloud.
If using the Yandex Cloud CLI, you can import the image by running the following command and specifying the link you got earlier:
Yandex Cloud CLIyc compute image create \ --name <image_name> \ --description <image_description> \ --os-type windows \ --source-uri "<link_to_image_in_Object_Storage>"
Adding an image to Cloud Desktop
- In the management console
, navigate to the folder where you want to create your image. - Go to Cloud Desktop.
- In the left-hand panel, select
Images.
- Click Add image.
- In the Image source field, select
Compute Cloud. - In the Image in Compute Cloud field, select the image you added earlier.
- Specify a name for the new image.
- Click Add.
Once the image is created, you can use it as a boot disk image for desktop groups.
Resizing boot disk without Cloudbase-Init
If you are not using Cloudbase-Init and you want to resize the boot disk in a desktop group:
- Increase the boot disk file system size on your desktop, e.g., using the
diskmgmt.mscsnap-in. - Create a new Cloud Desktop image from that desktop.
- Use the new image as a boot disk image for desktop groups.