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.

Citrix Reboot Script - Triggers
Citrix Reboot Script - Actions

Parameters

EXCLUDESERVERSA comma separated list of Citrix servers to exclude from the reboot routine.
REBOOTTHRESHOLDHow many days of uptime before a server should be considered for a reboot.
MAXACTIVESESSIONAGEThe 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"
				}
				
			}
		}
	}