While the transition to the Cloud is really starting also the move to the Citrix Cloud for the CVAD Control Plane components is becoming reality. Some organizations will make move the VDIs/Multi-session hosts to a Cloud platform as well, but there will be also a group that will keep the VDIs/Multi-session hosts still within their own on-prem datacenters. When using persistent VDIs (without Citrix provisioning techniques) one of the logical requirements would be that the user will be able to connect to his own VDI after the move to CVAD service. In this article I will discuss the possibilities to arrange this movement with preserving the current assignments.

Possibility 1: Citrix Tooling

The first possibility is using the Citrix tool called Automated Configuration for Virtual Apps and Desktops. This tool (based on PowerShell) is provided by Citrix to migrate your complete configuration from on-prem to Citrix Cloud, but can also be used for moving from cloud to cloud. New versions are released regularly with enhanced functionality. In basic you export the full configuration from your on-prem and imported into your Citrix Cloud environment. In the provided documentation (available at https://docs.citrix.com/en-us/citrix-virtual-apps-desktops-service/migrate.html) there are suggestions that you can specify per component what to export and components can be specified by name while importing in Citrix Cloud. It’s a pity that just two small examples are mentioned in the steps and I could not find any detailed information on such parameters provided.

First I think it’s really valuable that Citrix created a tool for this as manually editing all this information by hand is a time-consuming task. However often such a migration is the moment to start clean and define new configurations based on current practices, new naming conventions and so on. I personally did not used this tooling, but you should definitely consider it when you are migration to the cloud and check if is offers added-value in your use case.

Possibility 2: Create your own scripts

There are all kind of scenario’s where the Citrix Automated Configuration does not fit into your migration strategy. You can think of switching to another provisioning technique in the migration, adding another (Citrix) functionality as two examples where migrating is actually rebuilding the Citrix platform. A scenario where rebuild is not in the scope but maybe the Automation Tool does not fit is a scenario, I encountered where an approach migration method was preferred and the method required more additional steps in the automated process (like changing the VDA configuration). In this case we are talking around 5000 persistent VDIs, where we want to keep the current VDIs and assignments. For this scenario we defined roughly the following steps:

  • Hosting Connections, Machine Catalogs and Delivery Groups are configured up-front. As this was done by another team we did not have to automate that our selves. If you want to this check the articles by Aron Parker to get up to speed with this part (those are for on-prem but are 98% re-usable for Citrix Cloud à https://stealthpuppy.com/xendesktop-delivery-group-powershell/ and https://stealthpuppy.com/xendesktop-mcs-machine-catalog-powershell/).
  • Within our automation framework a Citrix PowerShell script will be need to be added that exports a specific Delivery Group with the VDIs and their assigned user, where the delivery group name will be provided. I will go into more details into this script in this article.
  • The configuration of the VDIs need to be changed to point to Citrix Cloud Connectors instead of Citrix Delivery Controllers. As this is part of the existing Infrastructure as Code and therefore not easy to fit in this article, I will want go into details on this part.
  • A script that import the VDIs with their assignment into the already created Machine Catalog and Delivery Group in Citrix Cloud. I will also go into more detail on this script as well.
  • These steps will be repeated for each Delivery Group separately

Export VDI with assignment per Delivery Group

The first part is getting the current VDIs and their assignments from the on-prem Citrix environment into a file that can be used for the import. The biggest challenge there is to remove the unneeded information from the VDI name and username. I used some basic trim functionality to accomplish that so only the actual computer name and username are added to the file. The script requires the actual Delivery Group name as a parameter. Further you need to declare some variables of your own environment like a Delivery Controller and your DNS suffix.

The script checks if the specified Delivery Group exists, followed by getting the DNSName (computer name), Associated User Name and (for verification) the Delivery Group and after trimming the information will be written down in a file.

############################################################################################## 
# Scriptusage: Export Delivery Group to CSV file with machine name + username –
# PowerShell Script to create a list of VDI with assigned user from specified Delivery Group
# Author: Wilco van Bragt
# File: ExportDeliveryGrouptoMachineUsers.ps1
# Version: 0.2
# Date last adjusted: 3 August 2021
# Adjusted by: Wilco van Bragt
#############################################################################################
# Execution: ./ExportDeliveryGrouptoMachineUsers.ps1 <<NAMEofDeliveryGrouptoExport>>
##############################################################################################

#Declare Variabeles
$DeliveryController="<<NAME of DELIVERY CONTROLLER>>”

#Load Citrix modules
add-pssnapin citrix*

#Fetch the Delivery Group to be queried from argument specified at command line
$DeliveryGroup=$args[0]
write-host Delivery Group: $DeliveryGroup will be queried, first check if Delivery Group actually exists

#Check if the specified Delivery Group exists on the on-premises Citrix back-end
$DeliveryGroupExists=Get-BrokerDesktopGroup $DeliveryGroup
if (!$DeliveryGroupExists)
    {write-host Specified Delivery Group: $DeliveryGroup does not exist, please provide an existing Delivery Group}

 #Fetch from specified Delivery Group VDI names, user names including the Delivery Group name
$result=Get-BrokerMachine  -AdminAddress "$DeliveryController:80" -Filter {(DesktopGroupName -eq $DeliveryGroup)} -MaxRecordCount 5000| Select-Object -Property "DNSName","AssociatedUserNames","DesktopGroupName"

# For each item trim the VDAname to computername only and username to domain\username
foreach ($objitem in $result)
    {
        $VDIName=$objitem.DNSNAME
        $VDINameTrim=$VDIName.TrimEnd("<<DNSSUFFIX>>")   

        if ($objitem.AssociatedUserNames)
            { $Username=$objitem.AssociatedUserNames
              $UsernameTrim=$Username.Trim("{","}")
            }

        $DeliveryGroupName=$objitem.DesktopGroupName
        $endresult="$VDINameTrim,$UsernameTrim,$DeliveryGroupName"

       #Place result in textfile for importing into Citrix Cloud
        Out-File "C:\VDITeam\VDItoUser-$DeliveryGroupName.csv" -InputObject $endresult -Append       

        #Clear Variabeles so they won't be re-used in the foreach loop  

        Clear-Variable -Name "VDIName" 
        Clear-Variable -Name "Username"
        Clear-Variable -Name "UsernameTrim"
        Clear-Variable -Name "VDINameTrim"
        Clear-Variable -Name "endresult"
        Clear-Variable -Name "DeliveryGroupName"
    }


Import VDI to Citrix Cloud per Delivery Group

Logically the exported file will be used as the source for importing the VDIs with their assignment into Citrix Cloud. Logically this should run on a system with access to the Internet, Citrix Remote PowerShell and the API access is configured on Citrix Cloud and the system.

The difficult part is that you need the path where the VM is located on the hypervisor. This path is based on the names within the hypervisor cluster and datastore. Most easy is to use cd XDHyp: to get the path and define that as a variable. Secondly you need the hypervisor connection uid, but than can be easily queried using Citrix cmdlets. In this environment we have several vCenters, that’s why it’s input of the script, if you only have one you can declare it as a variable.

Another thing that is not really easy is that both the pre-Windows 2000 domain name as the modern domain name is being used. In some organization (like in my example) this names differs and you need to define the correct one for that specific component.  Check on <<value>> parts in the script to adjust those with information from your own environment.

Last thing than can be the case is that multiple users are assigned to the same VDI. In that case the catched information from the export needs to be split again. I did this pretty basic and know that this can be done better (without a hard limit) but did not took that part of the script in my own (test)environment.

The script requires input both the location of the importfile, the hypervisorname defined in Citrix Cloud and the Delivery Group Name where the VDIs need to be added to. This could be a read out of the export file if the name stays the same, however in this case a naming convention change was done on the Citrix Cloud Delivery Groups. Also the script is based that the Delivery Group and Machine Catalog have exactly the same name. If this is not the case at your side you need to make some adjustments (by exporting also the Machine Catalog or specify it as an argument of variable) to the script.

##############################################################################################
# Scriptusage: Import VDI with assigned user to Machine Catalog and Delivery Group within Citrix Cloud (CVAD Service)
# Author: Wilco van Bragt
# File: ImportOnPremtoCitrixCloud.ps1
# Version: 0.1
# Date last adjusted: 3 August 2021
# Adjusted by: Wilco van Bragt
##############################################################################################
# Execution: ./ImportOnPremtoCitrixCloud.ps1 <<LocationImportfile>> <<HypervisornameinCitrixCloud>>
##############################################################################################
# Requirements:
#     1) Access to the Internet (via Proxy)
#     2) API access credentials are set on system were the script is executed
#     3) Delivery Group and Machine Catalog are already created
##############################################################################################

## Logon to Proxy server via Windows Credentials
$Wcl = new-object System.Net.WebClient
$Wcl.Headers.Add(“user-agent”, “PowerShell Script”)
$Wcl.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials

# To determine the machine id we need to have the path where the VDIs are hosted on the hypervisor. As this is per cluster different, define the correct information in below variable.
$VDIDataCenter="XDHyp:\Connections\<<VMware cluster+datastore information>>"

#Load Citrix modules
add-pssnapin citrix*

## Logon to Citrix Cloud via the API.
## Authentication Profile have been previously created via
## Set-XDCredentials command
Get-XDAuthentication -ProfileName "<<NAMEPROFILE set at Set-XDCredentials"

#Check if sourcefile exists
$Sourcefile=$args[0]

write-host Sourcefile: $Sourcefile will be used, first check if file actually exists. Message will be shown if not found

if (-not(Test-Path -Path $Sourcefile -PathType Leaf))
    {Write-Host $Sourcefile loction not found, please provide a valid path
    }

#Read content from sourcefile
$importdata = Get-Content "$Sourcefile"

#Determine HyperVisorConnectionUID for adding machines to Machine Catalog
$HypervisorConnectionCitrixCloud=$args[1]
Write-Host $HypervisorConnectionCitrixCloud
$HypervisorConnectionUID=Get-BrokerHypervisorConnection -Name $HypervisorConnectionCitrixCloud
Write-Host $HypervisorConnectionUID.uid

$DeliveryGroup=$args[2]
foreach ($objitem in $importdata) {
  ($splitems=$objitem.split(','))

  #Split from one one line the VDI name and user name
  $VDIname=$splitems[0]
  $Usernames=$splitems[1]

  #Split $Usernames if multiple users were assigned to one VDI. Assuming that no more than four users are assigned to one VDI maximum.
  $SplitUsers=$Usernames.Split(' ').Where({$_.Trim() -ne ''})
  $Username1=$SplitUsers[0]
  $Username2=$SplitUsers[1]
  $Username3=$SplitUsers[2]
  $Username4=$SplitUsers[3] 

  #Determine the Uid of the corresponding Machine Catalog
     $MCUID=Get-BrokerCatalog -Name $DeliveryGroup
    Write-Host $MCUID.Uid 

  #Determine the hostmachine ID from the hypervisorconnection as this a required for adding the system to the Machine Catalog
  $hostmachineid = dir "$VDIDataCenter" | ?{$_.Name -eq "$VDIname" }
  Write-Host $hostmachineid.id

  #Add machine to the Machine Catalog including the hypervisorconnection for powermanagement functionality
  new-brokermachine -Machinename "<<DOMAINAME>>\$VDIname" -CatalogUid $MCUID.uid -HypervisorConnectionUid $HypervisorConnectionUID.uid -HostedMachineID $hostmachineid.id

  #Add machine to corresponding Delivery Group
  Add-BrokerMachine -MachineName "<<PRE2000DOMAINNAME>>\$VDIname" -DesktopGroup $DeliveryGroup 

  # Assign User to his/her own VDI if user was assigned on-prem (when user is empty this will be skipped)
  if ($Username1)
            { Add-BrokerUser $Username1 -Machine <<PRE2000DOMAINNAME>>\$VDIname
            }

  if ($Username2)
            { Add-BrokerUser $Username2 -Machine <<PRE2000DOMAINNAME>>\$VDIname
            }

  if ($Username3)
            { Add-BrokerUser $Username3 -Machine <<PRE2000DOMAINNAME>>\$VDIname
            }

   if ($Username4)
            { Add-BrokerUser $Username4 -Machine <<PRE2000DOMAINNAME>>\$VDIname
            }

  #Clear Variabeles so they won't be re-used in the foreach loop
  Clear-Variable -Name "VDIName"
  Clear-Variable -Name "Usernames"
  Clear-Variable -Name "SplitUsers"
  Clear-Variable -Name "Username1"
  Clear-Variable -Name "Username2"
  Clear-Variable -Name "Username3"
  Clear-Variable -Name "Username4" 
}

Summarization

When migrating to the cloud with keeping existing VDIs you could use the Citrix Automated Configuration Tool or set-up your own scripts. I have shown two scripts used for exporting and importing VDIs from on-prem to the cloud. Especially the import script is provided as a basis which you can use for your own script as each scenario is different.