[Script] Checking the Percentage of Desktops Available in Each XenDesktop Site


When you manage multiple sites you may find that Citrix Director lacks an “at-a-glance” view of the status of multiple sites. This becomes extra apparent in a large pod-based design where multiple sites may exist within the same datacenter or in an active-active datacenter setup. With VDI, it is important to check the total number of VM’s that are available for brokering. If the number is too low, you could be faced with a boot storm (assuming you have powered off virtual machines) if a large number of users log in. Or worse, you may not have available capacity. It is important to catch such incidents before they happen. Therefore, I present to you a script to output a simple HTML formatted table that can be incorporated into existing monitoring dashboards or sites (such as SharePoint).

clip_image001

Figure 1 Sample table output from script

   <#
    .SYNOPSIS
        Generates a report from multiple XenDesktop sites showing desktops available.
    .DESCRIPTION
        Monitoring multiple sites can require multiple checks to ensure each is`
         in proper running order.  This script will determine the percentage of`
          desktops available and display an HTML formatted table to provide an `
          quick summary for an administrator.
    .PARAMETER SitesToMonitor
        A collection of strings with delivery controller addresses
    .PARAMETER WarningThreshold
        When the value falls below this number, cells in the report will be highlighted
    .PARAMETER Interactive
        Enable this switch to load the file after generation
    .EXAMPLE
        .\CheckDesktopsAvailable.ps1 -SitesToMonitor "site1.contoso.local","site2.contoso.local" -OutputPath "C:\temp\report.html"
    .EXAMPLE
        .\CheckDesktopsAvailable.ps1 -SitesToMonitor "site1.contoso.local","site2.contoso.local"  -OutputPath "C:\temp\report.html" -WarningThreshold 15 
    .EXAMPLE
        .\CheckDesktopsAvailable.ps1 -SitesToMonitor "site1.contoso.local","site2.contoso.local"  -OutputPath "C:\temp\report.html" -WarningThreshold 15 -Interactive
        
    .NOTES
        Author:             Preston Gallwas (functions credited)
        Twitter:            @AtumVirt
    .LINK
        http://www.atumvirt.com
    #>


    [CmdletBinding()]
    Param (
        [Parameter(Mandatory,Position=0,ValueFromPipeline)]
        [string[]]$SitesToMonitor,
        [Parameter(Mandatory,Position=1)]
        [string]$OutputPath,
        [Parameter(Position=2)]
        [int]$WarningThreshold=15,
        [Parameter(Position=3)]
        [switch]$Interactive
        )

Asnp Citrix*
Function Set-AlternatingRows {
 <#
 .SYNOPSIS
  Simple function to alternate the row colors in an HTML table
 .DESCRIPTION
  This function accepts pipeline input from ConvertTo-HTML or any
  string with HTML in it.  It will then search for  and replace 
  it with .  With the combination of CSS it
  can set alternating colors on table rows.
  
  CSS requirements:
  .odd { background-color:#ffffff; }
  .even { background-color:#dddddd; }
  
  Classnames can be anything and are configurable when executing the
  function.  Colors can, of course, be set to your preference.
  
  This function does not add CSS to your report, so you must provide
  the style sheet, typically part of the ConvertTo-HTML cmdlet using
  the -Head parameter.
 .PARAMETER Line
  String containing the HTML line, typically piped in through the
  pipeline.
 .PARAMETER CSSEvenClass
  Define which CSS class is your "even" row and color.
 .PARAMETER CSSOddClass
  Define which CSS class is your "odd" row and color.
 .EXAMPLE $Report | ConvertTo-HTML -Head $Header | Set-AlternateRows -CSSEvenClass even -CSSOddClass odd | Out-File HTMLReport.html
 
  $Header can be defined with a here-string as:
  $Header = @"
  
  "@
  
  This will produce a table with alternating white and grey rows.  Custom CSS
  is defined in the $Header string and included with the table thanks to the -Head
  parameter in ConvertTo-HTML.
 .NOTES
  Author:         Martin Pugh
  Twitter:        @thesurlyadm1n
  Spiceworks:     Martin9700
  Blog:           www.thesurlyadmin.com
  
  Changelog:
   1.1         Modified replace to include the  tag, as it was changing the class
                        for the TH row as well.
   1.0         Initial function release
 .LINK
  http://community.spiceworks.com/scripts/show/1745-set-alternatingrows-function-modify-your-html-table-to-have-alternating-row-colors
    .LINK
        http://thesurlyadmin.com/2013/01/21/how-to-create-html-reports/
 #>
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory,ValueFromPipeline)]
        [string]$Line,
       
        [Parameter(Mandatory)]
        [string]$CSSEvenClass,
       
        [Parameter(Mandatory)]
        [string]$CSSOddClass
    )
 Begin {
  $ClassName = $CSSEvenClass
 }
 Process {
  If ($Line.Contains(""))
  { $Line = $Line.Replace("","")
   If ($ClassName -eq $CSSEvenClass)
   { $ClassName = $CSSOddClass
   }
   Else
   { $ClassName = $CSSEvenClass
   }
  }
  Return $Line
 }
}
Function Set-CellColor{
   <#
    .SYNOPSIS
        Function that allows you to set individual cell colors in an HTML table
    .DESCRIPTION
        To be used inconjunction with ConvertTo-HTML this simple function allows you
        to set particular colors for cells in an HTML table.  You provide the criteria
        the script uses to make the determination if a cell should be a particular 
        color (property -gt 5, property -like "*Apple*", etc).
        
        You can add the function to your scripts, dot source it to load into your current
        PowerShell session or add it to your $Profile so it is always available.
        
        To dot source:
            .".\Set-CellColor.ps1"
            
    .PARAMETER Property
        Property, or column that you will be keying on.  
    .PARAMETER Color
        Name or 6-digit hex value of the color you want the cell to be
    .PARAMETER InputObject
        HTML you want the script to process.  This can be entered directly into the
        parameter or piped to the function.
    .PARAMETER Filter
        Specifies a query to determine if a cell should have its color changed.  $true
        results will make the color change while $false result will return nothing.
        
        Syntax
          
        
        ::= the same as $Property.  This must match exactly
        ::= "-eq" | "-le" | "-ge" | "-ne" | "-lt" | "-gt"| "-approx" | "-like" | "-notlike" 
             ::= "-and" | "-or"
             ::= "-not"
        
        The script first attempts to convert the cell to a number, and if it fails it will
        cast it as a string.  So 40 will be a number and you can use -lt, -gt, etc.  But 40%
        would be cast as a string so you could only use -eq, -ne, -like, etc.  
    .PARAMETER Row
        Instructs the script to change the entire row to the specified color instead of the individual cell.
    .INPUTS
        HTML with table
    .OUTPUTS
        HTML
    .EXAMPLE
        get-process | convertto-html | set-cellcolor -Propety cpu -Color red -Filter "cpu -gt 1000" | out-file c:\test\get-process.html

        Assuming Set-CellColor has been dot sourced, run Get-Process and convert to HTML.  
        Then change the CPU cell to red only if the CPU field is greater than 1000.
        
    .EXAMPLE
        get-process | convertto-html | set-cellcolor cpu red -filter "cpu -gt 1000 -and cpu -lt 2000" | out-file c:\test\get-process.html
        
        Same as Example 1, but now we will only turn a cell red if CPU is greater than 100 
        but less than 2000.
        
    .EXAMPLE
        $HTML = $Data | sort server | ConvertTo-html -head $header | Set-CellColor cookedvalue red -Filter "cookedvalue -gt 1"
        PS C:\> $HTML = $HTML | Set-CellColor Server green -Filter "server -eq 'dc2'"
        PS C:\> $HTML | Set-CellColor Path Yellow -Filter "Path -like ""*memory*""" | Out-File c:\Test\colortest.html
        
        Takes a collection of objects in $Data, sorts on the property Server and converts to HTML.  From there 
        we set the "CookedValue" property to red if it's greater then 1.  We then send the HTML through Set-CellColor
        again, this time setting the Server cell to green if it's "dc2".  One more time through Set-CellColor
        turns the Path cell to Yellow if it contains the word "memory" in it.
        
    .EXAMPLE
        $HTML = $Data | sort server | ConvertTo-html -head $header | Set-CellColor cookedvalue red -Filter "cookedvalue -gt 1" -Row
        
        Now, if the cookedvalue property is greater than 1 the function will highlight the entire row red.
        
    .NOTES
        Author:             Martin Pugh
        Twitter:            @thesurlyadm1n
        Spiceworks:         Martin9700
        Blog:               www.thesurlyadmin.com
          
        Changelog:
            1.5 Added ability to set row color with -Row switch instead of the individual cell
            1.03 Added error message in case the $Property field cannot be found in the table header
            1.02            Added some additional text to help.  Added some error trapping around $Filter
                            creation.
            1.01 Added verbose output
            1.0 Initial Release
    .LINK
        http://community.spiceworks.com/scripts/show/2450-change-cell-color-in-html-table-with-powershell-set-cellcolor
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory,Position=0)]
        [string]$Property,
        [Parameter(Mandatory,Position=1)]
        [string]$Color,
        [Parameter(Mandatory,ValueFromPipeline)]
        [Object[]]$InputObject,
        [Parameter(Mandatory)]
        [string]$Filter,
        [switch]$Row
    )
    
    Begin {
        Write-Verbose "$(Get-Date): Function Set-CellColor begins"
        If ($Filter)
        {   If ($Filter.ToUpper().IndexOf($Property.ToUpper()) -ge 0)
            {   $Filter = $Filter.ToUpper().Replace($Property.ToUpper(),"`$Value")
                Try {
                    [scriptblock]$Filter = [scriptblock]::Create($Filter)
                }
                Catch {
                    Write-Warning "$(Get-Date): ""$Filter"" caused an error, stopping script!"
                    Write-Warning $Error[0]
                    Exit
                }
            }
            Else
            {   Write-Warning "Could not locate $Property in the Filter, which is required.  Filter: $Filter"
                Exit
            }
        }
    }
    
    Process {
        ForEach ($Line in $InputObject)
        {   If ($Line.IndexOf("(.*?)<\/th>' -AllMatches
                $Index = 0
                ForEach ($Match in $Search.Matches)
                {   If ($Match.Groups[1].Value -eq $Property)
                    {   Break
                    }
                    $Index ++
                }
                If ($Index -eq $Search.Matches.Count)
                {   Write-Warning "$(Get-Date): Unable to locate property: $Property in table header"
                    Exit
                }
                Write-Verbose "$(Get-Date): $Property column found at index: $Index"
            }
            If ($Line -match "(.*?)<\/td>' -AllMatches
                $Value = $Search.Matches[$Index].Groups[1].Value -as [double]
                If (-not $Value)
                {   $Value = $Search.Matches[$Index].Groups[1].Value
                }
                If (Invoke-Command $Filter)
                {   If ($Row)
                    {   Write-Verbose "$(Get-Date): Criteria met!  Changing row to $Color..."
                        If ($Line -match "")
                        {   $Line = $Line -replace "","")
                        }
                    }
                    Else
                    {   Write-Verbose "$(Get-Date): Criteria met!  Changing cell to $Color..."
                        $Line = $Line.Replace($Search.Matches[$Index].Value,"$Value")
                    }
                }
            }
            Write-Output $Line
        }
    }
    
    End {
        Write-Verbose "$(Get-Date): Function Set-CellColor completed"
    }
}
function AddDesktopsAvailablePercent {
[CmdletBinding()]
    Param(
        [Parameter(Mandatory,ValueFromPipeline)]
        $Input
        )
        try {  $val=$($Input.DesktopsAvailable/$Input.totalDesktops) }
        catch { $val=0 }
   
        $per=[Math]::Truncate($val*100)

    $Input | Add-Member -NotePropertyName "DesktopsAvailablePercent" -NotePropertyValue $per

}




