Application virtualization, IoT and Cloud Computing, Blog of Sacha Thomet

Citrix PVS HealthCheck

Problem: There is currently no free tool to check the health status of a Citrix Provisioning Services farm with all Citrix relevant parameters. There are monitoring tools which tell you if a server is up and running or if it’s not, but now tool who gives you a special overview for Citrix Provisioning Services according to how much targets on which server, how much communication retries on which target an so on.

Solution:
Since some year’s I’m using the XenApp Health Check from Jason Poyner (deptive.co.nz) to get daily a status of my environment(s). I really appreciate the work from Jason, if you don’t know it until now, you really need have a look on his blog and on the Script . The mentioned XenApp Farm Health Check Script gave me the inspiration to create a similar Health Check Script for my Citrix Provisioning Server farm. To be honest, and I’m happy that Jason doesn’t mind, that I have “recycled” some parts, or say the “framework” of his XenApp Farm Health Check script to build my Script.

The script I built can be scheduled with a Windows task which trigger a run once a day and send out a email with the attached HTML-Report:

I’ve written the Script to perform the checks in 3 Parts: 

Target Devices:

  • Ping
  • Retry (Threshold configurable, default is 15)
  • PVS Disk (Value which I read from PVS farm with get-mcli)
  • PVS Disk (Value which is written in the personality.ini on the Target)
  • PVS disk version (V1.2)
  • The vDisk-Store *
  • PVS Server
  • WriteCache Size

* like you can read in my BlogPost “Achieve fastest Citrix Provisioning Target Device” I propose to have the vDisk Store local on each provisioning server. So I can check with that column if the Target is booting from the correct vDisk store. The Master Store is on CIFS and only used for updates of the Image.

vDisks:

  • Replication state (PVS don’t replicate anything – but can check if the vDisks on all servers are equal)
  • DeviceCount to see how many targets are currently reading from this Image
  • Load Balancing algorithm
    (Green if Best Effort because I want to have subnet affinity – feel free to change for your need.  I have two data centers with two separated subnet’s – so I can guarantee that each target stream from his own data centre as long the server is available. Just in case of an outage of all PVS server in one DC it fail over to the other DC  )
  • WriteCache Type
  • Name & Date of the vDisk

PVS Servers:

  • Ping
  • Active
  • How many device are connected

Don’t forget to register the PVS SnapIn DLL e.g. with .Net Framework 2.0 with the command:

“C:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727\InstallUtil.exe” “C:\Program Files\Citrix\Provisioning Services Console\McliPSSnapIn.dll”

Otherwise you get a blank output. If you have another .Net Framework version or path, please adjust the path in the command! 

#==============================================================================================
# Created on: 08.2014            Version: 1.63
# Created by: Sacha Thomet, blog.appcloud.ch / sachathomet.ch
# Filename: Citrix-PVS-Farm-Health-toHTML.ps1
#
# Special Thanks to:
# - Jason Poyner ... I've borrowed parts of the script and ideas to create this 
#   PVS health check script. Check his excellent XenApp Health Check @ techblog.deptive.co.nz.
# - Martin Hartmann to share his PowerShell KnowHow with me.
#
# Description: This script checks Citrix Provisioning Server, Farm, vDisk & Target devices.
#
# Prerequisite: Script must run on a PVS server, where MCLI snap-in is registered.
# Register SnapIn with command: C:\WINDOWS\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe 
# "C:\Program Files\Citrix\Provisioning Services Console\McliPSSnapIn.dll"
#
# Call by : Scheduled Task, e.g. once a day
#
# Change Log: 
#      V1.1:  Consolidated code
#      V1.2:  Add possibility to check only specified Versions
#      V1.3:  New version after Citrix Synergy 2015 GeekOvation Award nomination. 
#		      With correction of typos and add some documentation lines. 
#      V1.4:  Possibility for multiple stores (Thanks to Kafedzhiev  for the code) (08-2015) 
#      V1.5:  Show create date of vDisk, FileName and the count of the used vDisk (09-2015)
#      V1.6:  Add RamCache used from each target, added code by Jonathan Pitre, 
#		      code from Matthew Nics http://mattnics.com/?p=414 (10-2015) 
#      V1.61: Changed RamCache to general WriteCache, add possibility to get Size of Cache on HD
#      V1.62: Correction in Header to show correct farm name instead a "6", correction in 
#		      LoadBalancingAlgorithm, Error if a disk is assigned fix to a server. 
#      V1.63: Check of Stream-, Soap-, and TFTP-Service (12-2015)
#
#
#      THIS SCRIPT IS FOR PVS 7.6 AND BELOW. ASK FOR BETA VERSION OF PVS 7.7 HEALTH CHECK SCRIPT 
#      IF YOU ARE USING THE TECHPREVIEW OF PVS 7.7 WITH COMPLETE NEW POSH-IMPLEMENTATION
#
#
#==============================================================================================
if ((Get-PSSnapin "McliPSSnapIn" -EA silentlycontinue) -eq $null) {
try { Add-PSSnapin McliPSSnapIn -ErrorAction Stop }
catch { write-error "Error loading PVS McliPSSnapIn PowerShell snapin"; Return }
}
# Change the below variables to suit your environment
#==============================================================================================
# Target Device Health Check threshold:
$retrythresholdWarning= "15" # define the Threshold from how many retries the color switch to red
 
# Include for Device Collections, type "every" if you want to see every Collection 
# Example1: $Collections = @("XA65","XA5")
# Example2: $Collections = @("every")
$Collections = @("every")
 
# Information about the site you want to check:
$siteName="site" # site name on which the according Store is.
  
# E-mail report details
$emailFrom = "email@company.ch"
$emailTo = "citrix@company.ch"#,"sacha.thomet@appcloud.ch"
$smtpServer = "mailrelay.company.ch"
$emailSubjectStart = "PVS Farm Report"
$mailprio = "High"
#==============================================================================================
 
$currentDir = Split-Path $MyInvocation.MyCommand.Path
$logfile = Join-Path $currentDir ("PVSHealthCheck.log")
$resultsHTM = Join-Path $currentDir ("PVSFarmReport.htm")
$errorsHTM = Join-Path $currentDir ("PVSHealthCheckErrors.htm") 
 
#Header for Table 1 "Target Device Checks"
$TargetfirstheaderName = "TargetDeviceName"
$TargetheaderNames = "CollectionName", "Ping", "Retry", "vDisk_PVS", "vDisk_Version", "WriteCache", "PVSServer"
$TargetheaderWidths = "4", "4", "4", "4", "2" , "4", "4"
$Targettablewidth = 1200
#Header for Table 2 "vDisk Checks"
$vDiksFirstheaderName = "vDisk"
$vDiskheaderNames = "Store", "vDiskFileName", "deviceCount", "CreateDate" , "ReplState", "LoadBalancingAlgorithm", "WriteCacheType"
$vDiskheaderWidths = "4", "8", "2","4", "4", "4", "4"
$vDisktablewidth = 1200
#Header for Table 3 "PV Server"
$PVSfirstheaderName = "PVS Server"
$PVSHeaderNames = "Ping", "Active", "deviceCount","SoapService","StreamService","TFTPService"
$PVSheaderWidths = "4", "4", "4","4","4","4"
$PVStablewidth = 600
#Header for Table 4 "Farm"
$PVSFirstFarmheaderName = "FarmChecks"
$PVSFarmHeaderNames = "Setting", "Value"
$PVSFarmWidths = "4", "8", "8"
$PVSFarmTablewidth = 400
 
