Meppies Blog

Upgrade Citrix CVAD 2402 LTSR Database with Ansible code

December 09, 2024 | 13 Minute Read

Database upgrade

Citrix Virtual Apps and Desktops (CVAD) 2402 LTSR introduced changes to the database upgrade process, requiring additional manual steps. In collaboration with Citrix, we successfully automated these steps to streamline the upgrade process. The following steps outline the automated approach.

Ansible

I used Ansible to automate the upgrade process, leveraging variables (denoted by {{ }}) to enhance flexibility. All commands are PowerShell-based, adaptable to your preferred automation tool.

Backup the databases

Ensure a comprehensive backup of all relevant databases is performed before initiating the upgrade.

- name: Backup database by the first Citrix Delivery Controller
    win_shell: Backup-SqlDatabase -ServerInstance "{{ sql_server }}" -Database "{{ item }}" -BackupFile "{{ citrix.database.backup_path }}\\{{ item }}_{{ lookup('pipe', 'date +%Y%m%d') }}.bak" 
    with_items:
        - "{{ citrix.database.site }}"
        - "{{ citrix.database.logging }}"
        - "{{ citrix.database.monitoring }}"
    vars:
        ansible_become: yes
        ansible_become_user: "{{ ansible_user }}"
        ansible_become_password: "{{ ansible_password }}"
        ansible_become_method: runas

Monitoring Database script

The upgrade process for the monitoring component has been slightly modified, necessitating the creation of an additional upgrade script for the site database.

Retrieve the necessary script to upgrade the monitoring database. This process includes determining the latest compatible version and requesting the appropriate upgrade script.

- name: Generate Citrix monitoring database upgrade scripts
    win_shell: |
        $version = Get-MonitorInstalledDBVersion -Upgrade
        if ($version) {
            $sql = Get-MonitorDBVersionChangeScript -DatabaseName "{{ citrix.database.monitoring }}" -TargetVersion "$($version.Major[0]).$($version.Minor[0]).$($version.Build[0]).$($version.Revision[0])" -DataStore "{{ item }}"
            return $($sql.script)
        }
    loop:
        - Monitor
    register: mon_upgrade
    vars:
        ansible_become: yes
        ansible_become_user: "{{ ansible_user }}"
        ansible_become_password: "{{ ansible_password }}"
        ansible_become_method: runas

- name: Generate Citrix monitoring database upgrade scripts
    win_shell: |
        $version = Get-MonitorInstalledDBVersion -Upgrade
        if ($version) {
            $sql = Get-MonitorDBVersionChangeScript -DatabaseName "{{ citrix.database.site }}" -TargetVersion "$($version.Major[0]).$($version.Minor[0]).$($version.Build[0]).$($version.Revision[0])" -DataStore "{{ item }}"
            return $($sql.script)
        }
    loop:
        - Site
    register: mon_site_upgrade
    vars:
        ansible_become: yes
        ansible_become_user: "{{ ansible_user }}"
        ansible_become_password: "{{ ansible_password }}"
        ansible_become_method: runas

Logging Database script

The upgrade process for the Logging component has been slightly modified, necessitating the creation of an additional upgrade script for the site database.

We will now repeat the upgrade process, this time focusing on the Logging database.

- name: Generate Citrix logging database upgrade scripts
    win_shell: |
        $version = Get-LogInstalledDBVersion -Upgrade
        if ($version) {
            $sql = Get-LogDBVersionChangeScript -DatabaseName "{{ citrix.database.logging }}" -TargetVersion "$($version.Major[0]).$($version.Minor[0]).$($version.Build[0]).$($version.Revision[0])" -DataStore "{{ item }}"
            return $($sql.script)
        }
    loop:
        - Logging
    register: log_upgrade
    vars:
        ansible_become: yes
        ansible_become_user: "{{ ansible_user }}"
        ansible_become_password: "{{ ansible_password }}"
        ansible_become_method: runas

- name: Generate Citrix logging database upgrade scripts
    win_shell: |
        $version = Get-LogInstalledDBVersion -Upgrade
        if ($version) {
            $sql = Get-LogDBVersionChangeScript -DatabaseName "{{ citrix.database.site }}" -TargetVersion "$($version.Major[0]).$($version.Minor[0]).$($version.Build[0]).$($version.Revision[0])" -DataStore "{{ item }}"
            return $($sql.script)
        }
    loop:
        - Site
    register: log_site_upgrade
    vars:
        ansible_become: yes
        ansible_become_user: "{{ ansible_user }}"
        ansible_become_password: "{{ ansible_password }}"
        ansible_become_method: runas

Site Database script

Generate the required scripts to upgrade the site database. This involves creating multiple scripts that must be run sequentially to complete the upgrade process.

