At one of my customers we build a XenApp 6.5 environment, where the XenApp Session Hosts were running on local storage of the Hypervisor (VMware ESX 5.5). At this customer the hypervisor and Citrix XenApp were maintained by separate teams. The hypervisor team was used to use shared storage and the vMotion capabilities of ESX for their maintenance tasks on the ESX host during daily operations. However now the virtual machines were running local storage this technique could not be used anymore and the hypervisor team found it difficult to fulfill their maintenance tasks.

After some discussion we agreed that the process should be as easy as possible (without less inference of the XenApp team) and that it should be possible to do the maintenance during daily operations. The first step was adding additional capacity, so maintenance during the business hours was possible. Secondly I wrote a set of small simple scripts that the maintenance can be done without less interaction between the teams and user impact is minimal. In this article I would like to share those scripts with you including explaining what they are doing.

Script 1: Determine the XenApp Session Hosts

For these ESX maintenance activities I actually created a total of four scripts. Each script is part of the whole process. The first script is pretty straight forwarded. First the FQDN name of the vCenter server needs to be provided and a filename and location need be entered to store the XenApp Session Hosts in for the next script. During the script the hypervisor administrator will enter the FDQN of the ESX hosts on which he would like to execute maintenance. For each ESX host provided the VMs will be determined and stored in the earlier provided file. More ESX hosts can be entered during the script, till the administrator decided to add no more.

#---------------------------------------------------------------------

# Script:   GetXenAppSessionHosts.ps1

# Determine which   XenApp Session Hosts are running on the provided ESX host

# Creator: Wilco van   Bragt

# Creation Date:   15-10-2014

#---------------------------------------------------------------------

# Version: 0.1

# By: Wilco van   Bragt

# Date: 15-10-2014

# Changes: Initial   release

#---------------------------------------------------------------------

# # Requirements:

# - Scripts needs to run on server with   VMware ESX PowerShell cmdlets

#---------------------------------------------------------------------

# Import Modules

#---------------------------------

Add-PSSnapin   VMware.VimAutomation.Core

# Provide vCenter en   connect to this VIserver

#---------------------------------

$VIServer=Read-Host   "Please enter the FQDN of the vCenter server"

Connect-VIServer   $VIServer

# Define variable

#---------------------------------

$strQuit="Yes"

# Provide file to   store XenApp Session Hosts

#---------------------------------

$XenAppSessionHostFile=Read-Host   "Please enter the filename and path to store XenApp Session Hosts"

# Determine the VMs   on this host and store those names in a texfile

#---------------------------------

Do {

                $ESXHost=Read-Host   "Please enter the FQDN of the ESX host which will go in   maintenance"

                               get-vmhost   "$ESXHost" | Get-VM | Add-Content   "$XenAppSessionHostFile"

                               $strQuit=   Read-Host "Do want to enter another ESX host?"

                }

Until ($strQuit -eq   "No")

Script 2: Place Sessions Host in maintenance and shutdown

The second script is the most intelligent of the whole scripts. In this script the earlier collection Citrix XenApp Session Host will be placed in maintenance mode and shutdown the VM when no users’ sessions are running anymore (or after seven hours and several warning messages). When all VMs are shutdown the hypervisor administrator can start his maintenance activities.

Before you can use the script, please check the variable $ServertoWorkerGroup contains a location that is available on your machine.

The script starts with asking the location and filename in which the Citrix XenApp Session hosts are stored. This file will be read and for each server in the file the LogonMode will be changed to Prohibit New Logons. Secondly the server will be checked if it’s located in a reboot Worker Group and removed from this worker group, so the server is not restarted during this script. This information is also stored in a file, so we can reconfigure the Worker Group settings later. This customer had created several worker groups where to each WG a different reboot schedule policy was assigned to. Logically you can remove this part if this is not the case in your infrastructure. For administration purposes (the XenApp admin can see quickly which server is in maintenance) the machine is placed in a “maintenance” worker group.

When these steps are finished the script will wait for one hour and checks if there are still sessions on the machine. If there are no sessions running anymore, the machine will be shut down. If there are still sessions the script will check on active sessions on hour later. This step is repeated every hour, when after seven hours still active sessions are running on the machine a message is send to the users and the machine will be shut down. At the end of the script all machines are shut down and the hypervisor administrator can start his maintenance activities.

#---------------------------------------------------------------------

# Script:   SessionHostsinMainteance.ps1

# Take XenApp   Session Hosts offline for ESX maintenance

# Creator: Wilco van   Bragt

