Citrix Published Apps migration script

Citrix Published Apps migration script

Recently I was working on a XenApp and XenDesktop 7.9 upgrade project. The customer didn’t want to touch the existing 7.9 environment, as it was a production environment with around 1000 concurrent users from different parts of the world. Instead a new XenApp and XenDesktop 7.18 site was created and we had to create everything manually in the new site.

Fortunately, besides the published application, there really wasn’t much to be done. We had to create a couple of Machine Catalogs and a few Delivery Groups. However the customer had 50+ published applications and it would take quite a while to manually create those by hand.

As it turned out, the customer couldn’t wait for me to develop this script, so I actually didn’t test it out in that specific environment. However that didn’t stop me from finishing the script, as I expect more 7.x to 7.x or 7.x to 1808 and later migration projects in the future.

As I wasn’t able to find any useful tools from Citrix to help me migrate a 7.x site to another 7.x site, I decided to write my own script, with some inspiration from some older scripts I had used earlier.

The script can be found here:

#Requires -Version 3.0
<# ********************************************************************************************************************************* Name: Migrate-XAapps Author: Kasper Johansen Company: edgemo Contact: kjo@edgemo.com Last modified by: Kasper Johansen Last modified Date: 24-09-2018 Version 1.0 .SYNOPSIS Migrates published apps between Virtual Apps and Desktops sites. .DESCRIPTION This script migrates all published apps between Virtual Apps and Desktops sites. This script has been developed using inspiration from the XD7Export and XD7Import scripts from Peter Juncker at Atea Denmark. Credit goes out to Peter Juncker for the initial scripts. The script can export published applications from a Citrix XenApp and XeneDesktop and Citrix Virtual Apps and Desktops site. The script can import published applications to a Citrix XenApp and XeneDesktop and Citrix Virtual Apps and Desktops site. The script has been testet with: Citrix XenApp and XenDesktop 7.6 LTSR CU6 Citrix XenApp and XenDesktop 7.9 Citrix Virtual Apps and Desktops 1808 The most common information (icon information, user/group access information, commandline, commandline arguments and workdir) is exported to 3 different CSV files. The names of the CSV files are hardcoded, but the path to the CSV files is customizable. The specific CSV produced when exporting are: Icons.csv: Contains the binary icon information for all published applications. This file may grow large in size depending on the amount of published applications. Apps.csv: Contains all published applications property information. This is a 1 to 1 extraction with no filtering. Users: Contains the published applications UID and any Active Direcoty User/Group information. All 3 CSV files are needed for a successfull import! .PARAMETER DesktopGroupName The name of the Delivery Group to import/export published applications. If the Delivery Group name contains spaces if must be encased in double quotes! .PARAMETER CSVInput Specifies the path the CSV files. If not specified, the default path, which is the directory from where the script is executed, is selected. This parameter is only active when using the -Import switch. .PARAMETER CSVOutput Specifies the path the CSV files. If not specified, the default path, which is the directory from where the script is executed, is selected. This parameter is only active when using the -Export switch. .SWITCH Import Enables the import of published applications to a Citrix Virtual Apps and Desktop Site .SWITCH Export Enables the Export of published applications from a Citrix Virtual Apps and Desktop Site .PARAMETER LogDir Specifies the directory to store the transcription logfile. If not specified, the default $env:SystemRoot\Temp directory is selected. .EXAMPLES Export published applications: Migrate-XAapps -DesktopGroupName "XenApp" -Export Export published applications with custom CSV output path: Migrate-XAapps -DesktopGroupName "XenApp" -CSVOutput C:\CSVOutput -Export Import published applications: Migrate-XAapps -DesktopGroupName "XenApp" -Import Import published applications with custom CSV input path: Migrate-XAapps -DesktopGroupName "XenApp" -CSVInput C:\CSVInput -Import ********************************************************************************************************************************* #>
# Function parameters
Param(
    [Parameter(Mandatory = $true)]
    [string]$DesktopGroupName,
    [Parameter(ParameterSetName = "Import")]
    [string]$CSVInput = (Split-Path -parent $MyInvocation.MyCommand.Definition),
    [Parameter(ParameterSetName = "Export")]
    [string]$CSVOutput = (Split-Path -parent $MyInvocation.MyCommand.Definition),
    [Parameter(ParameterSetName = "Import", Mandatory)]
    [switch]$Import,
    [Parameter(ParameterSetName = "Export", Mandatory)]
    [switch]$Export,
    [string]$LogDir = "$env:SystemRoot\Temp"
    )

# Add Citrix Broker Admin PowerShell snap-in
Write-Host "Adding Citrix Broker Admin PowerShell snap-in" -Verbose
Add-PSSnapin -Name Citrix.Broker.Admin.V2 -ErrorAction Stop