- name: Generate Citrix site database upgrade scripts
    win_shell: |
        $version = {{ item.check }} -Upgrade
        if ($version) {
            $sql = {{ item.command }} -DatabaseName "{{ citrix.database.site }}" -TargetVersion "$($version.Major[0]).$($version.Minor[0]).$($version.Build[0]).$($version.Revision[0])"
            return $($sql.script)
        }
    loop:
        - { check: 'Get-AcctInstalledDBVersion', command: 'Get-AcctDBVersionChangeScript'}
        - { check: 'Get-AnalyticsInstalledDBVersion', command: 'Get-AnalyticsDBVersionChangeScript'}
        - { check: 'Get-AppLibInstalledDBVersion', command: 'Get-AppLibDBVersionChangeScript'}
        - { check: 'Get-ConfigInstalledDBVersion', command: 'Get-ConfigDBVersionChangeScript'}
        - { check: 'Get-AdminInstalledDBVersion', command: 'Get-AdminDBVersionChangeScript'}
        - { check: 'Get-EnvTestInstalledDBVersion', command: 'Get-EnvTestDBVersionChangeScript'}
        - { check: 'Get-HypInstalledDBVersion', command: 'Get-HypDBVersionChangeScript'}
        - { check: 'Get-ProvInstalledDBVersion', command: 'Get-ProvDBVersionChangeScript'}
        - { check: 'Get-OrchInstalledDBVersion', command: 'Get-OrchDBVersionChangeScript'}
        - { check: 'Get-SfInstalledDBVersion', command: 'Get-SfDBVersionChangeScript'}
        - { check: 'Get-TrustInstalledDBVersion', command: 'Get-TrustDBVersionChangeScript'}
        - { check: 'Get-BrokerInstalledDbVersion', command: 'Get-BrokerDBVersionChangeScript'}
    register: site_upgrade
    vars:
        ansible_become: yes
        ansible_become_user: "{{ ansible_user }}"
        ansible_become_password: "{{ ansible_password }}"
        ansible_become_method: runas

Create a directory to save the scripts

Because the SQL scripts are to long for the powershell command you need to save the Scripts to a file.

- name: Create directory structure
    win_file:
        path: "{{ citrix.backup }}"
        state: directory

Save the Scripts

Save the scripts to a file for future execution.

- name: Generate the SQL scripts on the SQL server
    ansible.windows.win_copy:
        content: "{{ item.stdout | trim }}"
        dest: "{{ citrix.backup }}\\upgrade_{{index}}.sql"
    when: item.stdout != ""
    with_items: 
        - "{{ site_upgrade.results }}"
        - "{{ mon_site_upgrade.results }}"
        - "{{ log_site_upgrade.results }}"
        - "{{ mon_upgrade.results }}"
        - "{{ log_upgrade.results }}"
    loop_control:
    index_var: index

Stop Citrix the services

To ensure a smooth database upgrade process, it is necessary to halt all Delivery Controller services. This will temporarily disrupt user access to published applications and desktops in StoreFront.

- name: stop citrix services
    win_shell: get-service citrix* -computername {{ item }} | stop-service -force
    loop: "{{ groups['CitrixDeliveryController'] }}"
    vars:
        ansible_become: yes
        ansible_become_user: "{{ ansible_user }}"
        ansible_become_password: "{{ ansible_password }}"
        ansible_become_method: runas

Start the upgrade of the databases

The database upgrade will commence with the Site database. Once completed, we will proceed with upgrading the other necessary databases.

- name: Update the schema of the Citrix Delivery Controller databases
    win_shell: Invoke-Sqlcmd -ServerInstance "{{ sql_server }}" -InputFile "{{ citrix.backup }}\\upgrade_{{index}}.sql" 
    when: item.stdout != ""
    with_items:
        - "{{ site_upgrade.results }}"
        - "{{ mon_site_upgrade.results }}"
        - "{{ log_site_upgrade.results }}"
        - "{{ mon_upgrade.results }}"
        - "{{ log_upgrade.results }}"
    loop_control:
    index_var: index
    vars:
        ansible_become: yes
        ansible_become_user: "{{ ansible_user }}"
        ansible_become_password: "{{ ansible_password }}"
        ansible_become_method: runas

Start the Citrix services

The database upgrade process has been successfully finalized. You now restart the Delivery Controller services, restoring user access to published applications and desktops via StoreFront.

- name: start citrix services
    win_shell: get-service citrix* -computername {{ item }} | start-service
    loop: "{{ groups['CitrixDeliveryController'] }}"
    vars:
        ansible_become: yes
        ansible_become_user: "{{ ansible_user }}"
        ansible_become_password: "{{ ansible_password }}"
        ansible_become_method: runas

