PowerShell
Der SOAP Serivce kann auf einem PVS Server nicht mehr gestartet werden
Nach den letzten monatlichen Microsoft Security Updates war einer meiner PVS Server nicht mehr in der Lade den SOAP Service zu starten. Ich kriegte einen Event 7000 mit der Meldung:
The Citrix PVS Soap Server service failed to start due to the following error: The service did not respond the the start or control request in a timely fashion.
Ich lebe in Bern
, und wir Berner sind dafür bekannt alles etwas ruhig wenn nicht sogar langsam anzugehen und uns nicht stressen zu lassen. Wahrscheinlich liegt dies an unserem Dialekt der einen gewissen ruhigen Klang ausstrahlt.
Jedenfalls war meine Idee aus der Meldung, wenn der Service sich gestresst fühlt, geben wir ihm doch einfach mehr Zeit:
Ich habe ein DWORD names ServicesPipeTimeout mit dem Wert 120000 in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control erstellt, dies bedeutet das nun die Services auf diesem Server 2 minuten Zeit haben um zu starten. Nach einem Reboot war die Einstellung aktiv und mein SOAP war wieder da.
Übrigens, der SOAP ist ein zickiges Ding und ich empfehle den Service so zu konfigurieren dass dieser nach einem Absturz automatisch wieder startet.
PowerManagement für dedizierte Citrix desktops? Klar mit Tags!
Habt ihr Tags bereits für XenApp & XenDesktop im Einsatz? Vielleicht gäbe es den einen oder anderen guten Anwendungsfall. Tags zu Resourcen, in meinem Fall virtuellen Desktops, können sehr mächtig sein speziell in Kombination mit Power Shell scripts. Es ist möglich Aktionen abhängig der Tags zu machen. Natürlich können Tags auch verwendet werden als Filter für Citrix Richtlinien (Policies) was ich auch als sehr hilfreich erachte.
Ich hatte das Problem das ich eine Bereitstellungsgruppe hatte mit dedizierten Windows 10 desktops und für dedizierte Desktops gibt es bekannter weise kein Power Management. Eigentlich ist ein Power Management dazu auch obsolet da herunter gefahrene Maschine beim anklicken im StoreFront wieder gestartet werden. Das Problem in unserer Umgebung ist jedoch das gewisse Desktops auch anders als über StoreFront angegangen werden und somit ausgeschaltete Maschinen ein Problem sind.
Wird eine Maschine somit herunter gefahren endet das in einem Incident Ticket.
Meine Lösung zu diesem Problem ist es, das ich diese speziellem Maschine mit einem tag „AlwaysOnline“ im Studio versehe. Weiter habe ich dann das nachfolgende kleine Script geschrieben welches auf dem Delivery Controller alle 15 Minuten per Scheduled Task gestartet wird:
param([string]$tags=$(throw "Tag parameter is required"), [string]$poweroperation=$(throw "Power operaton parameter is required")) #============================================================================================== # Created on: 09.2016 Version: 0.2 # Created by: Sacha Thomet # File name: PowerOperation-DependingMachineTags.ps1 # # Description: This is a Powershell to change the PowerState of VDI's or XenApp Servers in # a PowerManaged XenDesktop 7.x environment accodring to Tags. # # Prerequisite: None, a XenDesktop Controller with according privileges necessary # # Call by : Manual or Scheduled Task #============================================================================================== # Load only the snap-ins, which are used if ((Get-PSSnapin "Citrix.Broker.Admin.*" -EA silentlycontinue) -eq $null) { try { Add-PSSnapin Citrix.Broker.Admin.* -ErrorAction Stop } catch { write-error "Error Get-PSSnapin Citrix.Broker.Admin.* Powershell snapin"; Return } } # Change the below variables to suit your environment #============================================================================================== $maxmachines = "1000" # as default only 250 records, this increase it to 1000 #$tags = "AlwaysOnline" # if you comment out the param line you can have the tag here #$poweroperation = "TurnOn" # if you comment out the param line you can have the poweroperation here $machines = Get-BrokerMachine -MaxRecordCount $maxmachines | Where-Object {$_.tags -eq $tags } foreach($machine in $machines) { $machinename = $machine | %{ $_.MachineName } Write-Host "Action $poweroperation will be performed for $machinename " New-BrokerHostingPowerAction -Action $poweroperation -MachineName $machinename }
Ich weiss ich weiss … dies ist ein sehr ungewöhnlicher Anwendungsfall, aber das Script Konstrukt soll in erster Linie aufzeigen was mit Tags möglich ist, die Möglichkeiten sind fast unbegrenzt!
Mein Beispielscript auf GitHub: PowerOperation-DependingMachineTags.ps1
Endlich 1.0 – aber noch lange nicht fertig!
Im November 2014 habe ich ein Artikel geschrieben über die Übernahme des HealthCheckScripts für XenApp & XenDesktop 7.x:
XenDesktop & XenApp FMA (7.x) HealthCheck – Oops!… I Did It Again
Jetzt nach fast 2 Jahren fortlaufender Entwicklung dieses Scripts präsentiere ich die Version 1.0 des XenApp & XenDesktop HeathChecks.
Gestartet bin ich mit einer ziemlich rudimentären Version des Scripts und mittlerweile habe ich einige Tester und Mitwirkende bei diesem Script. Zwischenzeitlich ist das Script auch auf GitHub zu finden und es ist positiv überraschend wie viele Leute aus der Community bereits mit helfen das Script zu erweitern und verbessern.
Nachdem viele 0.xVersionen released wurden, ist jetzt neu auch die XML Konfigurationsdatei mit dabei. Diese Änderung, welche wir vor allem Stefan Beckmann zu verdanken haben, ist es Wert die Version nun 1.0 zu nennen.
Der grosse Vorteil der XML-Datei ist es, dass das Script und die Konfiguration von einander getrennt sind. Somit ist sowohl der Austausch des Scripts wie auch die Pflege von vielen verschiedenen Umgebungen kein Problem. Vorher musste nach dem Austausch des Scripts immer noch der Header angepasst werden. Jetzt wird einfach nur das Script aktualisiert, sofern nichts im XML geändert hat bleibt dieses genau so bestehen.
Die XML Datei muss sich im gleichen Verzeichnis befinden wie das Script und der gleiche Namen haben, wie hier dieses Beispiel:
XA-and-XD-HealthCheck.ps1 XA-and-XD-HealthCheck_Parameters.xml
Wie bereits erwähnt wurde die XML Datei von Stefan Beckmann (Twitter: @alphasteff) integriert.
Die neuste Version des Scripts kann jeweils auf GitHub gefunden werden:
https://github.com/sacha81/XA-and-XD-HealthCheck
Die HTML Ausgabe gewinnt von Version zu Version mehr Inhalt, schwierig ist es natürlich zu entscheiden welche Funktionserweiterungsanfragen berücksichtig werden und welche nicht. Jede weitere Funktion kann das Script unübersichtlicher machen und erhöht die Laufzeit des Scripts. Einige Funktionen können deshalb im XML ein und ausgeschaltet werden.
Aktuell wird von den Delivery Controller und den Worker Maschinen (XenApp Server und virtuelle Desktops) der Prozessor, der Festplattenplatz und der Ram Speicher gecheckt. Da in einer Umgebung mit der FMA Architektur problemlos unterschiedliche Versionen von VDAs im Einsatz stehen können, wird nun auch dies geprüft und ausgegeben. Weiter sind für die Störungssuche auch Informationen zu den Hypervisor Hosts in den Worker-Tabellen enthalten.
Dieser Code ist auf GitHub:
https://github.com/sacha81/XA-and-XD-HealthCheck/
Für Fehlermeldungen oder Funktionserweiterungsanfragen benutzt bitte direkt GitHub, natürlich freue mich auch über die Mitarbeit an den Scripts in GitHub!
Opfer des guten Rufes – zu wenig freie pooled Desktops frühzeitig erkennen
Der Citrix Director ist wirklich kein schlechtes Werkzeug und mit der Version 7.7 wurde es sogar noch um einiges besser da nun auch Emails versendet werden können. Aber ein Problem haben wir zurzeit in der Firma trotzdem, die unpersönlichen virtuellen Desktops welche für den Fernzugang genutzt werden, sind auf Grund guter Mund-zu-Mund Propaganda immer populärer. Und nun möchten wir gerne wissen wann es knapp wird bevor keine Desktops mehr zur Verfügung stehen. Natürlich kann ich mir täglich die Zahlen im Director anschauen, aber ich würde eigentlich gerne vom System informiert wenns eng wird.
Dazu habe ich nun ein ganz kurzes Script gemacht, welches sobald die Anzahl freier Desktops einen definierten Schwellwert unterschreitet eine Email auslöst:
Das Script ist auf GitHub:
https://github.com/sacha81/Citrix-XD-Alert-low-free-desktops
Happy New Script – PVS 7.7 HealthCheck
Bald ist Neujahr und ich möchte euch fürs 2016 nur das Beste wünschen! Etwas ganz gutes habe ich euch schon vor dem neuen Jahr, nur einige Stunden nach dem Release vom Citrix Provisioning Server 7.7 kann ich euch auch das neue Health Check Script dazu übergeben.
Heute hat Citrix die Produkte XenApp/XenDesktop 7.7 und Citrix Provisioning Services 7.7 herausgegeben, mit dabei sind eine Menge von neuen Features aber das wichtigste dabei neben dem Support für Windows 10 ist … Trommelwirbel …. die neue Power Shell APO für Citrix Provisioning Services. PVS mit PoSh bei 7.6 und früher war ein Sch….
Was sonst noch neu ist im PVS 7.7 hier: PVS 7.7 New in this release
Jetzt kommt PVS endlich mit einer echte PoSh API welche mehr zurück gibt als nur einen Haufen Strings. Ich konnte mit dem PVS 7.7 Tech Preview rumspielen und bin desshalb auch bereits ready mit dem komplett neuen Script. Achtung, für Versionen 7.6 und tiefer muss immer noch das alter Script verwendet werden. Diese Neue Version ist gültig ab 7.7 und wir dann für die Nachfolgenden Versionen gepflegt.
Wenn ihr neue Funktionen möchtet oder einen Fehler melden wollt, so tut dies bitte direkt auf GitHub.
Wie bereits geschrieben, diese Version geht nicht mit 7.6 und früheren Versionen, wenn ihr dazu ein HealthCheck braucht, gibt es hier das alte Script: Citrix PVS HealthCheck
Das Script findet ihr hier auf GitHub:
Überwache die Profile Verzeichnisse
Bereits vor Jahren als ich als Citrix Admin gearbeitet habe waren die anwachsenden Benutzerprofileverzeichnisse immer wieder ein Thema. Klar ist das Administratorenleben nun mit neueren Technologien wie dem Citrix User Profile Manager oder anderen 3rd Party Lösungen einfacher geworden, Profile Streaming und Exclusions können viel wert sein.
Trotzdem ist es immer noch ein Thema das als Citrix Admin oder Engineer berücksichtigt werden muss. In einer früheren Firma habe ich Tool’s wie Treesize (https://www.jam-software.com/treesize/) oder WinDirStat(http://windirstat.info) gearbeitet. Aber es gibt halt firmen die kein Geld für solche Tools ausgeben wollen oder auch OpenSource nicht trauen, bzw. der Softwareintegrationsprozess kompliziert und langwierig ist.
So hatte ich den Ansatz dieses Profilgrössenproblem mit Bordmitteln zu lösen. Ich habe nun ein kleines PoSh-Script erstellt welches mir rekursiv durch meine Profilordner läuft und die Grössen ausgibt.
#============================================================================================== # Created on: 10.2015 Version: 0.3 # Created by: Sacha Thomet # Filename: GetSubdirSizes.ps1 # # # Description: This script checks some Citrix Provisioning Server, Farm, vDisk & Target device parameters. # # Prerequisite: None # # Call by : Manual # # Change Log: # V0.1 first version # V0.2 only 1 recursive subfolder # V0.3 corrections of calculation # #============================================================================================== $path = "\localhostC$tempprofiles" $top = "4000" $outputpath = "C:tempSubdirSize-Report.txt" Function Get-FolderSize { $Sizes = 0 ForEach ($Item in (Get-ChildItem $Path)) { If ($Item.PSIsContainer) {$Sizes = (Get-ChildItem $path"$Item" -recurse | Measure-Object -property length -sum).sum} Else {Write-Host "No Subfolder"} $SizeFormated = "{0:N2}" -f ($Sizes/1MB) New-Object PSObject -Property @{'Folder'=$Item;'Size'=$SizeFormated } } } $outfile = Get-FolderSize $path | sort -property Size –Descending |select -first $top $outfile | out-file $outputpath echo $outfile
PoSh Script who alert me if I’m running out of pooled Desktops
Just a small one, last days I’ve created a small script who alert me if I’m running out of pooled Desktops.
If you run this script at regular intervals you will receive an email as soon you have less desktops free than you defined as threshold:
#============================================================================================== # Created on: 08.2015 Version: 0.2 # Created by: Sacha Thomet # File name: Citrix-XenDesktop-Alert-low-free-desktops.ps1 # # Description: Check for Free Desktops in DeliveryGroups # # Prerequisite: None # # Call by : Scheduled Task e.g every 10 minutes # # Changelog: # V0.1 Initial Version, create report file from array FreeDesktopReport and attach this to the email. # V0.2 Change from txt-file to formatted HTML-Mail # #============================================================================================== if ((Get-PSSnapin "Citrix.Common.Commands" -EA silentlycontinue) -eq $null) { try { Add-PSSnapin Citrix.* -ErrorAction Stop } catch { write-error "Error Citrix.* Powershell snapin"; Return } } # Change the below variables to suit your environment #============================================================================================== # Variables what should be changed according your environment and wishes $DeliveryGroups = @("Win7-Desktops","Win10-Desktops") $minDesktops = 10 $directoraddress="http://citrixdirector.mycompany.ch" $EnvironmentName="Production XenDesktop" # E-mail report details $emailFrom = "[email protected]" $emailTo = "[email protected]" $smtpServer = "mailrelay.mycompany.ch" #=======DONT CHANGE BELOW HERE ======================================================================================= $mailbody = $mailbody + "<!DOCTYPE html>" $mailbody = $mailbody + "<html>" $mailbody = $mailbody + "<head>" $mailbody = $mailbody + "<style>" $mailbody = $mailbody + "BODY{background-color:#fbfbfb; font-family: Arial;}" $mailbody = $mailbody + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse; width:60%; }" $mailbody = $mailbody + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black; text-align:left;}" $mailbody = $mailbody + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}" $mailbody = $mailbody + "</style>" $mailbody = $mailbody + "</head>" $mailbody = $mailbody + "<body>" $mailbody = $mailbody + "This is the Low-Desktop-Alert for $EnvironmentName, if you receive this mail the value of free desktops is below the configured threshold of $minDesktops desktops! <br><br>" $FreeDesktopReport = @() foreach($dg in $DeliveryGroups) { $desktops = Get-BrokerDesktopGroup | where {$_.Name -eq $dg } $CurrentDeliveryGroup = "" | Select-Object Name, Alert, DesktopsAvailable # Write Array Values $CurrentDeliveryGroup.Name = $dg $CurrentDeliveryGroup.DesktopsAvailable = $desktops.DesktopsAvailable if ($desktops.DesktopsAvailable -lt $minDesktops ) { Write-Host "Number of free desktops to low for DeliveryGroup $dg, sending email" # Add Line to Report $CurrentDeliveryGroup.alert = "True" } $FreeDesktopReport += $CurrentDeliveryGroup } $mailbody += $FreeDesktopReport | ConvertTo-Html $mailbody += "<br><br>Launch Citrix Studio or browse to <a href=$directoraddress>Citrix Director</a> see more information about the current Desktop usage<br>" $mailbody = $mailbody + "<body>" $mailbody = $mailbody + "</html>" # If any record raises an alert, send an email. if (($FreeDesktopReport | where {$_.alert -eq "True"}) -ne $null) {Send-MailMessage -to $emailTo -from $emailFrom -subject "********* Low free Desktop Alert for $EnvironmentName *********" -Body $mailbody -BodyAsHtml -SmtpServer $smtpServer }
Or download the Script here
GeekOvation – PVS Script on Top3
My Citrix PVS Health Check PowerShell Script was nominated into the final round (Top3) of the Citrix GeekOvation Award. This means I have to present my „geeky solution“ on Citrix Synergy 2015 in Orlando on the SYN501 Geek Speak Tonight Session. To be honest to speak in front of so much people is a new thing for me … and it makes me „a bit“ twitchy …
To be nominated for this Award was not just a big surprise for me, I see this also as a honor and glory for my work. I would say it’s not so a big thing this script and it isn’t rocket science.
Dear GeekSpeak Tonight audience, please check out my PVS Health Check Script and provide me feedback if you want. Also have a look to my XenApp / XenDesktop Script.
At this place I want to say thank you to Jason Poyner who gave me the idea for such kind of Script with his XenApp6 HealthCheck and where I took some code lines for the HTML output. To Martin Hartmann who helped me by learning PowerShell and of course to Aaron Parker (http://stealthpuppy.com) who was the assigned CTP helping me polish off my presentation materials.
Update 14.5.2015:
I won – thank YOU
I won the competition and seems that I’m the not-yet-known Geek #1 this Year! Thanks you all! When I saw the work from to other two guys, I had the feeling to have no chance, not just because I’m the only not-native english speaker. David Ott with a great PowerShell script to modify the user profile and Shane O’Neil with the C-Sharp code to have a self healing VDI, GREAT!
Read this blog post from Shane and Paul, if you are interested on more PVS PowerShell stuff: http://blogs.citrix.com/2015/04/07/wait-you-mean-pvs-has-powershell and watch the recording of „SYN514: Turn XenApp and XenDesktop into capacity on demand with Provisioning Services automation“
I’m sure I also had luck to win, and now I have the chance to go to Las Vegas Citrix Synergy 2016. I also want to thank ControlUp for the AppleWatch they sponsored for the GeekOvation Winner.
About the code
Like already mentioned the idea about the health check with the HTML output comes from Jason Poyner. The main script with all the logics it’s from me. The hardest thing when you Script with PoSh on PVS that it doesn’t return proper PS Object, that mean’s you doing a lot of format-cut stuff to get what you want. If you want to know more about that read Paul’s blog section „PVS comes with a PowerShell add-in that is not an easy beast to tame.“
What’s next?
I’m currently working on a Script which shows PVS retries during a variable timeframe (day, week or month) and create a graphic output, in this way you can see at which time you maybe have a traffic jam on your network.
I also made a script to export/import target’s into PVS and another one for export and import in XenDesktop, if I have time I’ll polish and publish this here.
XenDesktop & XenApp FMA (7.x) HealthCheck – Oops!… I Did It Again
Some months ago I’ve created the Citrix PVS Health-Check Script which is a based on the idea and some parts of code from the Health-Check Script for XenApp 6.x of Jason Poyer (http://deptive.co.nz/) .
Because now XenApp 7 with the Release 7.6 is finally in a state where considering an upgrade of the 6.x farms make sense, I belief that the demand for a XenApp 7.x Health Check Script grows.
So I did it again and took the „HealthCheck framework“ to build a new version which combines the Power of the Citrix PS-Snappins for XenDesktop/XenApp and the HTML-Output-Script of the existing HealthCheck Scripts.
The result is a new HealthCheck Script which is usable for XenApp and XenDesktop 7.x and what makes me also happy, with only a few line of more code the Script is downwardly compatible for XenDesktop 5.6 environments.
This is just the first version and I’m sure that more check’s need to be added. Feedback and „Feature requests“ are welcome … And to be honest I have not yet a big environment to test my Script, so please be insightfully if you find some bugs and report them to me.
In the first part of the Script you are able to configure some parameters. You can decide if you only want to see the „bad“ Desktops on which something it’s going wrong or if you want see everything. In huge XenDesktop environments you want definitely only see the bad machines … ( $ShowOnlyErrorVDI = 1 ) Also you can decide if you want only report XenApp or only XenDesktop or both. The Desktops and XenApps are in two different Tables. It’s also possible to exclude Collections ($ExcludeCatalogs) from the Check, so virtual Desktops which are for testing purposes are not checked.
If you have a feature request or a bug report please post it direct on GitHub.
Update 12.05.2016 (Version 0.95):
– Check CPU, Memory and C: of Controllers
– XenApp: Add values: CPU & Memory and Disk Usage
– XenApp: Option to toggle on/off to show Connected Users
– XenApp: DesktopFree set to N/A because not relevant
If you need a Health Check Script for XenApp Version which are older than XenApp 7.x see http://deptive.co.nz/xenapp-farm-health-check-v2 where it’s an excellent work and the inspiration for my HealthCheck-Scripts!
The code is on GitHub:
https://github.com/sacha81/XA-and-XD-HealthCheck/
For Bug Reports or feature Request please use GitHub, of course you can also contribute on this code!
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 protected]" $emailTo = "[email protected]"#,"[email protected]" $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.