# Creation Date:   10-10-2014

#---------------------------------------------------------------------

# Version: 0.1

# By: Wilco van   Bragt

# Date: 10-10-2014

# Changes: Initial   release

#---------------------------------------------------------------------

# Textfile with   XenApp Session Hosts is required (preferred generated by PowerCLI)

# Requirements:

# - Scripts needs to run on server with   Citrix PowerShell cmdlets

# - WorkerGroups defined in variabeles   $MaintenanceWG and $WGReboot should be available

#---------------------------------------------------------------------

# Import Modules

#---------------------------------

add-pssnapin   Citrix.*

#Define Variables

#---------------------------------

$WGReboot="Reboot"

$MaintenanceWG="MaintenanceWG"

$ServertoWorkerGroup="c:\Temp\ServertoWorkerGroup.txt"

$exe="C:\Windows\System32\msg.exe"

#Provide the file   with the XenApp Session Hosts, that should be taken off-line

#---------------------------------

$SessionHostFile=Read-Host   "Please enter the path and name of the file containing the XenApp   Session Hosts"

#Read Session Hosts   names, disable logon, remove server out of the WG reboot group and add to WG   maintenance group

#---------------------------------

$SessionHosts=Get-Content   $SessionHostFile

foreach ($line in   $SessionHosts)

                { $DeviceName = $Line

                Set-XAServerLogOnMode   -LogOnMode ProhibitNewLogOns -ServerName $DeviceName

                $WGCurrent=Get-XAWorkerGroup   -ServerName $DeviceName

                $WGs=foreach ($objitem in   $WGCurrent)

                               {write-host   $objitem

                               if ($objitem   -match "$WGReboot")

                                               {   Add-Content $ServertoWorkerGroup "$DeviceName,$objitem"

                                               remove-xaworkergroupserver   $objitem $DeviceName

                                               }

                               }

                add-xaworkergroupserver $MaintenanceWG   $DeviceName   

                }

#Wait on hour and   check if there are (still) sessions running on the server

#If there are no   sessions shutdown the machine, else wait

#---------------------------------

Clear-Variable   DeviceName      

start-sleep -s 3600

foreach ($line in   $SessionHosts)

                { $DeviceName = $Line

                               $sessions=get-xaserverload   -ServerName $DeviceName | select Load

                               $sessions =   $sessions -split('=')

                               $load =   $Sessions[1]

                               $load=$load.Trim("}")                  

                               if ($load -eq   0) {Stop-Computer $DeviceName

                               $ServersinUse=Get-Content   $SessionHostFile | Where-Object {$_ -ne $DeviceName}

                               $ServersinUse |   Out-File $SessionHostFile

                               }

                }

#Wait on hour and   check if there are (still) sessions running on the server

#If there are no   sessions shutdown the machine, else wait

#---------------------------------

Clear-Variable   DeviceName      

start-sleep -s 3600

$SessionHosts=Get-Content   $SessionHostFile

foreach ($line in   $SessionHosts)

                { $DeviceName = $Line

                               $sessions=get-xaserverload   -ServerName $DeviceName | select Load

                               $sessions =   $sessions -split('=')

                               $load =   $Sessions[1]

                               $load=$load.Trim("}")                  

                               if ($load -eq   0) {Stop-Computer $DeviceName

                               $ServersinUse=Get-Content   $SessionHostFile | Where-Object {$_ -ne $DeviceName}

                               $ServersinUse |   Out-File $SessionHostFile

                               }

                }

               

#Wait on hour and   check if there are (still) sessions running on the server

#If there are no   sessions shutdown the machine, else wait

#---------------------------------

Clear-Variable   DeviceName      

start-sleep -s 3600

$SessionHosts=Get-Content   $SessionHostFile

foreach ($line in   $SessionHosts)

                { $DeviceName = $Line

                               $sessions=get-xaserverload   -ServerName $DeviceName | select Load

                               $sessions =   $sessions -split('=')

                               $load =   $Sessions[1]

                               $load=$load.Trim("}")                  

                               if ($load -eq   0) {Stop-Computer $DeviceName

                               $ServersinUse=Get-Content   $SessionHostFile | Where-Object {$_ -ne $DeviceName}

                               $ServersinUse |   Out-File $SessionHostFile

                               }

                }

#Wait on hour and   check if there are (still) sessions running on the server

#If there are no   sessions shutdown the machine, else wait

#---------------------------------    

Clear-Variable   DeviceName      

start-sleep -s 3600