$Header = @"

"@

$dgs=@()
foreach ($siteAddress in $SitesToMonitor)
{
 $dgs+=Get-BrokerDesktopGroup -AdminAddress $siteAddress
}


foreach($group in $dgs)
{
 $group | AddDesktopsAvailablePercent
}

$output= $dgs | select name,desktopsavailable, TotalDesktops,InMaintenanceMode,DesktopsAvailablePercent | ConvertTo-HTML -head $header -body "

Desktops Available - Warning at $($WarningThreshold)

" | ` set-cellcolor InMaintenanceMode yellow -filter "InMaintenanceMode -eq 'True'"  ` | set-cellcolor DesktopsAvailablePercent red -filter "DesktopsAvailablePercent -lt $($WarningThreshold)" ` | Set-AlternatingRows -CSSEvenClass even -CSSOddClass odd $output | out-file $OutputPath if($Interactive) { invoke-expression $OutputPath }  

Copy the script above into a text editor and save it as a .ps1 file. From there you can execute it via the command line or run it as a schedule task. For example:

.\CheckDesktopsAvailable.ps1 -SitesToMonitor “site1.contoso.local”,”site2.contoso.local” -OutputPath “C:\temp\report.html” -Interactive

If you are running the script as a scheduled task, omit the -Interactive switch to receive the file only. You can then integrate the output via another method into your monitoring platform of choice.

Leave a comment

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.