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
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
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.)