Citrix XenApp Health Monitor

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 following 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 if servers are a member of, this can be left blank

CheckAdditionalServers = true/false Do you want to define a list of extra 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...........:  https://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

2 thoughts on “Citrix XenApp Health Monitor”

  1. Would this help for being able to run the script:

    Create your script (in my case \\server\share\myscript.ps1)
    Create a *.cmd which executes your *.ps1 file:
    powershell.exe -NoLogo -ExecutionPolicy bypass -File \\server\share\myscript.ps1
    Execute the *.cmd you’ve created

    Reply
  2. I’d love if ExcludeServers could take a wildcard.
    (I want to run this against 20 servers in a XenApp Farm that has 400 + servers.)

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.