Overview
I have a Citrix farm and have written the below PowerShell script to automatically reboot the servers when there are no active connections.
Usage
The script would need to be scheduled to run on a Citrix server with the XenApp PowerShell SDK installed https://www.citrix.com/downloads/xenapp/sdks/powershell-sdk.html
I have setup a scheduled task than runs every hour during none business hours.
Parameters
EXCLUDESERVERS | A comma separated list of Citrix servers to exclude from the reboot routine. |
REBOOTTHRESHOLD | How many days of uptime before a server should be considered for a reboot. |
MAXACTIVESESSIONAGE | The maximum time in hours a session is allowed to be active. Any users with an active session over this time will get a 5 min warning and a message before the server reboots. |
The Script
#-----------------------------------------------------------------------------------
#DEFINE
#-----------------------------------------------------------------------------------
Add-PSSnapin "Citrix.XenApp.Commands"
$Global:EXCLUDESERVERS = "CTX-MANAGE-01" #Define which Citrix servers should be excluded from processing. Comma seperated list, short names only, case insensitive (for example "CORPCTX01,CORPCTX02,CORPCTX05")
[int]$Global:REBOOTTHRESHOLD = "1" # Process the server for reboot if it has been up longer than this number of days
[int]$Global:MAXACTIVESESSIONAGE = "15" #Max age for an active session in hours
$Global:EventLog = New-Object -type System.Diagnostics.Eventlog -argumentlist Application # Creates a global object for logging to the Application event log
$Global:EventLog.Source = "Citrix Reboot Script" # All event logs will be entered with the source of Citrix Reboot Script
$infiniteLoop = $true #DO NOT CHANGE Create an infinite loop variable
#-----------------------------------------------------------------------------------
#FUNCTIONS
#-----------------------------------------------------------------------------------
#Check to see if the server is online by checking its Citrix load
function ServerOnline {
$server = "$args" # Create a variable named server from the first passed variable
$serverload = @(get-xaserverload | Where {$_.ServerName -eq $server}) # Create a query to validate the server is online before proceeding
foreach ($result in $serverload){
return $true
}
}
#The reboot function
function StartReboot {
$server = "$args" # Create a variable named server from the first passed variable
Invoke-Expression "Shutdown.exe /m $server /r /t 300 /f /c ""A auto reboot is about to run please save your work and logoff.""" # Initiate shutdown on remote server
#Wait for the server to come up and have a load under 5000 before processing the next server
do {
$rebooted = $false # Reset variable back to false before checking for reboot
start-sleep -s 360 # Wait for 360 seconds between checking for reboot completion
$serverload = @(get-xaserverload | Where {$_.Load -lt "5000"} | Where {$_.ServerName -eq $server}) # Create a query to validate the server is online and load evaluator has reset less than 5000 before proceeding
foreach ($result in $serverload){
$rebooted = $true # Server has rebooted and the load evaluator is less than 5000, proceed to next server
$EventLog.WriteEntry($server + " rebooted properly, load rebalanced. Proceeding with subsequent servers.","Information","811")
}
} while ($rebooted -eq $false) # Loop until the server has completed its reboot and load evaluator has returned to idle state
}
#-----------------------------------------------------------------------------------
#MAIN
#-----------------------------------------------------------------------------------
$excludedservers = $GLOBAL:EXCLUDESERVERS.Split(',')
$farmservers = get-xaserver | sort-object -property ServerName # Create an array with all servers sorted alphabetically
foreach ($farmserver in $farmservers){
#Work out now minus $MAXACTIVESESSIONAGE
$time2 = Get-Date -Format "MMM dd yyyy HH:mm"
$time1 = Get-Date
$tminus_maxactive = $time1.addhours(-$MAXACTIVESESSIONAGE)
#Get the current server name
$server = $farmserver.ServerName
#If the server is not in the exclude list
if ($excludedservers -notcontains $server) {
if (ServerOnline $server) {
write-host "=========================="
write-host "SERVER = " $server
write-host "=========================="
#
# Get Uptime
#
$os = gwmi Win32_OperatingSystem -computerName $server
$lastbootuptime = $os.ConvertToDateTime($os.LastBootUpTime)
$starttime = $OS.converttodatetime($OS.LastBootUpTime)
$uptime = New-TimeSpan (get-date $Starttime)
$uptime_days = 0
$uptime_days = [int]$uptime.days
write-host "UPTIME = " $uptime_days
#
# Get Active Sessions
#
$session_count = 0
#Gives number of active sessions
$sessions = @(get-xasession | Where {$_.ServerName -eq $server} | Where {$_.State -ne "Listening"} | Where {$_.State -ne "Disconnected"} | Where {$_.SessionName -ne "Console"} | Where {$_.LogOnTime -gt $tminus_maxactive} | group AccountName) # Create a query against server passed through as first variable where protocol is Ica. Disregard disconnected or listening sessions
foreach ($session in $sessions)
{
$session_count+=1
}
write-host "SESSIONS = " $session_count
#
# Reboot if over the threshold and there are no active sessions
#
if ($uptime_days -ge $REBOOTTHRESHOLD -and $session_count -eq 0) {
write-host "REBOOT = YES"
$EventLog.WriteEntry("Initiating reboot process on " + $server + " Uptime Days = " + $uptime_days + " Active Sessions = " + $session_count + ".","Information","911")
StartReboot $server # Initialize the StartReboot function
} else {
write-host "REBOOT = NO"
}
}
}
}