$SessionHosts=Get-Content   $SessionHostFile

foreach ($line in   $SessionHosts)

                { $DeviceName = $Line

                               $sessions=get-xaserverload   -ServerName $DeviceName | select Load

                               $sessions =   $sessions -split('=')

                               $load =   $Sessions[1]

                               $load=$load.Trim("}")                  

                               if ($load -eq   0)

                               {Stop-Computer   $DeviceName

                               $ServersinUse=Get-Content   $SessionHostFile | Where-Object {$_ -ne $DeviceName}

                               $ServersinUse |   Out-File $SessionHostFile

                               }

                }

               

#Wait on hour and   check if there are (still) sessions running on the server

#If there are no   sessions shutdown the machine, else wait

#---------------------------------

clear-Variable   DeviceName       

start-sleep -s 3600

$SessionHosts=Get-Content   $SessionHostFile

foreach ($line in   $SessionHosts)

                { $DeviceName = $Line

                               $sessions=get-xaserverload   -ServerName $DeviceName | select Load

                               $sessions =   $sessions -split('=')

                               $load =   $Sessions[1]

                               $load=$load.Trim("}")                  

                               if ($load -eq   0) {Stop-Computer $DeviceName

                               $ServersinUse=Get-Content   $SessionHostFile | Where-Object {$_ -ne $DeviceName}

                               $ServersinUse |   Out-File $SessionHostFile

                               }

                }

               

#Wait on hour and   check if there are (still) sessions running on the server

#If there are no   sessions shutdown the machine, else wait

#---------------------------------

clear-Variable   DeviceName       

start-sleep -s 3600

$SessionHosts=Get-Content   $SessionHostFile

foreach ($line in   $SessionHosts)

                { $DeviceName = $Line

                               $sessions=get-xaserverload   -ServerName $DeviceName | select Load

                               $sessions =   $sessions -split('=')

                               $load =   $Sessions[1]

                               $load=$load.Trim("}")                  

                               if ($load -eq   0) {Stop-Computer $DeviceName

                               $ServersinUse=Get-Content   $SessionHostFile | Where-Object {$_ -ne $DeviceName}

                               $ServersinUse |   Out-File $SessionHostFile

                               }

                }

#Wait on hour and   check if there are (still) sessions running on the server

#If there are no   sessions shutdown the machine, else wait and send message to the users

#---------------------------------    

Clear-Variable   DeviceName      

start-sleep -s 3600

$SessionHosts=Get-Content   $SessionHostFile

foreach ($line in   $SessionHosts)

                { $DeviceName = $Line

                               $sessions=get-xaserverload   -ServerName $DeviceName | select Load

                               $sessions =   $sessions -split('=')

                               $load =   $Sessions[1]

                               $load=$load.Trim("}")                  

                               if ($load -eq   0) {Stop-Computer $DeviceName

                               $ServersinUse=Get-Content   $SessionHostFile | Where-Object {$_ -ne $DeviceName}

                               $ServersinUse |   Out-File $SessionHostFile

                               }

                                               else   {& $exe * /Server:$DeviceName /time:120 "DThis server will be shut   down in two hours. Please log-off. You can directly login again"}

                }

#Wait on hour and   check if there are (still) sessions running on the server

#If there are no   sessions shutdown the machine, else wait and send message to the users

#---------------------------------

clear-Variable   DeviceName       

start-sleep -s 3600

$SessionHosts=Get-Content   $SessionHostFile

foreach ($line in   $SessionHosts)

                { $DeviceName = $Line

                               $sessions=get-xaserverload   -ServerName $DeviceName | select Load

                               $sessions =   $sessions -split('=')

                               $load =   $Sessions[1]

                               $load=$load.Trim("}")                  

                               if ($load -eq   0) {Stop-Computer $DeviceName

                               $ServersinUse=Get-Content   $SessionHostFile | Where-Object {$_ -ne $DeviceName}

                               $ServersinUse |   Out-File $SessionHostFile

                               }

                                               else   {& $exe * /Server:$DeviceName /time:120 "This server will be shut   down in one hour. Please log-off. You can directly login again"}

                }             

#Wait on hour and   check if there are (still) sessions running on the server

#If there are no   sessions shut down the machine, else wait 5 minutes and shut down the servers

#---------------------------------

Clear-Variable   DeviceName      

#start-sleep -s 3600

$SessionHosts=Get-Content   $SessionHostFile