function Export-XAapps
    {
    param(
         $DesktopGroupName,
         $CSVOutput,
         $LogDir
        )

    # Start time measuring and transcription
    $LogPS = $LogDir + "\Export-XAapps.log"
    $startDTM = (Get-Date)
    Start-Transcript $LogPS

        # CSV path variables
        $IconsCSV = $CSVOutput + "\Icons.csv"
        $AppsCSV = $CSVOutput + "\Apps.csv"
        $UsersCSV = $CSVOutput + "\Users.csv"

        # Get DesktopGroup
        $DesktopGroup = Get-BrokerDesktopGroup -Name $DesktopGroupName
                
            If (!($DesktopGroup))
            {
                Write-Host "$DesktopGroup does not exist" -Verbose
                break
            }
            else
            {
                # Export published apps icon information
                Write-Host "Exporting published apps icon information" -Verbose
                If (Test-Path -Path $IconsCSV)
                {
                    Remove-Item -Path $IconsCSV
                }
                Get-BrokerIcon | Export-Csv $IconsCSV -Encoding "UTF8" -Verbose
                
                    # Export published apps 
                    Write-Host "Exporting published apps" -Verbose
                    If (Test-Path -Path $AppsCSV)
                    {
                        Remove-Item -Path $AppsCSV
                    }
                    Get-BrokerApplication -AssociatedDesktopGroupUID $DesktopGroup.UID | Export-Csv $AppsCSV -Encoding "UTF8" -NoTypeInformation -Verbose

                        # Export published apps groups/users
                        Write-Host "Exporting published apps groups/users access information" -Verbose
                        If (Test-Path -Path $UsersCSV)
                        {
                            Remove-Item -Path $UsersCSV
                        }
                        
                        $PublishedApps = Get-BrokerApplication -AssociatedDesktopGroupUID $DesktopGroup.UID
                        Add-Content $UsersCSV "UID,Username" -Encoding "UTF8" -Verbose
                        ForEach ($App in $PublishedApps)
                        {
                            
                            If ($App.AssociatedUserNames -gt "1")
                            {
                                $UserName = $App.AssociatedUserNames
                                ForEach ($user in $UserName)
                                {
                                    $UID = $App.UID
                                    $AppGroupUIDs = $App.AssociatedDesktopGroupUids
                                    $UserName = $user

                                    Add-Content -Path $UsersCSV "$UID,$UserName" -Encoding "UTF8"
                                }
                            }
                            else
                            {
                                $UID = $App.UID
                                $AppGroupUIDs = $App.AssociatedDesktopGroupUids
                                $UserName = $App.AssociatedUserNames
                                                        
                                Add-Content -Path $UsersCSV "$UID,$UserName" -Encoding "UTF8"
                            }
                        }
            }
                
    # End time measuring and transcription
    $EndDTM = (Get-Date)
    Write-Output "Elapsed Time: $(($EndDTM-$StartDTM).TotalMinutes) Minutes" -Verbose
    Stop-Transcript
    }

