XenApp/PVS Global Farm Overview

Since there was a lot of interest in the last Visio I posted, I thought I’d share another.

These diagrams outline high level overviews of a global XenApp w/PVS deployment, with XenApp zones and PVS sites in each datacenter. Each XenApp zone has two data collectors/XML brokers, PVS-Streamed OU-based worker groups. Each geographic region has a corresponding StoreFront Store (directed by host name):



The PVS farm configuration is very similar, consisting of sites in each datacenter to stream XenApp workers for each XenApp zone in that datacenter, with the master database homed in the US datacenter:



The intent of these overviews are mainly to demonstrate how the XenApp and PVS farms interact in a global zone/site architecture. I’ll share some overview diagrams of XenApp zones and PVS sites in another post. Hope you enjoy!


PVS Write Cache Monitor

I was recently working on a PVS deployment where we wanted to monitor and alert on target devices that were exceeding 70% write cache utilization. Since there wasn’t a way to do this in the PVS console, I dug into the PVS PoSH SDK to find a way to do this programatically.

After some research I came up with the following script that will check all target devices in a PVS farm, and send an email alert listing any machines that are using 70%+ of their write cache (adjustable via the $threshold variable):

$pass = cat .\securestring.txt | ConvertTo-SecureString
$mycred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "domain\admin",$pass
$message = @()
$threshold = 70

function get-value{
return $strText.SubString($strText.IndexOf($strDelimiter)+2)
function get-name{
return $strText.SubString(0,$strText.IndexOf($strDelimiter))
Add-PSSnapin McliPS* -ErrorAction SilentlyContinue
$all = @()
$obj = New-Object System.Collections.ArrayList
$lines = Mcli-Get DeviceInfo -f ServerName,ServerIpConnection,DeviceName,SiteName,CollectionName,Active,Status,diskLocatorName
for($i=0;$i -lt $lines.length;$i++){
	if(($lines[$i].length -gt 0) -and ($lines[$i].contains(":")) -and -not ($lines[$i] -match "Executing: Get "))
		$name = get-name -strText $lines[$i] -strDelimiter ":"
		$value = get-value -strText $lines[$i] -strDelimiter ":"
		if ($name -eq "status" -and $value.Length -le 0)
			$value = "0"
		if ($value.Contains(",") -and $name -eq "status")
		$obj | Add-Member -membertype noteproperty -name $name -Value $value.Split(",")[1]
		$obj | Add-Member -membertype noteproperty -name $name -Value $value
		if($lines[$i].contains("#") -or (($i+1) -eq $lines.length)){
		$all += $obj
		$obj = New-Object psObject

foreach ($item in $all)
	if ([int] $item.status -gt $threshold)
		$message += $item
$message = $message | Where-Object -FilterScript { ([int] $_.status -gt 0) -and ([int]$_.status -le 100) } | Sort-Object {[int] $_.status} -descending | Format-Table @{Expression={$_.deviceName};Label="Device"}, @{Expression={$_.status};Label="RAM Cache Used (%)"}
function sendMail{

     Write-Host "Sending Email"

     #SMTP server name
     $smtpServer = "mail.domain.com"

     #Creating a Mail object
     $msg = new-object Net.Mail.MailMessage

     #Creating SMTP server object
     $smtp = new-object Net.Mail.SmtpClient($smtpServer)
	 $smtp.Credentials = $mycred

     #Email structure 
     $msg.From = "citrixadmin@domain.com"
     $msg.ReplyTo = "citrixadmin@domain.com"
     $msg.subject = "PVS Write Cache $($threshold)%+ Utilization: " + $summary
	 $msg.body = Out-String -InputObject $message
	 $msg.priority = "High"

     #Sending email 

if ($message.Count -gt 0)