Updated: 02/09/2016 – Fixed multiple bugs, added disk space monitoring and now using PowerShell remoting so the script no longer needs to be run directly on a Citrix Server

Details

Recently I had a need for a tool that would easily allow me or our Service Desk to monitor the health of our XenApp farm, there are obviously commercial products for this but I just needed something simple and free so I created this PowerShell script.

I have been running this on XenApp 6.5.

The script shows,

  • The total number of users logged onto the farm.
  • Each servers uptime
  • Each servers number of logged on users
  • Each servers Citrix load metric
  • Each servers RAM usage %
  • Each servers CPU usage %
  • Each servers C: drive free space – Added 02/09/2014
  • Each servers Logon Mode
  • (Optional) If each server is currently a member of a give publish desktop
  • (Optional) The script also allows you to monitor CPU and RAM usage on a short list of other servers such as Store Front and SQL servers.

Parameters

At the top of the script the follow parameters can be set as needed

ManagementServer = The name of a Citrix server the script will transact with

REFRESHINTERVAL = how many seconds the script should wait after getting to the bottom before starting to starting from the top again

EXCLUDESERVERS = a comma separated list of Citrix servers to exclude from being monitored

ServerDesktopName = The name of the published server desktop you wish to check id servers are a membr of, this can be left blank

CheckAdditionalServers = true / false Do you want to define a list of exta servers to monitor CPU and RAM on such as SQL servers

EXTRASERVERS = The list of additional not Citrix servers to monitor

Running the Script

The client running the script will need XenApp PowerShell SDK (at least version 6.5) installed https://www.citrix.com/downloads/xenapp/sdks/powershell-sdk.html

I generally run a PowerShell script like this powershell -file <patch to XenApp Health script>

If you get a message like the below you  will need to run set-executionpolicy remotesigned cannot be loaded because the execution of scripts is disabled on this system. Please see “get-help about_signing” for more details.

The Script

############################################################################
##                                                                        ##
##  SCRIPT.........:  XenAppHealth.ps1                                    ##
##  AUTHOR.........:  Phil Eddies                                         ##
##  LINK...........:  http://www.geekshangout.com/content/citrix-xenapp-health-monitor                                      ##
##  VERSION........:  1.4                                                 ##
##  DATE...........:  02/09/2015                                          ##
##  DESCRIPTION....:  This script collects Citrix Server Information and         ##
##                    displays the results on the screen.                 ##
##                                                                        ##
##                                                                        ##
############################################################################

#-----------------------------------------------------------------------------------
#DEFINE
#-----------------------------------------------------------------------------------

Add-PSSnapin "Citrix.XenApp.Commands"
$ManagementServer = "CitrixSRV_01.charleswells.co.uk"

[int]$Global:REFRESHINTERVAL = "5" # Define the refresh interval (in seconds) for processing
$Global:EXCLUDESERVERS = "CitrixSRV_12,CitrixSRV_13" #Define which Citrix servers should be excluded from processing. Comma seperated list, short names only, case insensitive (for example "CORPCTX01,CORPCTX02,CORPCTX05")
$Global:EXCLUDESERVERS_SERVERDESKTOP = "CitrixPublishedApp_01" #Define whcih Citrix servers should be excluded from checking server desktop membership 
$Global:EXTRASERVERS = "MailSRV01,FileSRV01,SQLSRV01" #Define a list of additional non Citrix windows server to monitor RAM and CPU on
$ServerDesktopName = "Server Desktop"

$infiniteLoop = $true #DO NOT CHANGE Create an infinite loop variable

#-----------------------------------------------------------------------------------
#SETUP THE WINDOW
#-----------------------------------------------------------------------------------
Clear-Host #Clear the screen
$a = (Get-Host).UI.RawUI
$a.BackgroundColor = "darkgreen"
$a.ForegroundColor = "white"
$a.WindowTitle = "XenApp Health 1.4"
$a.WindowTitle