#==============================================================================================
#log function
function LogMe() {
Param(
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $logEntry,
[switch]$display,
[switch]$error,
[switch]$warning,
[switch]$progress
)
 
 if ($error) {
$logEntry = "[ERROR] $logEntry" ; Write-Host "$logEntry" -Foregroundcolor Red}
elseif ($warning) {
Write-Warning "$logEntry" ; $logEntry = "[WARNING] $logEntry"}
elseif ($progress) {
Write-Host "$logEntry" -Foregroundcolor Green}
elseif ($display) {
Write-Host "$logEntry" }
  
 #$logEntry = ((Get-Date -uformat "%D %T") + " - " + $logEntry)
$logEntry | Out-File $logFile -Append
}
#==============================================================================================
function Ping([string]$hostname, [int]$timeout = 200) {
$ping = new-object System.Net.NetworkInformation.Ping #creates a ping object
try {
$result = $ping.send($hostname, $timeout).Status.ToString()
} catch {
$result = "Failure"
}
return $result
}
#==============================================================================================
Function writeHtmlHeader
{
param($title, $fileName)
$date = ( Get-Date -format R)
$head = @"
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
<title>$title</title>

<STYLE TYPE="text/css">
<!-- td { font-family: Tahoma; font-size: 11px; border-top: 1px solid #999999; border-right: 1px solid #999999; border-bottom: 1px solid #999999; border-left: 1px solid #999999; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow: hidden; } body { margin-left: 5px; margin-top: 5px; margin-right: 0px; margin-bottom: 10px; table { table-layout:fixed; border: thin solid #000000; } -->
</style>

</head>
<body>

<table width='1200'>

<tr bgcolor='#CCCCCC'>

<td colspan='7' height='48' align='center' valign="middle">
<font face='tahoma' color='#003399' size='4'>
<strong>$title - $date</strong></font>
</td>

</tr>

</table>

"@
$head | Out-File $fileName
}
# ==============================================================================================
Function writeTableHeader
{
param($fileName, $firstheaderName, $headerNames, $headerWidths, $tablewidth)
$tableHeader = @"

<table width='$tablewidth'>
<tbody>

<tr bgcolor=#CCCCCC>

<td width='6%' align='center'><strong>$firstheaderName</strong></td>

"@
$i = 0
while ($i -lt $headerNames.count) {
$headerName = $headerNames[$i]
$headerWidth = $headerWidths[$i]
$tableHeader += "
<td width='" + $headerWidth + "%' align='center'><strong>$headerName</strong></td>

"
$i++
}
$tableHeader += "</tr>

"
$tableHeader | Out-File $fileName -append
}
# ==============================================================================================
Function writeTableFooter
{
param($fileName)
"</table>

"| Out-File $fileName -append
}
#==============================================================================================
Function writeData
{
param($data, $fileName, $headerNames)
  
 $data.Keys | sort | foreach {
$tableEntry += "
<tr>"
$computerName = $_
$tableEntry += ("
<td bgcolor='#CCCCCC' align=center><font color='#003399'>$computerName</font></td>

")
#$data.$_.Keys | foreach {
$headerNames | foreach {
#"$computerName : $_" | LogMe -display
try {
if ($data.$computerName.$_[0] -eq "SUCCESS") { $bgcolor = "#387C44"; $fontColor = "#FFFFFF" }
elseif ($data.$computerName.$_[0] -eq "WARNING") { $bgcolor = "#FF7700"; $fontColor = "#FFFFFF" }
elseif ($data.$computerName.$_[0] -eq "ERROR") { $bgcolor = "#FF0000"; $fontColor = "#FFFFFF" }
else { $bgcolor = "#CCCCCC"; $fontColor = "#003399" }
$testResult = $data.$computerName.$_[1]
}
catch {
$bgcolor = "#CCCCCC"; $fontColor = "#003399"
$testResult = ""
}
  
 $tableEntry += ("
<td bgcolor='" + $bgcolor + "' align=center><font color='" + $fontColor + "'>$testResult</font></td>

")
}
  
 $tableEntry += "</tr>

"
  
  
 }
  
 $tableEntry | Out-File $fileName -append
}
# ==============================================================================================
Function writeHtmlFooter
{
param($fileName)
@"

<table>

<table width='1200'>

<tr bgcolor='#CCCCCC'>

<td colspan='7' height='25' align='left'>

<font face='courier' color='#000000' size='2'><strong>Retry Threshold =</strong></font><font color='#003399' face='courier' size='2'> $retrythresholdWarning
<tr></font>

<tr bgcolor='#CCCCCC'>
</td>

</tr>


<tr bgcolor='#CCCCCC'>
</tr>

</table>

</body>
</html>
"@ | Out-File $FileName -append
}
 
#==============================================================================================
# == MAIN SCRIPT ==
#==============================================================================================
rm $logfile -force -EA SilentlyContinue
"Begin with Citrix Provisioning Services HealthCheck" | LogMe -display -progress
" " | LogMe -display -progress
 
  
 
# ======= PVS Target Device Check ========
"Check PVS Target Devices" | LogMe -display -progress
" " | LogMe -display -progress
$allResults = @{}
$pvsdevices = mcli-get device -f deviceName | Select-String deviceName
foreach($target in $pvsdevices)
 {
  
 $tests = @{} 
  
 # Check to see if the server is in an excluded folder path
$target | Select-String deviceName 
 $_targetshort = $target -replace "deviceName: ",""
 $pvcollectionName = mcli-get deviceinfo -p devicename=$_targetshort | select-string collectionName
$short_collectionName = $pvcollectionName.ToString().TrimStart("collectionName: ")
  
 #Only Check Servers in defined Collections: 
 if ($Collections -contains $short_collectionName -Or $Collections -contains "every") { 
  
 
 $target | Select-String deviceName 
 $_targetshort = $target -replace "deviceName: ",""
$_targetshort | LogMe -display -progress
  
 # Ping server 
 $result = Ping $_targetshort 100
if ($result -ne "SUCCESS") { $tests.Ping = "ERROR", $result }
else { $tests.Ping = "SUCCESS", $result 
 }
  
 #CollectionName
$pvcollectionName = mcli-get deviceinfo -p devicename=$_targetshort | select-string collectionName
$short_collectionName = $pvcollectionName.ToString().TrimStart("collectionName: ")
$tests.CollectionName = "NEUTRAL", "$short_collectionName"
 # Test Retries
$devicestatus = mcli-get deviceinfo -p devicename=$_targetshort -f status
$retrycount = $devicestatus[4].TrimStart("status: ") -as [int]
if ($retrycount -lt $retrythresholdWarning) { $tests.Retry = "SUCCESS", "$retrycount Retry = OK" }
else { $tests.Retry = "WARNING","$retrycount retries!" }
  
 #Check assigned Image
$devicediskFileName = mcli-get deviceinfo -p devicename=$_targetshort | select-string diskFileName
$short_devicediskFileName = $devicediskFileName.ToString().TrimStart("diskFileName: ")
$tests.vDisk_PVS = "SUCCESS", "$short_devicediskFileName"
 #Check assigned Image Version
$devicediskVersion = mcli-get deviceinfo -p devicename=$_targetshort | select-string diskVersion:
$short_devicediskVersion = $devicediskVersion.ToString().TrimStart("diskVersion: ")
$tests.vDisk_Version = "SUCCESS", "$short_devicediskVersion"
 #PVS-Server
$PVSServername = mcli-get deviceinfo -p devicename=$_targetshort | select-string serverName
$short_PVSServername = $PVSServername.ToString().TrimStart("serverName: ")
$tests.PVSServer = "Neutral", "$short_PVSServername"


################ PVS WriteCache SECTION ###############

		
		if (test-path \\$_targetshort\c$\Personality.ini)
		{

			$wconhd = ""
			$wconhd = Get-Content \\$_targetshort\c$\Personality.ini | Where-Object  {$_.Contains("WriteCacheType=4") }
			
			If ($wconhd -match "$WriteCacheType=4") {Write-Host Cache on HDD
			
			#WWC on HD is $wconhd

				# Relative path to the PVS vDisk write cache file
				$PvsWriteCache   = "d$\.vdiskcache"
				# Size of the local PVS write cache drive
				$PvsWriteMaxSize = 10gb # size in GB
			
				$PvsWriteCacheUNC = Join-Path "\\$_targetshort" $PvsWriteCache 
				$CacheDiskexists  = Test-Path $PvsWriteCacheUNC
				if ($CacheDiskexists -eq $True)
				{
					$CacheDisk = [long] ((get-childitem $PvsWriteCacheUNC -force).length)
					$CacheDiskGB = "{0:n2}GB" -f($CacheDisk / 1GB)
					"PVS Cache file size: {0:n2}GB" -f($CacheDisk / 1GB) | LogMe
					#"PVS Cache max size: {0:n2}GB" -f($PvsWriteMaxSize / 1GB) | LogMe -display
					if($CacheDisk -lt ($PvsWriteMaxSize * 0.5))
					{
					   "WriteCache file size is low" | LogMe
					   $tests.WriteCache = "SUCCESS", $CacheDiskGB
					}
					elseif($CacheDisk -lt ($PvsWriteMaxSize * 0.8))
					{
					   "WriteCache file size moderate" | LogMe -display -warning
					   $tests.WriteCache = "WARNING", $CacheDiskGB
					}   
					else
					{
					   "WriteCache file size is high" | LogMe -display -error
					   $tests.WriteCache = "ERORR", $CacheDiskGB
					}
				}              
			   
				$Cachedisk = 0
			   
				$VDISKImage = get-content \\$_targetshort\c$\Personality.ini | Select-String "Diskname" | Out-String | % { $_.substring(12)}
				if($VDISKImage -Match $DefaultVDISK){
					"Default vDisk detected" | LogMe
					$tests.vDisk = "SUCCESS", $VDISKImage
				} else {
					"vDisk unknown"  | LogMe -display -error
					$tests.vDisk = "SUCCESS", $VDISKImage
				}   
			
			}
			else 
			{Write-Host Cache on Ram
			
			#RAMCache
			#Get-RamCache from each target, code from Matthew Nics http://mattnics.com/?p=414
			$RAMCache = [math]::truncate((Get-WmiObject Win32_PerfFormattedData_PerfOS_Memory -ComputerName $_targetshort).PoolNonPagedBytes /1MB)
			$tests.WriteCache = "Neutral", "$RamCache MB on Ram"
		
			}
		
		}
		else 
		{Write-Host WriteCache not readable
		$tests.WriteCache = "Neutral", "Cache not readable"	
		}
		############## END PVS WriteCache SECTION #############
			

#Forward results to $allResult array which will be written in HTM-File
$allResults.$_targetshort = $tests
 }
}
# ======= PVS vDisk Check #==================================================================
"Check PVS vDisks" | LogMe -display -progress
" " | LogMe -display -progress
 
$storenames = mcli-get store | Select-string storename
$vdiskResults = @{}
foreach ($storenameA in $storenames)
{
$storename = $storenameA -replace "storename: ",""
$storeid = mcli-get store -p storeName=$storename | Select-String storeId
$storeid_short = $storeid -replace "storeId: ",""
$alldisks = Mcli-Get disklocator -p siteName=$siteName, storeId=$storeid_short | Select-String diskLocatorName
foreach($disk in $alldisks)
{
$disk1 = $disk | Select-String diskLocatorName
$disklocator_short = $disk1 -replace "diskLocatorName: ",""
foreach($diksloc in $disklocator_short)
{
  
 $VDtests = @{} 
  
 $DiskVersion = Mcli-Get DiskVersion -p diskLocatorName=$disklocator_short, siteName=$siteName, storeName=$storename
$diskreplstatus = $DiskVersion | Select-String goodInventoryStatus
$diskreplstatus_short = $diskreplstatus -replace "goodInventoryStatus: ","" 
  
  
 $disklocator_short
$diskreplstatus_short
  
 # vDiskFileName & createDate 
 $pathA = mcli-get store -p storeName=$storename | Select-String path -casesensitive
$path = $pathA -replace "path: ",""
  
 $diskfilenameA = Mcli-Get DiskVersion -p diskLocatorName=$disklocator_short, siteName=$siteName, storeName=$storename | Select-String diskFileName 
 $diskfilename = $diskfilenameA -replace "diskFileName: ","
"
  
 $createDateA = Mcli-Get DiskVersion -p diskLocatorName=$disklocator_short, siteName=$siteName, storeName=$storename | Select-String createDate 
 $createDate = $createDateA -replace "createDate: ","
"
  
 $VDtests.vDiskFileName = "OK", " $diskfilename"
Write-Host ("Path is $path $disklocator_short $diskfilename")
  
 $VDtests.createDate = "OK", " $createDate"
Write-Host ("Path is $path $disklocator_short $createDate")
  
 $vdiskResults.$disklocator_short = $VDtests
  
  
  
 #Check if correct replicated
if($diskreplstatus_short -eq 1 ){
"$disklocator_short correct replicated" | LogMe
$VDtests.ReplState = "SUCCESS", "Replication is OK"
  
 } else {
"$disklocator_short not correct replicated " | LogMe -display -error
$VDtests.ReplState = "ERROR", "Replication is NOT OK"
}
 # Check deviceCount: 
 $diskdevicecount = $DiskVersion | Select-String deviceCount
$diskdevicecounts_short = $diskdevicecount -replace "deviceCount: ","
" 
 $VDtests.deviceCount = "OK", "$diskdevicecounts_short "
  
  
 #Label Storename 
 $VDtests.Store = "OK", " $storename "
Write-Host ("Store is $storename")
  
 $vdiskResults.$disklocator_short = $VDtests
  
  
# Check for LB-Algorithm
# ----------------------
# Feel free to change it to the the from you desired State (e.g.Exchange a SUCCESS with a WARNING)
# In this default configuration "BestEffort" or "None" is desired and appears green on the output.
# is desired)

#ServeName must be empty! otherwise no LB is active!
$LBnoServer = ""
$LBnoServer_short = ""
$LBnoServer = Mcli-Get disklocator -p siteName=$siteName, storeName=$storename, diskLocatorName=$disklocator_short | Select-String serverName
$LBnoServer_short = $LBnoServer -replace "serverName: ","" 
Write-Host ("vDisk is fix assigned to $LBnoServer")
#not assigned to a server
if ($LBnoServer_short -eq "")
		{
		$LBAlgo = Mcli-Get disklocator -p siteName=$siteName, storeName=$storename | Select-String subnetAffinity
		$LBAlgo_short = $LBAlgo -replace "subnetAffinity: ","" 
		  
		#SubnetAffinity: 1=Best Effort, 2= fixed, 0=none
		if($LBAlgo_short -eq 1 ){
		"LB-Algorythm is set to BestEffort" | LogMe
		$VDtests.LoadBalancingAlgorithm = "SUCCESS", "LB is set to BEST EFFORT"} 
		  
		 elseif($LBAlgo_short -eq 2 ){
		"LB-Algorythm is set to fixed" | LogMe
		$VDtests.LoadBalancingAlgorithm = "WARNING", "LB is set to FIXED"}
		  
		 elseif($LBAlgo_short -eq 0 ){
		"LB-Algorythm is set to none" | LogMe
		$VDtests.LoadBalancingAlgorithm = "SUCCESS", "LB is set to NONE, least busy server is used"}

		}

#Disk fix assigned to a server
else
{
$VDtests.LoadBalancingAlgorithm = "ERROR", "vDisk is fix assigned to $LBnoServer, no LoadBalancing!"}
}
  
  
  
 #Check for WriteCacheType
# -----------------------
# Feel free to change it to the the from you desired State (e.g.Exchange a SUCCESS with a WARNING)
# In this default configuration, only "Cache to Ram with overflow" and "Cache to Device Hard disk" is desired and appears green on the output.
  
 $WriteCacheType = Mcli-Get DiskInfo -p diskLocatorName=$disklocator_short, siteName=$siteName, storeName=$storename
$WriteCacheType_short = $WriteCacheType -replace "WriteCacheType: ",""
  
 #$WriteCacheType 9=RamOfToHD 0=PrivateMode 4=DeviceHD 8=DeviceHDPersistent 3=DeviceRAM 1=PVSServer 7=ServerPersistent 
  
 if($WriteCacheType_short -eq 9 ){
"WC is set to Cache to Device Ram with overflow to HD" | LogMe
$VDtests.WriteCacheType = "SUCCESS", "WC Cache to Ram with overflow to HD"}
  
 elseif($WriteCacheType_short -eq 0 ){
"WC is not set because vDisk is in PrivateMode (R/W)" | LogMe
$VDtests.WriteCacheType = "Error", "vDisk is in PrivateMode (R/W) "}
  
 elseif($WriteCacheType_short -eq 4 ){
"WC is set to Cache to Device Hard Disk" | LogMe
$VDtests.WriteCacheType = "SUCCESS", "WC is set to Cache to Device Hard Disk"}
  
 elseif($WriteCacheType_short -eq 8 ){
"WC is set to Cache to Device Hard Disk Persistent" | LogMe
$VDtests.WriteCacheType = "Error", "WC is set to Cache to Device Hard Disk Persistent"}
  
 elseif($WriteCacheType_short -eq 3 ){
"WC is set to Cache to Device Ram" | LogMe
$VDtests.WriteCacheType = "WARNING", "WC is set to Cache to Device Ram"}
  
 elseif($WriteCacheType_short -eq 1 ){
"WC is set to Cache to PVS Server HD" | LogMe
$VDtests.WriteCacheType = "Error", "WC is set to Cache to PVS Server HD"}
  
 elseif($WriteCacheType_short -eq 7 ){
"WC is set to Cache to PVS Server HD Persistent" | LogMe
$VDtests.WriteCacheType = "Error", "WC is set to Cache to PVS Server HD Persistent"}
}
}
  
  

# ======= PVS Server Check ==================================================================
"Check PVS Servers" | LogMe -display -progress
" " | LogMe -display -progress
 
$PVSResults = @{}
$allPVSServer = mcli-get server | Select-String serverName
foreach($PVServerName in $allPVSServer)
{
$PVStests = @{} 
  
 $PVServerName1 = $PVServerName | Select-String serverName
$PVServerName_short = $PVServerName1 -replace "serverName: ","" 
 $PVServerName_short
  
 # Ping server 
 $result = Ping $PVServerName_short 100
if ($result -ne "SUCCESS") { $PVStests.Ping = "ERROR", $result }
else { $PVStests.Ping = "SUCCESS", $result 
 } 
  
 #Check PVS Service Status
$serverstatus = mcli-get ServerStatus -p serverName=$PVServerName_short -f status
$actviestatus = $serverstatus[4].TrimStart("status: ") -as [int]
if ($actviestatus -eq 1) { $PVStests.Active = "SUCCESS", "active" }
else { $PVStests.Active = "Error","inactive" }

# Check services
		if ((Get-Service -Name "soapserver" -ComputerName $PVServerName_short).Status -Match "Running") {
			"SoapService running..." | LogMe
			$PVStests.SoapService = "SUCCESS", "Success"
		} else {
			"SoapService service stopped"  | LogMe -display -error
			$PVStests.SoapService = "ERROR", "Error"
		}
			
		if ((Get-Service -Name "StreamService" -ComputerName $PVServerName_short).Status -Match "Running") {
			"StreamService service running..." | LogMe
			$PVStests.StreamService = "SUCCESS","Success"
		} else {
			"StreamService service stopped"  | LogMe -display -error
			$PVStests.StreamService = "ERROR","Error"
		}
			
		if ((Get-Service -Name "BNTFTP" -ComputerName $PVServerName_short).Status -Match "Running") {
			"TFTP service running..." | LogMe
			$PVStests.TFTPService = "SUCCESS","Success"
		} else {
			"TFTP  service stopped"  | LogMe -display -error
			$PVStests.TFTPService = "ERROR","Error"
		
 }
  
 #Check PVS deviceCount
$serverdevicecount = mcli-get ServerStatus -p serverName=$PVServerName_short -f deviceCount
$numberofdevices = $serverdevicecount[4].TrimStart("deviceCount: ") -as [int]
if ($numberofdevices -gt 1) { $PVStests.deviceCount = "SUCCESS", " $numberofdevices active" }
else { $PVStests.deviceCount = "WARNING","No devices on this server" }
  
  
  
 $PVSResults.$PVServerName_short = $PVStests
  
}
# ======= PVS Farm Check ====================================================================
"Read some PVS Farm Parameters" | LogMe -display -progress
" " | LogMe -display -progress
$PVSFarmResults = @{}
$PVSfarms = mcli-get Farm #| Select-String FarmName

$farmname = mcli-get Farm | Select-String FarmName
$farmname_short = $farmname -replace "farmName: ",""

$Nr=0
foreach($PVSFarm in $PVSfarms)
{
$PVSFarmtests = @{}
# remove not needed record parts
if ($PVSFarm -like '*description*'){continue;}
if ($PVSFarm -like '*record*'){continue;}
if ($PVSFarm -like '*failover*'){continue;}
if ($PVSFarm -like '*executing*'){continue;}
if ($PVSFarm -like '*defaultSiteName*'){continue;}
if ($PVSFarm -like '*autoAddEnabled*'){continue;}
if ($PVSFarm -like '*role*'){continue;}
if ($PVSFarm -like '*audit*'){continue;}
if ($PVSFarm -like '*defaultSiteId*'){continue;}
if ($PVSFarm -like '*maxVersions*'){continue;}
if ($PVSFarm -like '*databaseInstanceName*'){continue;}
if ($PVSFarm -like '*farmId*'){continue;}
if ($PVSFarm -like '*merge*'){continue;}
if ($PVSFarm -like '*adGroups*'){continue;}
 if ($PVSFarm -ne '') {
$Nr += 1
$arr = $PVSFarm -split ': '
$farmsetting = $arr[0]
$PVSFarmtests.Setting = "NEUTRAL", "$farmsetting"
$arr = $PVSFarm -split ': '
$farmsettingvalue = $arr[1]
$PVSFarmtests.Value = "NEUTRAL", "$farmsettingvalue"
$farmnr=$Nr
$PVSFarmResults.$farmnr = $PVSFarmtests
}
}
 
 
 
# ======= Write all results to an html file =================================================
Write-Host ("Saving results to html report: " + $resultsHTM)
writeHtmlHeader "PVS Farm Report $farmname_short" $resultsHTM
writeTableHeader $resultsHTM $TargetFirstheaderName $TargetheaderNames $TargetheaderWidths $TargetTablewidth
$allResults | sort-object -property collectionName | % { writeData $allResults $resultsHTM $TargetheaderNames}
writeTableFooter $resultsHTM
writeTableHeader $resultsHTM $vDiksFirstheaderName $vDiskheaderNames $vDiskheaderWidths $vDisktablewidth
$vdiskResults | sort-object -property ReplState | % { writeData $vdiskResults $resultsHTM $vDiskheaderNames }
writeTableFooter $resultsHTM
writeTableHeader $resultsHTM $PVSFirstheaderName $PVSheaderNames $PVSheaderWidths $PVStablewidth
$PVSResults | sort-object -property PVServerName_short | % { writeData $PVSResults $resultsHTM $PVSheaderNames}
writeTableFooter $resultsHTM
 
writeTableHeader $resultsHTM $PVSFirstFarmheaderName $PVSFarmHeaderNames $PVSFarmWidths $PVSFarmTablewidth
$PVSFarmResults | % { writeData $PVSFarmResults $resultsHTM $PVSFarmHeaderNames}
writeTableFooter $resultsHTM
writeHtmlFooter $resultsHTM
#send email
$emailSubject = ("$emailSubjectStart - $farmname_short - " + (Get-Date -format R))
$mailMessageParameters = @{
From = $emailFrom
To = $emailTo
Subject = $emailSubject
SmtpServer = $smtpServer
Body = (gc $resultsHTM) | Out-String
Attachment = $resultsHTM
}
# Send mail if you wish
Send-MailMessage @mailMessageParameters -BodyAsHtml -Priority $mailprio

or Download the file here.

Question or suggestion for improvement? Just contact me.

Update 12.11.14: Now in version 1.2 it’s possible to define the device collection which should be checked. An other additional feature is that the script shows the version of the vDisk.
Update 15.08.15: Now in version 1.4 it’s possible to have more than just one vDisk store.
Update 16.09.15: Now in version 1.5 the vDisk file name, it’s date and the count of the used disk will be showed.
Update 14.10.15: Version 1.61 New feature, PVS Cache (on disk or Ram) will be reported. / 20.10.2015 1.62: fixed some bugs (see comments)

Update 29.12.15: If you use PVS 7.7 or higher check out the new blog post with the 7.7 Script: http://blog.appcloud.ch/happy-new-script-pvs-7-7-healthcheck/

Because of the Release of PVS with the new PoSh API the further development of this Script is discontinued. Sorry.

146 Responses to Citrix PVS HealthCheck

  • This is an excellent rehash of the XenApp Health Check script, excellent for my PVS environment…only one problem, i’m getting blank results for the FarmCheck section all the way at the bottom below the PVS Server section…any ideas?

  • i’m not able to copy the script to some editors, the script would be placed in one line 🙁
    please send it to my mail address or how can i download the script ?

  • Hello,

    Firstly like to say that this script is awesome, really loving your work!

    Question: Can I limit the script to a specific Device Collection, for example I have multiple collections and would like to limit the script checking to a specific collection name.

    Cheers.

    • In the current version this is not possible, but all is possible to script 🙂 When I have time a will create a new version and maybe add this feature.

  • Hi,
    great script! Like Ken I’m getting blank results for the FarmCheck. Do you have a solution for this?

    cheers
    Dennis

    • Found out this behaviour results on old PVS Versions. Now I have this script in Version 1.3 availiable and it works fine also with older PVS versions.

  • Hi Dennis,

    Did you registered successfull the SnapIn ?:

    C:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727\InstallUtil.exe ‘C:\Program Files\Citrix\Provisioning Services Console\McliPSSnapIn.dll’

    Cheers, Sacha

  • Is it possible to add support for multiple vdisk stores? We have about 8 stores, but the script only supports 1.

    Thanks,
    Ard

    • Hi Ard,

      In the current version multiple stores are not supported, but feel free to adopt the script for your desire.

      Cheers,
      Sacha

  • U Rock Sacha!!!

  • Sacha,

    Thanks for your awesome script! One quikc question. My report shows that there are few servers which come up a sping time out. I am new to this PVS environment.
    I dont find those servers anywhere except in the health report. What could be the reason?
    Is it something similar to what we see when citrix server not being removed from farm cleanly…Please guide.
    Email me at svineesh87@gmail.com

  • Hello, all!

    Ive been pretty busy at work, so I’m working on this as I can. I seem to be getting this error consistently, but I am unable to find where it is referring to in the script:

    Missing closing ‘)’ in expression.
    At line:64 char:1
    + <<<<
    + CategoryInfo : ParserError: (CloseParenToken:TokenId) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MissingEndParenthesisInExpression

    Any ideas??

  • Actually- I ran this with ISE and received all of these errors- any help anyone could provide would be greatly appreciated:))

    I’d love to get this running in our environment 🙂

    At line:128 char:11
    + $head = @”
    + ~
    Missing closing ‘)’ in expression.
    At line:129 char:1
    +
    + ~
    The ‘
    + ~
    Missing expression after unary operator ‘–‘.
    At line:157 char:4
    + –>
    + ~
    Missing file specification after redirection operator.
    At line:185 char:19
    + $tableHeader += “$head …
    + ~
    The ‘<' operator is reserved for future use.
    At line:185 char:23
    + $tableHeader += "
    $head …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Unexpected token ‘width='” + $headerWidth + “%” in expression or statement.
    At line:234 char:170
    + … 2’> $storename
    + ~~~~
    The ‘<' operator is reserved for future use.
    At line:251 char:2
    + "Begin with Citrix Provisioning Services HealthCheck" | LogMe -display -progress
    + ~~~~~
    Unexpected token 'Begin' in expression or statement.
    At line:462 char:49
    + writeHtmlHeader "PVS Farm Report $farmname_short" $resultsHTM
    + ~~~~~~~~~~~~~
    The string is missing the terminator: ".
    At line:148 char:6
    + body {
    + ~
    Missing closing '}' in statement block.
    + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MissingEndParenthesisInExpression

  • Hi,

    the script is very nice. i’m getting blank results for the FarmCheck section all the way at the bottom below the PVS Server section…any ideas?

    In the script there is a command:

    $PVSFarm1 = $PVSFarm | Select-String blub

    What do you mean here? What ist the string “blub” ?

    Thank You.

  • Hi Sacha:

    here is my solution for the farmcheck. It works for me with PVS 7.1:

    # ======= PVS Farm Check ====================================================================
    “Read some PVS Farm Parameters” | LogMe -display -progress
    ” ” | LogMe -display -progress
    $PVSFarmResults = @{}

    $PVSfarms = mcli-get Farm #| Select-String FarmName
    #$farmname_short = mcli-get Farm | Select-String FarmName | % { $_ -replace “farmName: “,””}

    #| LogMe -display -progress

    $Nr=0

    foreach($PVSFarm in $PVSfarms)
    {
    #$farmname_short = mcli-get Farm | Select-String FarmName | % { $_ -replace “farmName: “,””}
    $PVSFarmtests = @{}

    # remove not needed record parts
    if ($PVSFarm -like ‘*description*’){continue;}
    if ($PVSFarm -like ‘*record*’){continue;}
    if ($PVSFarm -like ‘*failover*’){continue;}
    if ($PVSFarm -like ‘*executing*’){continue;}
    if ($PVSFarm -like ‘*defaultSiteName*’){continue;}
    if ($PVSFarm -like ‘*autoAddEnabled*’){continue;}
    if ($PVSFarm -like ‘*role*’){continue;}
    if ($PVSFarm -like ‘*audit*’){continue;}
    if ($PVSFarm -like ‘*defaultSiteId*’){continue;}
    if ($PVSFarm -like ‘*maxVersions*’){continue;}
    if ($PVSFarm -like ‘*databaseInstanceName*’){continue;}
    if ($PVSFarm -like ‘*farmId*’){continue;}
    if ($PVSFarm -like ‘*merge*’){continue;}
    if ($PVSFarm -like ‘*adGroups*’){continue;}

    if ($PVSFarm -ne ”) {

    #$PVSFarm1 = $PVSFarm # | Select-String blub
    #$farmname_short = $PVSFarm -replace “farmName: “,””

    #$PVSFarmtests.FarmChecks = “NEUTRAL”, $farmname_short
    $Nr += 1
    $arr = $PVSFarm -split ‘: ‘
    $farmsetting = $arr[0]
    $PVSFarmtests.Setting = “NEUTRAL”, “$farmsetting”

    $arr = $PVSFarm -split ‘: ‘
    $farmsettingvalue = $arr[1]

    $PVSFarmtests.Value = “NEUTRAL”, “$farmsettingvalue”
    $farmname_short=$Nr

    $PVSFarmResults.$farmname_short = $PVSFarmtests
    }
    }

  • What is this? “nbsp”

    & : The term ‘nbsp’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify
    that the path is correct and try again.
    At C:\Temp\Citrix-PVS-Farm-Health-toHTML.ps1:365 char:2

  • Just wanted to say thanks for the script, saved me hours of dev work.

    I managed to get your script to report on 2 PVS Sites in the same Farm!
    now we just have 1 Citrix PVS HealthCheck for our Farm!

    Thanks!

  • Awesome script! I am trying to figure out how to configure multiple stores (i have 6).. can’t figure it out.
    Can someone point me in the right direction ? 🙂 Any help would be appreciated !

    • Hi Roofuzz, Thanks for your appreciation. I will take this input in my work on the next version, you are not the only one who has this need to check more than one Store. My idea is to loop trough all stores and check them.

  • Sacha,

    Love this script !! The only thing that doenst work in our environment is the Vdisks section. We use PVS 7.6.

    Regards,

    Peter

    • Peter thanks you for the appreciation. I’m using this script in my PS 7.6 environments, can you tell me what error behavior you have or what’s the error message you get?

  • If you want the query to be made for multiple stores modify the code like this:
    Remove the initial variable from the beginning of the script ($storename = *)
    $storenames = mcli-get store | Select-string storename
    $vdiskResults = @{}
    foreach ($storenameA in $storenames)

    {

    $storename = $storenameA -replace “storename: “,””
    ## rest of the checks
    ## .ReplState
    ## .deviceCount
    ## .LoadBalancingAlgorithm
    ## .WriteCacheType

    }

    • This might be more accurate:

      $storenames = mcli-get store | Select-string storename
      $vdiskResults = @{}
      foreach ($storenameA in $storenames)
      {
      $storename = $storenameA -replace “storename: “,””
      $storeid = mcli-get store -p storeName=$storename | Select-String storeId
      $storeid_short = $storeid -replace “storeId: “,””
      $alldisks = Mcli-Get disklocator -p siteName=$siteName, storeId=$storeid_short | Select-String diskLocatorName
      foreach($disk in $alldisks)
      {

      $disk1 = $disk | Select-String diskLocatorName
      $disklocator_short = $disk1 -replace “diskLocatorName: “,””

      foreach($diksloc in $disklocator_short)
      {

      $VDtests = @{}

      ## rest of the checks
      ## .ReplState
      ## .deviceCount
      ## .LoadBalancingAlgorithm
      ## .WriteCacheType

      } ## Close first foreach statement foreach ($storenameA in $storenames)

      • Thanks – I will take this in my next version

        • Thank you! You’ve done the heavy lifting 🙂

          • i want to rebuild the script to use multiple stores. @Kafedzhiev, could you please explain where exactly to put your code in the original Script to make it work ?

  • The replication status seems like is only looking at the local server that you are running the script and not the other PVS servers in the same store. My issue is that the report is saying that all vdisks are replicated but when I see the vDisk Store on the each box there are few vidsks that are missing.

  • Hi Sacha,

    Little Input about query different Stores with foreach loop function.

    Original Script “Part PVS vDisk Check”:
    —————————————-
    # read store id which belongs to defined storeName
    $storeid = mcli-get store -p storeName=$storename | Select-String storeId
    $storeid_short = $storeid -replace “storeId: “,””
    $vdiskResults = @{}

    Customize Script = replace the code above with the following:
    —————————————-
    $storenames = mcli-get store | Select-string storename
    $vdiskResults = @{}
    # read store id which belongs to defined storeName
    foreach ($storenameA in $storenames)
    {
    $storename = $storenameA -replace “storename: “,””
    $storeid = mcli-get store -p storeName=$storename | Select-String storeId
    $storeid_short = $storeid -replace “storeId: “,””

    And add the followed Line at the End of “Part PVS vDisk Check”:
    —————————————-
    } ## Close first foreach statement foreach ($storenameA in $storenames)

    Remove the $Storename Variable
    —————————————-
    $storename=”vDisk_Store” #store on which vDisk are checked for proper replication

    Have Fun

    • Thanks Sascha, I know I need to make a new version of the PVS script with some extensions and new features. I will implement a similar function – or take your function – as soon I have time to create a new version. I’m pretty sure that I need to re-engineer as soon PVS 7.7 with the new PoSh implementation is out.

  • Hello,

    Thanks a lot for this very very nice script

    An impressive work.

    I’ve a question related to the replication state.
    Is it possible to display the status for each vdisk and each server (in order to see where the replication is not working) ?

    Thanks a lot

    • Basically technical all is possible – but it’s always a question about effort and benefit. It’s easy to show which vDisk in a store is not synchronous. But it’s not so easy to show if it’s not synchronous, especially if a file exists but it’s not 100% identical. For this case in my point of view a human interaction will work better to determine what exactly is the problem. So I’m pretty sure I will not implement this feature in the next time.

  • Hi,

    I’m trying to use the script, but I keep hitting in issue revolving around the $logfile variable

    Out-File : Cannot bind argument to parameter ‘FilePath’ because it is null

    i’ve tried to specify the path and file myself rather than let it auto create, but still no luck.
    If you can drop me an email I’d greatful

    • I had to comment that line out and uncomment the line above it to stop reporting that error:

      Use this line

      $logEntry = ((Get-Date -uformat “%D %T”) + ” – ” + $logEntry)

      • If you like you can add your change in my GitHub Repo

        • It may need more testing since it looked to be reference elsewhere in the script and was showing errors during the manual execution. I didnt really look to the log file anyway since the email was sent with all the main info I needed. Thanks for this script by the way!

  • Awesome script, keep up the good work.

  • Excellent Script <3 Thank you Sacha

  • Sacha,
    I’m getting the following error with v1.5, using the same parameters as v1.4 in v1.5. On the same server, v1.4 works.:
    At C:\Users\adm_chauvrc\Desktop\Citrix-PVS-Farm-Health-toHTML v1.5.ps1:345 char:34
    + $storename = $storenameA -replace “storename: “,””
    + ~
    You must provide a value expression following the ‘-replace’ operator.
    At C:\Users\adm_chauvrc\Desktop\Citrix-PVS-Farm-Health-toHTML v1.5.ps1:345 char:35
    + $storename = $storenameA -replace “storename: “,””
    + ~~~~~~~~~~~~~
    Unexpected token ‘“storename:’ in expression or statement.
    At C:\Users\adm_chauvrc\Desktop\Citrix-PVS-Farm-Health-toHTML v1.5.ps1:347 char:35
    + $storeid_short = $storeid -replace “storeId: “,””
    + ~
    You must provide a value expression following the ‘-replace’ operator.
    At C:\Users\adm_chauvrc\Desktop\Citrix-PVS-Farm-Health-toHTML v1.5.ps1:347 char:36
    + $storeid_short = $storeid -replace “storeId: “,””
    + ~~~~~~~~~~~
    Unexpected token ‘“storeId:’ in expression or statement.
    At C:\Users\adm_chauvrc\Desktop\Citrix-PVS-Farm-Health-toHTML v1.5.ps1:353 char:37
    + $disklocator_short = $disk1 -replace “diskLocatorName: “,””
    + ~
    You must provide a value expression following the ‘-replace’ operator.
    At C:\Users\adm_chauvrc\Desktop\Citrix-PVS-Farm-Health-toHTML v1.5.ps1:353 char:38
    + $disklocator_short = $disk1 -replace “diskLocatorName: “,””
    + ~~~~~~~~~~~~~~~~~~~
    Unexpected token ‘“diskLocatorName:’ in expression or statement.
    + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ExpectedValueExpression

    • Did you downloaded the file or copied the code from website?

    • I had the same issue. Check form another mashine if the lines show correct with ” instead of “ .
      On the mashine I downloaded the script i made a simple copy and paste into the target script. But it is actually a fileencoding problem and changing the encoding to UTF8 instead of ANSI with an texteditor should fix it aswell.

      • I can run the script as ANSI, UTF or Unicode – I don’t really know whats the problem.

        • I don’t know exactly why this happens, but I belive it has to do with different regional settings. It was correct when i downloaded to my german Win7 and changed when I copied the script to my english 2012r2 provisioning Server.

      • Benjamin,
        That worked, thanks 🙂

        So I had to open Notepad++, set the encoding to UTF8, copy the code from the link above labeled “Click here to see the code…”, then paste it into Notepad++. I then verified that line 345 had “” instead of “. Once I verified that, I copied the script from Notepad++, pasted it into Powershell, and everything worked.

  • Thx for this awesome script!

    We have 8 pvs stores with different vdisk files, but the output of the script shows only 5 stores. How can I tell the script to output everything?

  • Hi,
    Very nice piece of work and I got it up and running as a scheduled task with no difficulty.
    One thing however, in our XD site, we have 2 PVS servers, 4 images and 4 device collections serving approx 800 desktops and the script takes about 6.5 hours to run to completion, does this sound normal to you?

    • Thanks! The loop through 800 would take some time, and I’m sure thats thats important you set the variable that only desktops with errors are reported. But 6.5 hours is very long … Maybe it makes sense that you make 4 “clones” of the script and only check 1 device collection in each script. So you can run the scripts parallel 4 times.

      • You could change the script itself so that they are processed in parallel using either the Start-Job or Invoke-Command cmdlets. You’ll get different mileage from these methods, but it will process large environments fast.

        Cheers,
        Jeremy

  • Thanks for the replies, I’d like to explore processing in parallel using either the Start-Job or Invoke-Command cmdlets, but I’m not that good a scripter, are there any references I can review for this?

    • I’m not sure if it’s so easy because we are filling an array with values… to be honest I’m not experienced with filling an array with parallel processing.

    • Larz, I have a farm with 100 VDI’s as PVS targets, the script runs for 14 minutes. So I guess when you need 6,5 hours for 800 targets something is wrong. Do you have a lot unreachable targets?

  • Have a question about the HTML header……. mine displays the following:

    PVS Farm Report 6 – Thu, 15 Oct 2015 07:00:06 GMT

    How can I get rid of the 6? and why is it there?

  • Hi Sacha-
    Great script, however the replication status portion works great if you have a Store that is shared on a NAS among the PVS servers in the farm. But it when you have a Store and the vdisks are store locally on each boxes (ex. F:\ drive) the replication status is not working. In my case seems like is reading the path but it only looks on the PVS server that you are running the script. If you go to the vDisk itself in the PVS console and you look on Replication Status it only shows one server. Make sense? If you need more information please let me know.

    • Hi Omar, thanks! I use it with my store on each box (e:\) and useually it works fine. But sometimes it doesent work correct, neither in PVS console. Restart of PVS help.

    • Omar,
      I have PVS vDisk stored locally on both of my PVS Servers, in my case the D drive of both and the script works flawless.

      • You are running the latest version 1.6? any modification?

        • 1.61 what I’ve released yesterday – but not changed on this script part since months.

          • Another finding on my side….In the LB setting, if you set the vdisk to “Use this server to provide the vDisk” and selecting an specific server, in the report it detects that as “Use the Load Balancing Algorithm” Subnet Affinity “None”, which is not accurate. in that case should failed instead of reporting that LB was set to NONE.

          • Thanks – will check that on next version. Can be very bad when you have a dedicated server instead of load balancing …

          • It should be solved with 1.62 thank you Omar for this good Input!

  • script works great except I am unable to get the writecache to display in the report, here is the error I am getting form the script.

    parsing ” Executing: Get DiskInfo Get succeeded. 1 record(s) returned. Record #1 diskLocatorId: 0596ea98-341a-4d0a-8a55-fcc060eccef1 diskLocatorName: MTD03_10-13-15 siteId: 0654c2df-fd48-4049-b47b-a706152d8885 siteName:
    FortWorth storeId: 3b17e864-bb6e-4d18-b8c7-947b8b8938d6 storeName: SOFSStore description: menuText: serverId: serverName: enabled: 1 role: 100 mapped: 0 active: 1 rebalanceEnabled: 0 rebalanceTriggerPercent: 25
    subnetAffinity: 0 diskUpdateDeviceId: diskUpdateDeviceName: class: imageType: diskSize: 85899345920 vhdBlockSize: 2048 writeCacheSize: 6114 autoUpdateEnabled: 0 activationDateEnabled: 0 adPasswordEnabled: 1 haEnabled:
    1 printerManagementEnabled: 0 writeCacheType: 9 licenseMode: 2 activeDate: 2015/10/13 longDescription: serialNumber: de793a8d-5a71-4dd5-8951-8aa9acb90eae date: 10/13/2015 18:07:42 author: title: company: internalName:
    S:\Store\MTD3_10-13-15.vhd originalFile: S:\Store\MTD3_10-13-15.vhd hardwareTarget: majorRelease: 1 minorRelease: 0 build: 1 deviceCount: 60 locked: …” – Unrecognized escape sequence \M.
    At C:\Temp\PVS HealthChecker\PVS HealthChecker_FortWorth_v1.61_NoEmail.ps1:310 char:8
    + If ($wconhd -match “$WriteCacheType=4”) {Write-Host Cache on HDD
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OperationStopped: (:) [], ArgumentException
    + FullyQualifiedErrorId : System.ArgumentException

    • Thanks for the Feedback Shane! What kind of WriteCache do you use?

      • cache to ram overflow to disk

        Thank you for the quick response.

        • cache to ram overflow to disk should work, I use also this kind of WriteCache. I cannot find a indication what the problem is in your case 🙁 Unrecognized escape sequence \M => maybe some wrong letter in the script? Do you want to send me your script-file that I can try to run it in my environment?

  • Sacha, excellent script. Thank you for your contribution.
    I have one question:
    Is it possible to adapt the script to check the following ?

    Say i have to 2 servers with shared storage.
    Optimally device collection A should have all devices connected to server A (normally done with subnet loadbalancing, but all devices and the servers are 1 subnet).
    The same for collection B, the target devices shoudl be booted from server B.

    If collection A devices are not connected to server A, then the servename column turns orange ? The same for B.

    How would i realize that ?

    • Guess you have to datacenter location and want to ensure every target connect to the server in it’s datacenter, but you have one subnet streched over both datacenter? I wonder how do you solve the assignment from the collection to the server?

      Are the Targets A and Server A distinguishable by hostname from the B maschines?

  • Hi Sacha-
    Hope everything is well. Just to confirm you that the LoadBalancingAlgorithm is fixed on the new version. However, I found that the ReplState still showing as OK when the vdisk is not replicated to the other server in my farm. We have 2 PVSs in 7.6 but we are using local storage instead of a SAN to store the vdisks. Dont know why is showing that replication is OK when the vdisk doesn’t exist in the other PVS. Any additional configuration that I need to do from my side to make it work? Please advise.

  • Hi, Sacha, can we add pvs services like stream service, soap service status etc to this script? I created a function to check the status of 4 pvs services, then reply ok, if running, and service not available if not running. I have some difficulties in writing the output to html file. Can you help?

  • I already tried it.. only problem is, my output has,
    name value
    pvs01 ok
    pvs02 ok

    now, I am able to write pvs01 and pvs02 to html file. my output looks like,
    ServerName S
    pvs01
    pvs02

    The OK results are not coming in the html file, and the headername i entered in the script was service name, but it shows only one character.

    • ok, i figured out my mistake and now i got the OK in my result. Now in my table, I only have one character in the table header. I have modified table 2 with only two columns, first servername and second servicestatus. Now in second column, only s is shown in the header in output. If i add third column, then all the three table headers are shown properly. But if I update only one, it shows only the first character.

      • See my email

        • Along with streamservice and soap service i also wanted to check TFTP and two stage boot service. So, I have updated the second Table column and I got my script 100% in the way i needed. Earlier I have added some functions in the script as per my requirement. I have changed them now and it works as expected. Thanks for your time. 🙂

  • Hi Sacha

    The script is great indeed and Thanks for that !

    need some help with below

    1. I see lot of messages under write cache column –>Cache not readable
    2. under Vdisk replication state – replication state is not ok
    any idea?

    • To read the Cache you need some open ports beteeen PVS and Target device (remote wmi query) – as soon this is open your cache is readable.

      Is the replication state ok inside the PVS console? Are the files synchronous. If yes reboot the pvs servers if possible, sometimes a wrong status is reported.

  • HI Chaitanya

    I saw that you have added columns for services which is awesome, I am new to scripting, is it possible for you to share the script which can do all this ?

    Thanks
    Shailender

    • I have the services inside too, will add the new script in the next days.

      • Very Nice Sacha , Thanks for the quick reply

        I will check the ports , do you know what port numbers are, I am new to PVS but I think they might start with 16**, could be wrong

        I would be great to have script with services .. when you get a chance can you publish it please will be a great help Thanks again !!#

        Cheers
        Shailender

  • Sacha,

    FYI:
    The new version 1.63 is having a little issue with line breaks on the different vdisk version and deviceCount, etc. Doesn’t look like it breaking to a new line.

    Cheers,
    Ryan

    P.S. Thank you so much for your time and experience, this is really a life saver 🙂

  • Hi Sacha

    Thanks for your reply on ports !

    did you get a chance to take a look at services script ?

  • hi Sacha, when i run the script in ise in normal or elevated mode i get the below error

    parsing ” Executing: Get DiskInfo Get succeeded. 1 record(s) returned. Record #1 diskLocatorId: 53dbd4c4-b340-4dcb-ac31-724956ee2684 diskLocatorName: XD_Base_W81 siteId:
    5e21e35e-96d3-44bd-97d5-6c1a1fa49a1d siteName: SF-Site storeId: d8262bb7-6728-4d6d-b9f2-ddbbdfb78512 storeName: W81Base description: menuText: serverId: serverName: enabled: 1 role: 100 mapped: 0
    active: 1 rebalanceEnabled: 0 rebalanceTriggerPercent: 25 subnetAffinity: 0 diskUpdateDeviceId: diskUpdateDeviceName: class: imageType: diskSize: 38654705664 vhdBlockSize: 0 writeCacheSize: 512
    autoUpdateEnabled: 0 activationDateEnabled: 0 adPasswordEnabled: 1 haEnabled: 1 printerManagementEnabled: 0 writeCacheType: 9 licenseMode: 2 activeDate: 2015/10/28 longDescription: serialNumber:
    342b8c5b-97b6-44bc-a180-d063914a64ab date: 10/28/2015 12:58:51 author: title: company: internalName: F:\Win81Base\XD_Base_W81.vhd originalFile: F:\Win81Base\XD_Base_W81.vhd hardwareTarget:
    majorRelease: 1 minorRelease: 0 build: 1 deviceCount: 21 locked: 1 =4″ – Unrecognized escape sequence \X.
    At pvs status.ps1:325 char:8
    + If ($wconhd -match “$WriteCacheType=4”) {Write-Host Cache on HDD
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OperationStopped: (:) [], ArgumentException
    + FullyQualifiedErrorId : System.ArgumentException

    • Hi Jeremy, sorry for that. But it’s extreme hard to troubleshoot without access to the system. I can’t reproduce this error and have currently no idea what the reason for is. Guess the HTML has no value in the requested info field, all other is ok? If you wish you can send me the log-file and the html-output to my email address.

  • Hi Sacha,
    Great script.I have added few lines to check the CPU, Mem, c drive and d drive disk space of PVS servers. I could see the results on the log file but is not written to html file. could you please help. i have sent you the log file and script to your email address.

    • I didn’t receive your email. Please send it again. To be honest I have not always time to help making personal updates of the scirpts, sorry. But send it, as soon as I have time I’ll have a look.

    • I implemented this now in Version 1.2 in the version which is working with PVS 7.7 and higher.

  • Hi Sacha,

    We are using PVS 7.6 on Windows 2008 R2.
    The script runs flawlessly when manually run but I get blank output when ran trough scheduled task…
    I’ve registered the dll:

    C:\Program Files\Citrix\Provisioning Services Console>C:\Windows\Microsoft.NET\F
    ramework64\v2.0.50727\InstallUtil.exe McliPSSnapIn.dll
    Microsoft (R) .NET Framework Installation utility Version 2.0.50727.5483
    Copyright (c) Microsoft Corporation. All rights reserved.

    Running a transacted installation.

    Beginning the Install phase of the installation.
    See the contents of the log file for the C:\Program Files\Citrix\Provisioning Se
    rvices Console\McliPSSnapIn.dll assembly’s progress.
    The file is located at C:\Program Files\Citrix\Provisioning Services Console\Mcl
    iPSSnapIn.InstallLog.
    Installing assembly ‘C:\Program Files\Citrix\Provisioning Services Console\McliP
    SSnapIn.dll’.
    Affected parameters are:
    logtoconsole =
    assemblypath = C:\Program Files\Citrix\Provisioning Services Console\McliPSSn
    apIn.dll
    logfile = C:\Program Files\Citrix\Provisioning Services Console\McliPSSnapIn.
    InstallLog

    The Install phase completed successfully, and the Commit phase is beginning.
    See the contents of the log file for the C:\Program Files\Citrix\Provisioning Se
    rvices Console\McliPSSnapIn.dll assembly’s progress.
    The file is located at C:\Program Files\Citrix\Provisioning Services Console\Mcl
    iPSSnapIn.InstallLog.
    Committing assembly ‘C:\Program Files\Citrix\Provisioning Services Console\McliP
    SSnapIn.dll’.
    Affected parameters are:
    logtoconsole =
    assemblypath = C:\Program Files\Citrix\Provisioning Services Console\McliPSSn
    apIn.dll
    logfile = C:\Program Files\Citrix\Provisioning Services Console\McliPSSnapIn.
    InstallLog

    The Commit phase completed successfully.

    The transacted install has completed.

    • Under which user the scheduled task runs? Did this user has the correct permission to the farm?

      • SYSTEM
        Generally we have reports for VDIs (get-brokerdesktop) running with system and we have no issues.
        Unfortunately I am not sure if I have access to an account I can set up the scheduled task with and has appropriate access to the farm.

        • I’m pretty sure it’s not possible to run it without farm permissions so without a real user configured as a delegated admin for the farm.

          • Sacha, you are correct. Dimitar, you’ll need to create a svc (service account in AD) and give that service account farm rights, then run the scheduled task with that service account. The reason to use the service account is so you don’t have to keep updating the scheduled task with the new password.

  • Great script, thanks for putting it together.
    I was able to run it however missing this information. Any idea?

    vDisk Store vDiskFileName deviceCount CreateDate ReplState LoadBalancingAlgorithm WriteCacheType

    This is the error code: Any help would greatly appreciated!!!

    Mcli-Get : The specified Site does not exist.
    At D:\scripts\Citrix-PVS-Farm-Health-toHTML.ps1:404 char:13
    + $alldisks = Mcli-Get disklocator -p siteName=$siteName, storeId=$storeid_short | …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : DeviceError: (:) [Mcli-Get], EAException
    + FullyQualifiedErrorId : InvalidSite,McliPSSnapIn.McliGet

    Mcli-Get : The specified Site does not exist.
    At D:\scripts\Citrix-PVS-Farm-Health-toHTML.ps1:404 char:13
    + $alldisks = Mcli-Get disklocator -p siteName=$siteName, storeId=$storeid_short | …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : DeviceError: (:) [Mcli-Get], EAException
    + FullyQualifiedErrorId : InvalidSite,McliPSSnapIn.McliGet

  • Hi
    This is a great script I was looking advise on how to get the PVS disk cache location added to this

    Many Thanks

    • imho PVS disk cache location is always the same, or do I understand your question not correct?

  • Hi,
    can we configure this script with SMPT authentication?

    Actually my SMPT server requires authentication.

    • Yes that’s possible, have a look to the code of the XenDesktop/XenApp 7.x Check – this is with authentication – you can do it equal.

      • Hi Sacha, Thanks for your response.

        Where can we mention credential (ID/Password) in the script for SMTP authentication ?

        Please help.

  • Sacha- Im now implementing this for the second time. I absolutely love this script! Something that has crossed my mind a few times: I send this to management, and while he is technical, there are some things that need to be explained. One thing that comes up frequently: A box wi2ll show a failed ping and unregistered if it is powered off. This can easily be determined if you log into Studio, however he does not do that. It would be nice, if the script also checked for a power state and if it is powered off, skips the registration and ping checks. It would just state “powered off” and would be green. This would be helpful on the XA/XD script as well as the PVS script (PVS script: skip ping and state “powered off”. Is that something you could add easily?

    • Micah this script is for PVS even if it’s not in combination with XenDesktop, so this feature is not implemented in this script. But if you also make use of the XenApp/XenDesktop 7.x script there is it possible to add this into the script. Do you have a GitHub account? If yes, please post this feature request there so I have it im my list. (https://github.com/sacha81) Honestly I was not able to continue coding on my scripts in the last weeks – but I will stat improve my scripts as soon I have more free time.

      • Sacha- Sounds good!

        One other thing- On the HTM file, dont have anything populated under WriteCache (other than a few that state they are unreadable). Were running RAM w/ overflow to Disk. The section for this method of WC should populate this field, right?

        else
        {Write-Host Cache on Ram

        #RAMCache
        #Get-RamCache from each target, code from Matthew Nics http://mattnics.com/?p=414
        $RAMCache = [math]::truncate((Get-WmiObject Win32_PerfFormattedData_PerfOS_Memory -ComputerName $_targetshort).PoolNonPagedBytes /1MB)
        $tests.WriteCache = “Neutral”, “$RamCache MB on Ram”

        I mean, I know this is working, because I replaced this:

        {Write-Host Cache on Ram

        With this:

        {Write-Host “Cache on Ram = $RamCache MB”

        And the script told me what the memory usage was in the powershell window. Its just not ending up in the HTM file for some reason.

        It looks like the data from this pull is written to $tests.WriteCache and then compiled into $allResults and then $allResults.$_targetshort = $tests

        When I look at $tests, this is all I see (not the WriteCache):

        PS C:\Utilities\PVSHealthCheckScript> $tests

        Name Value
        —- —–
        Retry {SUCCESS, 2 Retry = OK}
        Ping {SUCCESS, Success}
        vDisk_Version {SUCCESS, 7}
        vDisk_PVS {SUCCESS, ……………………avhdx}
        PVSServer {Neutral, ……………………..}
        CollectionName {NEUTRAL, ……………………………..}

        If I look at $AllResults Im not finding any info (however it cuts off even if exported to a CSV):

        Name Value
        —- —–
        Server1 {Retry, Ping, vDisk_Version, vDisk_PVS…}
        Server2 {Retry, Ping, vDisk_Version, vDisk_PVS…}
        Server3 {Retry, Ping, vDisk_Version, vDisk_PVS…}
        Server4 {Retry, Ping, vDisk_Version, vDisk_PVS…}
        Server5 {Retry, Ping, vDisk_Version, vDisk_PVS…}
        Server6 {Retry, Ping, vDisk_Version, vDisk_PVS…}
        Server7 {Retry, Ping, vDisk_Version, vDisk_PVS…}
        Server8 {Retry, Ping, vDisk_Version, vDisk_PVS…}
        Server9 {Retry, Ping, vDisk_Version, vDisk_PVS…}

        Do you know of any possible reasons I dont see any information on WriteCache? The script isnt erroring when it checks, I know it can get the info, its just not ending up in the HTM.

  • Sacha-

    Is there any way to see what is being populated in $tests ? I can look at it after the fact by kicking it out in a text file, however, there is nothing in it.

    Im just trying to figure out why Im not seeing any “$RamCache MB on Ram” data in the WriteCache Field of the HTM. The weird thing, is, I see “Cache not readable” on some of them, and the script is written in the same format for output on both sections:

    $tests.WriteCache = “Neutral”, “$RamCache MB on Ram”

    $tests.WriteCache = “Neutral”, “Cache not readable”

    It sounds like the data is not getting written to $tests for the “$RamCache MB on Ram” section, so I wanted to verify if that was true.

    Again, I know that this section works up until the “$tests.WriteCache = “Neutral”, “$RamCache MB on Ram”” section:

    $RAMCache = [math]::truncate((Get-WmiObject Win32_PerfFormattedData_PerfOS_Memory -ComputerName $_targetshort).PoolNonPagedBytes /1MB)

    I know this because I changed “{Write-Host Cache on Ram” to “{Write-Host “$RAMCache mb on Ram” and I get a read-out in the powershell window.

    Is anyone else experiencing/has experienced and has fixed this type of issue?

  • ut-File : Cannot bind argument to parameter ‘FilePath’ because it is null.
    At line:170 char:18
    + $head | Out-File $fileName
    + ~~~~~~~~~
    + CategoryInfo : InvalidData: (:) [Out-File], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.OutFileCommand

  • what could be the reason for this failing…

  • Hi

    If we have more the 1 site how can we ajust the script

    $siteName=”xxx” # site name on which the according Store is.

    Tx

Leave a Reply

Your email address will not be published. Required fields are marked *

Script Archive GitHub
sacha81 @ Github

Hide my repositories

My Tweets & Retweets
CUGC