function Import-XAapps
    {
    param(
        $DesktopGroupName,
        $CSVInput,
        $LogDir
        )
    # Start time measuring and transcription
    $LogPS = $LogDir + "\Import-XAapps.log"
    $startDTM = (Get-Date)
    Start-Transcript $LogPS

        # CSV path variables
        $IconsCSV = $CSVInput + "\Icons.csv"
        $AppsCSV = $CSVInput + "\Apps.csv"
        $UsersCSV = $CSVInput + "\Users.csv"

        # Get DesktopGroup
        $DesktopGroup = Get-BrokerDesktopGroup -Name $DesktopGroupName
                
            If (!($DesktopGroup))
            {
                Write-Host "$DesktopGroup does not exist" -Verbose
                break
            }
            else
            {
                # Import published apps icon information
                import-csv -path $AppsCSV|ForEach-Object {
                    
                    $AppApplicationName = $_.ApplicationName
	                $AppApplicationType=$_.ApplicationType
	                $AppBrowserName=$_.BrowserName
	                $AppClientFolder=$_.ClientFolder
	                $AppCommandLineArguments=$_.CommandLineArguments
	                $AppCommandLineExecutable=$_.CommandLineExecutable
	                $AppCpuPriorityLevel=$_.CpuPriorityLevel
	                $AppDescription=$_.Description
	                $AppEnabled=$_.Enabled -as [bool]
	                $AppIconFromClient=$_.IconFromClient -as [bool]
	                $AppIconUid=$_.IconUid
	                $AppName=$_.Name
	                $AppPublishedName=$_.PublishedName
	                $AppSecureCmdLineArgumentsEnabled=$_.SecureCmdLineArgumentsEnabled -as [bool]
	                $AppShortcutAddedToDesktop=$_.ShortcutAddedToDesktop -as [bool]
	                $AppShortcutAddedToStartMenu=$_.ShortcutAddedToStartMenu -as [bool]
	                $AppStartMenuFolder=$_.StartMenuFolder
	                $AppUUID=$_.UUID
	                $AppUid=$_.Uid
	                $AppUserFilterEnabled=$_.UserFilterEnabled
	                $AppVisible=$_.Visible -as [bool]
	                $AppWaitForPrinterCreation=$_.WaitForPrinterCreation -as [bool]
	                $AppWorkingDirectory=$_.WorkingDirectory
                
                    # Import icon information
                    $IconData = Import-Csv $IconsCSV | Where-Object {$_.UID -eq $AppIconUid}
                    
                    # Import user/group access information
                    $UserAccess = import-csv -path $UsersCSV | Where-Object {$_.UID -eq $AppUid}

                    # Create icons
                    $IconID = New-BrokerIcon -EncodedIconData $IconData.EncodedIconData

                        If (Get-BrokerApplication | Where { $_.Name -eq $AppName })
                        {
                            Write-Host "$AppName already exists!" -Verbose
                            Write-Host
                        }
                        else
                        {
                            Write-Host "Importing $AppName" -Verbose
                            Write-Host "Importing icon for $AppName" -Verbose

                            New-BrokerApplication -Name $AppName -ApplicationType $AppApplicationType -ClientFolder $AppClientFolder -CommandLineArguments $AppCommandLineArguments -CommandLineExecutable $AppCommandLineExecutable -CpuPriorityLevel $AppCpuPriorityLevel -Description $AppDescription -DesktopGroup $DesktopGroup -Enabled $AppEnabled -IconUid $IconID.UID -Priority 0 -PublishedName $AppPublishedName -SecureCmdLineArgumentsEnabled $AppSecureCmdLineArgumentsEnabled -StartMenuFolder $AppStartMenuFolder -ShortcutAddedToDesktop $AppShortcutAddedToDesktop -ShortcutAddedToStartMenu $AppShortcutAddedToStartMenu -UserFilterEnabled $False -Visible $AppVisible -WaitForPrinterCreation $AppWaitForPrinterCreation -WorkingDirectory $AppWorkingDirectory -Verbose
                            
                            If ($AppUserFilterEnabled -eq "True")
                            {
                                Set-BrokerApplication -Name $AppName -UserFilterEnabled $True -Verbose
                                Add-BrokerApplication -Name $AppName -DesktopGroup $DesktopGroup -Verbose
                                                                    
                                    $UserAccess | ForEach-Object {
                                    Write-Host "Importing user/group access for $AppName" -Verbose
                                    Write-Host "Configuring AD Group/user:" $_.Username -Verbose
                                    Write-Host

                                        ForEach ($user in $_.Username)
                                        {
                                            Add-BrokerUser -Name $user -Application $AppName -Verbose -ErrorAction SilentlyContinue
                                        }
                                    }
                            }
                            else
                            {
                                Set-BrokerApplication -Name $AppName -UserFilterEnabled $False -Verbose
                                Add-BrokerApplication -Name $AppName -DesktopGroup $DesktopGroup -Verbose
                            }

                        }                
                }
                
            }
                
    # End time measuring and transcription
    $EndDTM = (Get-Date)
    Write-Output "Elapsed Time: $(($EndDTM-$StartDTM).TotalMinutes) Minutes" -Verbose
    Stop-Transcript
    }

function Migrate-XAapps ($DesktopGroupName,$CSVInput,$CSVOutput,$LogDir,$Import,$Export)
    {
        If ($Export)
        {
            Export-XAapps -DesktopGroupName $DesktopGroupName -CSVOutput $CSVOutput -LogDir $LogDir
        }
            If ($Import)
            {
                Import-XAapps -DesktopGroupName $DesktopGroupName -CSVInput $CSVInput -LogDir $LogDir
            }
    }

Migrate-XAapps $DesktopGroupName $CSVInput $CSVOutput $LogDir $Import $Export

Copy the code above and save it to file called Migrate-XAapps.ps1. The script contains basic information on usage and also examples of the different switches and paramaters that can be used.

Let me know if you experience any issues. As mentioned in the script, I have tested the code on XenApp and XenDesktop 7.6 LTSR CU6, XenApp and XenDesktop 7.9 and Citrix Virtual Apps and Desktops 1808 and I haven’t run into any issues, however I have probably not covered every possible published application scenario out there.

Comments are closed.