#Full Screen
mode 300

#Windowed Mode - comment out mode 300 above
#$b = $a.WindowSize
#$b.Height = 40
#$a.WindowSize = $b

$HostInfo = @() #Reset the array
$ExtraHostInfo = @() #Reset the array
$UsersLoggedOn = 0
$firstLoop = $true

#-----------------------------------------------------------------------------------
#FUNCTIONS
#-----------------------------------------------------------------------------------
function ServerOnline {
	$server = "$args" # Create a variable named server from the first passed variable
	$serverload = @(get-xaserverload -computername $ManagementServer | Where {$_.ServerName -eq $server}) # Create a query to validate the server is online before proceeding
	foreach ($result in $serverload){
		return $true
	}
}

function inServerDesktop {
    $server = "$args"

    $serverdesktop_servers = @(Get-XAServer -computername $ManagementServer -BrowserName $ServerDesktopName | select ServerName | Where {$_.ServerName -eq $server})
    
	foreach ($result in $serverdesktop_servers){
		return $true
	}    
}

function clearLineMarker {
     foreach ($result1 in $HostInfo){
        $result1.L = "  "
	}   
    
    foreach ($result2 in $ExtraHostInfo){
        $result2.L = "  "
	}                 

}

function Format-Color([hashtable] $Colors = @{}, [switch] $SimpleMatch) {
	$lines = ($input | Out-String) -replace "`r", "" -split "`n"
	foreach($line in $lines) {
		$color = ''
		foreach($pattern in $Colors.Keys){
			if(!$SimpleMatch -and $line -match $pattern) { $color = $Colors[$pattern] }
			elseif ($SimpleMatch -and $line -like $pattern) { $color = $Colors[$pattern] }
		}
		if($color) {
			Write-Host -BackgroundColor $color $line
		} else {
			Write-Host $line
		}
	}
}

#-----------------------------------------------------------------------------------
#MAIN LOOP
#-----------------------------------------------------------------------------------
$excludedservers = $GLOBAL:EXCLUDESERVERS.Split(',')
$excludedservers_serverdesktop = $GLOBAL:EXCLUDESERVERS_SERVERDESKTOP.Split(',')
$extra_servers = $GLOBAL:EXTRASERVERS.Split(',')