foreach ($line in $SessionHosts)

                { $DeviceName = $Line

                               $sessions=get-xaserverload   -ServerName $DeviceName | select Load

                               $sessions =   $sessions -split('=')

                               $load =   $Sessions[1]

                               $load=$load.Trim("}")                  

                               if ($load -eq   0) {Stop-Computer $DeviceName

                               $ServersinUse=Get-Content   $SessionHostFile | Where-Object {$_ -ne $DeviceName}

                               $ServersinUse |   Out-File $SessionHostFile

                               }

                                               else   {& $exe * /Server:$DeviceName /time:120 "This server will be shut   down within five minutes. Please log-off. You can directly login again"

                                               start-sleep   300

                                               Stop-Computer   $DeviceName -Force

                                               }

                }

Script 3: Place Sessions Host in maintenance and shutdown

When the hypervisor administrator finished his maintenance activities the XenApp Session Host can be put back in production. Also this part is divided in two parts. The first part is script 3 in which the Worker Group settings are restored and the machine is taken out of maintenance mode. This is based on the file from script 2 configured at the variable $ServertoWorkerGroup. Therefore please check that the variable in this script points to the same location as in script two.

The script will read this file (which now includes the Reboot Worker Group the machine was member of) and adds the machine to his original Worker Group. Also the machine is removed from the reboot Worker Group, so the XenApp administrators know that the server will be back in production again.

#---------------------------------------------------------------------

# Script:   EndSessionHostMainteance.ps1

# Take XenApp   Session Hosts online after ESX maintenance

# Creator: Wilco van   Bragt

# Creation Date:   10-10-2014

#---------------------------------------------------------------------

# Version: 0.1

# By: Wilco van   Bragt

# Date: 10-10-2014

# Changes: Initial   release

#---------------------------------------------------------------------

# Textfile with   XenApp Session Hosts is required (generated by SessionHostMaintenance Script)

# Requirements:

# - Scripts needs to run on server with   Citrix PowerShell cmdlets

#---------------------------------------------------------------------

# Import Modules

#---------------------------------

add-pssnapin   Citrix.*

#Define Variables

#---------------------------------

$MaintenanceWG="Maintenance"

$ServertoWorkerGroup="c:\Temp\ServertoWorkerGroup.txt"

#Read Session Hosts   names, remove server from maintenace WG and add to original Reboot WG

#---------------------------------

$SessionHosts=Get-Content   $ServertoWorkerGroup

foreach ($line in   $SessionHosts)

                {              $line = $line -split(',')

                               $DeviceName =   $Line[0]

                               write-host   "$DeviceName"

                               $WG = $Line[1]

                               write-host   "$WG"

                               add-xaworkergroupserver   $WG $DeviceName

                               remove-xaworkergroupserver   $MaintenanceWG $DeviceName

                               Set-XAServerLogOnMode   -LogOnMode AllowLogOns -ServerName $DeviceName

                               }

Remove-Item   $ServertoWorkerGroup -Force

Script 4: Place Sessions Host in maintenance and shutdown

The last script is pretty simple. The last step is to start the virtual machines again on the hypervisor. This can be done in script 3 as well, but to really divide the steps I created a separate script. The script will ask the location of the original file created in script one. The file will be read and for each machine the start-vm command will be provided to start the machine again.

#---------------------------------------------------------------------

# Script:   StartXenAppSessionHosts.ps1

# Start XenApp   Session Hosts after maintenance

# Creator: Wilco van   Bragt

# Creation Date:   15-10-2014

#---------------------------------------------------------------------

# Version: 0.1

# By: Wilco van   Bragt

# Date: 15-10-2014

# Changes: Initial   release

#---------------------------------------------------------------------

# # Requirements:

# - Scripts needs to run on server with   VMware ESX PowerShell cmdlets

# - File with XenApp Session Hosts created by   GetXenAppSessionHosts.ps1

#---------------------------------------------------------------------

# Import Modules

#---------------------------------

Add-PSSnapin   VMware.VimAutomation.Core

# Provide file to   store XenApp Session Hosts

#---------------------------------

$XenAppSessionHostFile=Read-Host   "Please enter the filename and path which contains the XenApp Session   Hosts"

# Read Session Host   XenApp Session Hosts

#---------------------------------

$SessionHosts=Get-Content   $XenAppSessionHostFile

foreach ($line in   $SessionHosts)

                { $DeviceName = $Line

                               start-vm   $DeviceName

In this article I provided and explained four scripts, which are used to arrange that maintenance tasks can be executed on the hypervisor platform. It shows that lots of stuff can be easily automated in small and simple scripts. Use them for your needs.