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.sachathomet.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 on GitHub: Citrix-PVS-Farm-Health-toHTML.ps1
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.sachathomet.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.
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?
Have a look in your email-Inbox.
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 ?
Guess you have already seen my email in your inbox 🙂
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.
Great script Sacha!
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:WINDOWSMicrosoft.NETFramework64v2.0.50727InstallUtil.exe ‘C:Program FilesCitrixProvisioning Services ConsoleMcliPSSnapIn.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.
Blub is just a placeholder. What is the Version of pvs you are using? This only works with 7.5 and higher as far as i know.
Hi,
i use still PVS 7.1 at the moment .
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
}
}
Cool – thanks!!
I’ve updated the Script to v 1.3 with your Input. Thank you!
Where can you DL the v1.3 script?
The current version is 1.3, I’ve just not updated the header correct. Sorry.
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:TempCitrix-PVS-Farm-Health-toHTML.ps1:365 char:2
This is in the log section, explained:
http://en.wikipedia.org/wiki/Non-breaking_space
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!
How did you add multiple farms? I have been trying several methods with no success as of yet. Thanks in advance!
I meant to ask sites, not farms. My apologies.
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.
Which version of PVS? Which kind of store? For me that works fine.
Version 7.6
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:Usersadm_chauvrcDesktopCitrix-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:Usersadm_chauvrcDesktopCitrix-PVS-Farm-Health-toHTML v1.5.ps1:345 char:35
+ $storename = $storenameA -replace “storename: “,â€â€
+ ~~~~~~~~~~~~~
Unexpected token ‘“storename:’ in expression or statement.
At C:Usersadm_chauvrcDesktopCitrix-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:Usersadm_chauvrcDesktopCitrix-PVS-Farm-Health-toHTML v1.5.ps1:347 char:36
+ $storeid_short = $storeid -replace “storeId: “,â€â€
+ ~~~~~~~~~~~
Unexpected token ‘“storeId:’ in expression or statement.
At C:Usersadm_chauvrcDesktopCitrix-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:Usersadm_chauvrcDesktopCitrix-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 downloaded the file
Strange – run as admin? For other it works I guess
I did run as admin, ran it in the same PowerShell ISE as I ran v1.4 to verify it wasn’t something else going on with my server. Again v1.4 works just fine, but not v1.5, weird.
I will check it next week
No worries, thanks. Let me know I you need anything from me to help troubleshoot it better. I can setup a WebEx meeting if you’re interested as well, if not that’s cool.
see comment of Benjamin, maybe an encoding issue? I cannot reproduce you error. Try to copy the code in your texteditor.
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?
What’s the name of your farm? On line 597 we write the HTML-Header:
writeHtmlHeader “PVS Farm Report $farmname_short” $resultsHTM
can it be that your farm is named “6” ?
no, my farm name is the following:
ALB-BRT-PVS
Hmm, currently no idea …
It’s not that big of a deal, just curious.
It’s solved with new version 1.62
Just tested and it’s fixed, you da man, thanks so much 🙂
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.
Did that already but keep saying that replication is OK on all the vdisks but I go i check on the other PVS server meaning the secondary manually the vdisks are not there.
What version of PVS are you running?
How many Pvs you have? I have just two.
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:StoreMTD3_10-13-15.vhd originalFile: S:StoreMTD3_10-13-15.vhd hardwareTarget: majorRelease: 1 minorRelease: 0 build: 1 deviceCount: 60 locked: …” – Unrecognized escape sequence M.
At C:TempPVS HealthCheckerPVS 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 intend to implement this in one of the next versions
I am trying to add a table with server name, servivce names, status columns. I have modified the table, able to add server names column, but i m not able to write the output from the function to this html file. 🙁
You can copy the lines from the other checks and modify it
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. 🙂
Great to hear 🙂 i will add this in my version too
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
I think it’s port 445 needed, script with service check is online.
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 🙂
Can you send me screenshot on my email, for me it’s working fine.
Hi Sacha
Thanks for your reply on ports !
did you get a chance to take a look at services script ?
How already said in my last comment, services are checked in the current version.
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:Win81BaseXD_Base_W81.vhd originalFile: F:Win81BaseXD_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 FilesCitrixProvisioning Services Console>C:WindowsMicrosoft.NETF
ramework64v2.0.50727InstallUtil.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 FilesCitrixProvisioning Se
rvices ConsoleMcliPSSnapIn.dll assembly’s progress.
The file is located at C:Program FilesCitrixProvisioning Services ConsoleMcl
iPSSnapIn.InstallLog.
Installing assembly ‘C:Program FilesCitrixProvisioning Services ConsoleMcliP
SSnapIn.dll’.
Affected parameters are:
logtoconsole =
assemblypath = C:Program FilesCitrixProvisioning Services ConsoleMcliPSSn
apIn.dll
logfile = C:Program FilesCitrixProvisioning Services ConsoleMcliPSSnapIn.
InstallLog
The Install phase completed successfully, and the Commit phase is beginning.
See the contents of the log file for the C:Program FilesCitrixProvisioning Se
rvices ConsoleMcliPSSnapIn.dll assembly’s progress.
The file is located at C:Program FilesCitrixProvisioning Services ConsoleMcl
iPSSnapIn.InstallLog.
Committing assembly ‘C:Program FilesCitrixProvisioning Services ConsoleMcliP
SSnapIn.dll’.
Affected parameters are:
logtoconsole =
assemblypath = C:Program FilesCitrixProvisioning Services ConsoleMcliPSSn
apIn.dll
logfile = C:Program FilesCitrixProvisioning Services ConsoleMcliPSSnapIn.
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:scriptsCitrix-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:scriptsCitrix-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
You have adjustet the Site in the Script to your correct site name?
I am going to have 140+ sites, can I get replstatus on entire farm?
Honestly I never tested that in such big environments, I cannot give you an answer. Just test it.
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:UtilitiesPVSHealthCheckScript> $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.
Can/Must be an error, I’m using the new version of the PVS health check (for 7.7 an higher – see http://blog.sachathomet.ch/happy-new-script-pvs-7-7-healthcheck ) and here it’s working fine. I do not make corrections on the old version of the script because I advise all to move to 7.7 or higher, PowerShell is much better integrated in those versions. Sorry that I can’t support all my scripts.
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 did you set as variable for filename?
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
currently not implemented, I added this to my issue list on GitHub: https://github.com/sacha81/citrix-pvs-healthcheck/issues => but I will only work on the Script for >= V7.7
excellent script, its working in our organization
Hi all,
I recently upgraded from PVS 7.1 to 7.6 cu4 and the PS script stopped working.
I attempted to re-register the McliPSSnapin, but get below error after registering the snapin
“C:\Monitoring\Citrix-PVS-Farm-Health-toHTML.ps1 : Error loading PVS McliPSSnapIn PowerShell snapin
At line:1 char:48
+ C:\Monitoring\Citrix-PVS-Farm-Health-toHTML.ps1 <<<<
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Citrix-PVS-Farm-Health-toHTML.ps1"
I am using .Net 4.7, PS version 2
Any advise appreciated.
To clarify – the above error occurs after the snapin is registed using below.
C:\WINDOWS\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe
“C:\Program Files\Citrix\Provisioning Services Console\McliPSSnapIn.dll”
I am getting blank out put when I run the script. It was working fine and suddenly after months it has started giving blank output in email. Can you suggest what should I be checking in the Citrix farm so that the script starts working again. It’s urgent and need some inputs from you to proceed. I have tried to restart the XML and IMA service but still no go.
Did you maybe update the PVS version and don’t re-registered the snap-in? what output you receive on the powershell when you run the script? XML and IMA has absolutely nothing to do with PVS. Do you have trouble with the PVS health check or with the XA/XD health check?
Hi Sacha
I have a request which i hope you can help me with
We are trying to add vdisk retries entries to a sql database and the build that into SCOM so that we can do alerting and trending etc
Can you by any chance help with the code to do that
just the code to write the data to a sql database would be awesome
Thanks so much
Hello, you can the the retries of a target with Get-PvsDeviceStatus -Name $targetName – sorry I don’t have free time to write the code for you.
Hi Sacha
Thank you for creating the script. We use it everyday to monitor our PVS environments. We have a few PVS servers in a site that are no longer in use or active. The script when runs reports the PVS checks as failed. Is there any way to show them as inactive or not in use?
Any help is much appreciated.
I assume you just need to remove the servers from the farm and they will no be checked again
at this point we can’t remove the servers yet. we need to maintain them for a couple more months ( internal policies). Is there any way to show them as In-active?