do { # Create an infinite loop

    $farmservers = get-xaserver -computername $ManagementServer | sort-object -property ServerName # Create an array with all servers sorted alphabetically

    $UsersLoggedOn = 0
    $sessions_farm = @(get-xasession -computername $ManagementServer | Where {$_.State -ne "Listening"} | Where {$_.State -ne "Disconnected"} | Where {$_.SessionName -ne "Console"} | group AccountName) # Create a query against server passed through as first variable where protocol is Ica. Disregard disconnected or listening sessions
	foreach ($session_farm in $sessions_farm) {$UsersLoggedOn+=1} # Count number of sessions, if there are any active sessions, go to sleep for 5 minutes
	{}
    
    $server = ""
	
	foreach ($farmserver in $farmservers){
        #Get the current server name
		$server = $farmserver.ServerName
        $serverLogOnMode = $farmserver.LogOnMode
		
        
		if ($excludedservers -notcontains $server) {
            if (ServerOnline $server) {
    			#
    			# 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_string = [string]$uptime.days + " Days " + $uptime.hours + "h " + $uptime.minutes + "m " + $uptime.seconds + "s"
    			$uptime_string = [string]$uptime.days + "d " + $uptime.hours + "h "
				
    			#
    			# Get Active Sessions
    			#
    			$session_count = 0    
                
                #Gives number of users, remove  | group AccountName to get the number of sessions instead
                $sessions = @(get-xasession -computername $ManagementServer | Where {$_.ServerName -eq $server} | Where {$_.State -ne "Listening"} | Where {$_.State -ne "Disconnected"} | Where {$_.SessionName -ne "Console"} | 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} # Count number of sessions, if there are any active sessions, go to sleep for 5 minutes
    			{}
			
    			
    			# 
    			# Get Server Load
    			$server_load = get-xaserverload -computername $ManagementServer | Where {$_.ServerName -eq $server}
    			
    			#
    			# CPU and RAM
    			#
    			$SystemInfo = Get-WmiObject -Class Win32_OperatingSystem -computername $server | Select-Object Name, TotalVisibleMemorySize, FreePhysicalMemory 
     
    			 #Retrieving the total physical memory and free memory 
    			 
    			$TotalRAM = $SystemInfo.TotalVisibleMemorySize/1MB 
    			 
    			$FreeRAM = $SystemInfo.FreePhysicalMemory/1MB 
    			 
    			$UsedRAM = $TotalRAM - $FreeRAM 
    			 
    			$RAMPercentUsed = [Math]::Round(($UsedRAM / $TotalRAM) * 100 ,0)
    			 
    			#Calculating the memory used and rounding it to 2 significant figures 
    			 
    			$CPU = Get-WmiObject win32_processor -computername $server  | Measure-Object -property LoadPercentage -Average | Select Average     
    			 
    			#Get CPU load of machine 
    			 
				$CPULoad = [system.math]::round($CPU.average,0)
				
                #
                # Disk Space
                #
				# Get free Disk space of the servers C Drive                         
				$DiskSpaceC = $disk = Get-WmiObject -class Win32_LogicalDisk -computername $Server -filter "DeviceID='C:'"
				#$DiskFreePercentC = $disk.freespace / $disk.size * 100   
				$DiskFreeC = [Math]::Round($disk.freespace  / 1GB  ,1)

    			# Lets throw them into an object for outputting
                clearLineMarker
                
    			$objHostInfo = New-Object System.Object
                $objHostInfo | Add-Member -MemberType NoteProperty -Name "L" -Value "->"
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "SERVER" -Value $server
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "UPTIME" -Value $uptime_string
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "USERS" -Value $session_count
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "LOAD" -Value $server_load
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "RAM %" -Value $RAMPercentUsed
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "CPU %" -Value $CPULoad
				$objHostInfo | Add-Member -MemberType NoteProperty -Name "C: FREE (GB)" -Value $DiskFreeC
                                
                # Shorten ProhibitNewLogOnsUntilRestart to something that will display
                if ($serverLogOnMode -eq "ProhibitNewLogOnsUntilRestart")
                {
                    $objHostInfo | Add-Member -MemberType NoteProperty -Name "LOGON MODE" -Value "##OutForReboot##"
                } else {
                    $objHostInfo | Add-Member -MemberType NoteProperty -Name "LOGON MODE" -Value $serverLogOnMode
                }
                
				if ($excludedservers_serverdesktop -notcontains $server) {
					if (inServerDesktop $server)
					{
						$objHostInfo | Add-Member -MemberType NoteProperty -Name "IN DESKTOP" -Value "Yes"
					} else {
						$objHostInfo | Add-Member -MemberType NoteProperty -Name "IN DESKTOP" -Value "##No##"
					}
				} else {
					$objHostInfo | Add-Member -MemberType NoteProperty -Name "IN DESKTOP" -Value " "
				}
            } else {
                # SERVER OFFLINE
                
                # Lets throw them into an object for outputting
                clearLineMarker
                
    			$objHostInfo = New-Object System.Object
                $objHostInfo | Add-Member -MemberType NoteProperty -Name "L" -Value "->"
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "SERVER" -Value $server
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "UPTIME" -Value "DOWN"
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "USERS" -Value " "
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "LOAD" -Value " "
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "RAM%" -Value " "
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "CPU%" -Value " "
    			$objHostInfo | Add-Member -MemberType NoteProperty -Name "C: FREE (GB)" -Value " "
                $objHostInfo | Add-Member -MemberType NoteProperty -Name "LOGON MODE" -Value " "
                $objHostInfo | Add-Member -MemberType NoteProperty -Name "IN DESKTOP" -Value " "   
            }
            

            if ($firstLoop)
            {
            
            }else{
                #Delete the server from the array first
                $HostInfo = $HostInfo  |? {$_.SERVER -ne $server}
            }

			# Lets dump our info into an array
			$HostInfo += $objHostInfo
			
            
            Clear-Host
            Write-Host "USERS LOGGED ON: " $UsersLoggedOn
           $HostInfo | Sort-Object SERVER | format-table -auto | Format-Color @{'##OutForReboot##' = 'DarkGray';'##No##' = 'Red'; 'DOWN' = 'Black'}
           $ExtraHostInfo | Sort-Object SERVER | format-table -auto
		}	
        
        }
		
		  foreach ($extraserver in $extra_servers){
			  $SystemInfo = Get-WmiObject -Class Win32_OperatingSystem -computername $extraserver | Select-Object Name, TotalVisibleMemorySize, FreePhysicalMemory 
 
# 			 Retrieving the total physical memory and free memory 
			 
			  $TotalRAM = $SystemInfo.TotalVisibleMemorySize/1MB 
			 
			  $FreeRAM = $SystemInfo.FreePhysicalMemory/1MB 
			 
			  $UsedRAM = $TotalRAM - $FreeRAM 
			 
			  $RAMPercentUsed = [Math]::Round(($UsedRAM / $TotalRAM) * 100 ,0)
			 
 #			Calculating the memory used and rounding it to 2 significant figures 
			 
			  $CPU = Get-WmiObject win32_processor -computername $extraserver  | Measure-Object -property LoadPercentage -Average | Select Average     
			 
 #			Get CPU load of machine 
			 
		    $CPULoad = [system.math]::round($CPU.average,0)

                #
                # Disk Space
                #
				# Get free Disk space of the servers C Drive                         
				$DiskSpaceC = $disk = Get-WmiObject -class Win32_LogicalDisk -computername $extraserver -filter "DeviceID='C:'"
				#$DiskFreePercentC = $disk.freespace / $disk.size * 100   
				$DiskFreeC = [Math]::Round($disk.freespace  / 1GB  ,1)


 #			Lets throw them into an object for outputting
            clearLineMarker
            
			  $objExtraHostInfo = New-Object System.Object
              $objExtraHostInfo | Add-Member -MemberType NoteProperty -Name "L" -Value "->"
			  $objExtraHostInfo | Add-Member -MemberType NoteProperty -Name "SERVER" -Value $extraserver
			  $objExtraHostInfo | Add-Member -MemberType NoteProperty -Name "RAM%" -Value $RAMPercentUsed
			  $objExtraHostInfo | Add-Member -MemberType NoteProperty -Name "CPU%" -Value $CPULoad
			  $objExtraHostInfo | Add-Member -MemberType NoteProperty -Name "C: FREE (GB)" -Value $DiskFreeC
			
             if ($firstLoop)
            {
            
             } else {
                  #Delete the server from the array first
                  $ExtraHostInfo = $ExtraHostInfo  |? {$_.SERVER -ne $extraserver}
              }
            
			  $ExtraHostInfo += $objExtraHostInfo
			
			  Clear-Host
              Write-Host "USERS LOGGED ON: " $UsersLoggedOn
			  $HostInfo | Sort-Object SERVER | format-table -auto  | Format-Color @{'##OutForReboot##' = 'DarkGray';'##No##' = 'Red'; 'DOWN' = 'Black'}
			  $ExtraHostInfo | Sort-Object SERVER | format-table -auto
	 }
	
    $firstLoop = $false
	start-sleep -s ($REFRESHINTERVAL) # Sleep for REFRESHINTERVAL


	
} while ($infiniteLoop -eq $true) # Infinite loop