Cleanup the files

Clean up the directory where the SQL scripts are stored by deleting any temporary files.

- name: Remove directory structure
    win_file:
        path: "{{ citrix.backup }}"
        state: absent

Final step

This step, developed in collaboration with Citrix, should be executed on all Delivery Controllers after completing the database upgrade.

Function CheckInstanceRegistered($instance, $registeredInstances)
{
    foreach ($registeredInstance in $registeredInstances)
    {
        if (
            ($registeredInstance.ServiceType -eq $instance.ServiceType) -and
            ($registeredInstance.InterfaceType -eq $instance.InterfaceType) -and
            ($registeredInstance.Address -eq $instance.Address) -and
            ($registeredInstance.Version -eq $instance.Version) -and
            ($registeredInstance.Binding -eq $instance.Binding) -and
            ($registeredInstance.ServiceAccount -eq $instance.ServiceAccount) -and
            ($registeredInstance.ServiceAccountSid -eq $instance.ServiceAccountSid) -and
            ($registeredInstance.ServiceGroupName -eq $instance.ServiceGroupName) -and
            ($registeredInstance.ServiceGroupUid -eq $instance.ServiceGroupUid)            
        )
        {
            return $true
        }  
    }
    
    return $false
}

Function GetUnregisteredInstances($instances)
{
    $unregisteredInstances = @()
    $registeredInstances=Get-ConfigRegisteredServiceInstance

    foreach ($instance in $instances)
    {
        $registered = CheckInstanceRegistered $instance $registeredInstances
        if ($registered -eq $false)
        {
            $unregisteredInstances += $instance
        }
    }
    return $unregisteredInstances
}

Function RegisterServiceInstances($serviceType)
{
    $cmdlet="Get-{0}ServiceInstance" -f $serviceType  
    $instances=Invoke-Expression $cmdlet

    $unregisteredInstances = GetUnregisteredInstances $instances
    if ($unregisteredInstances.Count -ne 0)
    {
        Write-Host "Register service instance $ServiceType..."

        foreach ($instance in $unregisteredInstances)
        {
            Write-Host $instance.Address

            if (($instance.ServiceType -eq "Orch") -and ($instance.InterfaceType -eq "RestApi"))
            {
                Get-ConfigRegisteredServiceInstance -ServiceType "Orch" -InterfaceType "RestApi" | Unregister-ConfigRegisteredServiceInstance
            }
        }           
        Register-ConfigServiceInstance -ServiceInstance $unregisteredInstances
    }
}

Function UpgradeServiceInstances()
{
    $serviceTypes=@("Config", "Admin", "Log", "Acct", "Analytics", "AppLib", "EnvTest", "Hyp", "Prov", "Sf", "Broker", "Trust", "Orch")
    foreach ($serviceType in $serviceTypes)
    {
        RegisterServiceInstances $serviceType
    }

    Write-Host "ServiceInstances have been upgraded"
}

Function UpgradeAdminConfiguration()
{
    Import-ConfigFeatureTable  -Path "C:\Program Files\Citrix\XenDesktopPoshSdk\Module\Citrix.XenDesktop.Admin.V1\Citrix.XenDesktop.Admin\FeatureTable.xml"
    Import-AdminRoleConfiguration -Path "C:\Program Files\Citrix\XenDesktopPoshSdk\Module\Citrix.XenDesktop.Admin.V1\Citrix.XenDesktop.Admin\StudioRoleConfig\RoleConfigSigned.xml"
    Import-AdminRoleConfiguration -Path "C:\Program Files\Citrix\XenDesktopPoshSdk\Module\Citrix.XenDesktop.Admin.V1\Citrix.XenDesktop.Admin\StudioRoleConfig\DirectorRoleConfigSigned.xml"

    $site=Set-ConfigSite -ProductVersion "7.41"

    $serviceTypes=@("Config", "Admin", "Log", "Acct", "Analytics", "AppLib", "EnvTest", "Hyp", "Prov", "Sf", "Broker", "Trust", "Orch")
    foreach ($serviceType in $serviceTypes)
    {
        $cmdlet="Reset-{0}EnabledFeatureList" -f $serviceType  
        Invoke-Expression $cmdlet
    }

    New-AdminAdministrator  -IsHidden $True -Name "S-1-5-20" -ErrorAction "SilentlyContinue"
    Add-AdminRight -Administrator "S-1-5-20" -Role "fd793b26-1c19-407f-92ec-1c4177fed7b4" -Scope "00000000-0000-0000-0000-000000000000"
    Start-OrchRestApi

    Write-Host "AdminConfiguration has been upgraded"
}

UpgradeServiceInstances
UpgradeAdminConfiguration