Skip to main content

Introduction

When building new images to be used with Citrix Provisioning Services (PVS) we can at times come across situations where we see excessive use of the writecache virtual disk (vdiskdif.vhdx) located on our persistent drive of the target device which can eventually lead to the persistent drive filling up and the target device / Citrix VDA crashing and impacting all users.

There’s often usual suspects to this, in addition to the possibility that app-specific behaviours on the image are to blame (excessive log file or temp file writes, auto-downloads of files, etc.) Common contributors to high writes against C: in a default image configuration (not a complete list) are:

  • SCCM pushing packages
  • Windows Built-in Apps not pruned from image
  • Offline files being enabled
  • Windows Defender and other AV software generating a lot of writes or downloads to C:
  • Citrix optimizer not used on images
  • Large profiles (any solution that is not using a profile container, so Citrix Profile Manager, etc. where profile exclusions were not properly tuned and monitored for path pruning)
  • Never rebooting target devices on a schedule, the list goes on…

Common tools to see what’s going on within the OS include Performance Analyzer, ProcMon, and one of Michael Shuster’s favourites: FolderChangesView.

The Script

I created the script from scratch with help from a program I use to look at .net, wmi, cim objects in Windows. The script gets performance statistics for target device machines provisioned by PVS. Administrators can use it to track and monitor usage of RAMCACHE and WRITECACHE (redirected to drive from overflow of RAM) on their target devices and to determine performance or risk of disk exhaustion issues. Administrators could also extend the script to send alert at certain thresholds via e-mail etc. to alert administrators of potential risks that could cause data loss for users.

DESCRIPTION

The Get-TargetDeviceVirtualDiskStatus script cmdlet is the PowerShell version of retrieving device and product edition information on the target device. Citrix Administrators can use this information to determine if Target Devices are performing as they intended, provide daily/weekly reporting on performance, or for high-level troubleshooting of virtual disks provisioned by Citrix Provisioning Services. This script cmdlet provides vDisk information such as where the target devices is booted from, either the local hard drive or from the virtual disk, the current access modes, the cache type used on the virtual disk and the cache statistics.

PARAMETER ComputerName <String[]>

Specifies the computers for which this cmdlet gets active processes. The default is the local computer. Type the NetBIOS name, an IP address, or a fully qualified domain name (FQDN) of one or more computers. This parameter does rely on Windows PowerShell remoting if you intend to query multiple computers.

EXAMPLE

This example shows all the computer names of the VDA in the PooledDesktops Delivery Group and displays the vDisk status of each computer.

citrix pvs target device cache use script

SCRIPT

<#
    .SYNOPSIS
        Gets performance statistics for target device machines provisioned by Citrix Provisioning Services (PVS).
 
    .DESCRIPTION
        The Get-TargetDeviceVirtualDiskStatus script cmdlet is the PowerShell version of retrieving device and product
        edition information on the target device. Citrix Administrators can use this information to determine if Target Devices
        are performing as they intended, provide daily/weekly reporting on performance, or for high-level troubleshooting
        of virtual disks provisioned by Citrix Provisioning Services. This script cmdlet provides vDisk information such
        as where the target devices is booted from, either the local hard drive or from the virtual disk, the current
        access modes, the cache type used on the virtual disk and the cache statistics.
     
    .PARAMETER ComputerName <String[]>
        Specifies the computers for which this cmdlet gets active processes. The default is the local computer. Type the
        NetBIOS name, an IP address, or a fully qualified domain name (FQDN) of one or more computers. This parameter does
        relies on Windows PowerShell remoting if you intend to query multiple computers.
 
    .EXAMPLE
        PS C:scripts> Add-PSSnapin -Name Citrix.Broker.Admin.V2
        PS C:scripts> $tdobj = (Get-BrokerDesktop -DesktopGroupName PooledDesktops -MaxRecordCount 1000 | Where-Object -FilterScript {$_.SessionState -eq "Active"}).DnsName
        PS C:scripts> .Get-TargetDeviceVirtualDiskStatus -ComputerName $tdobj | Format-Table
 
 
        ComputerName               BootFrom VirtualDisk        Mode     RamCacheMB  CacheType                                    CacheSizeMB CacheUsedMB Version
        ------------               -------- -----------        ----     ----------  ---------                                    ----------- ----------- -------
        ctx-pvs-p-001.birdi.pri    vDisk    W1021H2.2.avhdx    ReadOnly       2240  Device RAM with overflow on local hard drive       30718        1793 7.24.25
        ctx-pvs-p-002.birdi.pri    vDisk    W1021H2.2.avhdx    ReadOnly       2194  Device RAM with overflow on local hard drive       30718        2815 7.24.25
        ctx-pvs-p-003.birdi.pri    vDisk    W1021H2.1.avhdx    ReadOnly       2269  Device RAM with overflow on local hard drive       30718        5089 7.24.25
        ctx-pvs-p-004.birdi.pri    vDisk    W1021H2.1.avhdx    ReadOnly       2259  Device RAM with overflow on local hard drive       30718        3391 7.24.25
        ctx-pvs-p-005.birdi.pri    vDisk    W1021H2.vhdx       ReadOnly       2296  Device RAM with overflow on local hard drive       30718        1121 7.24.25
 
        This example shows all the computer names of the VDA in the PooledDesktops Delivery Group, and displays the vDisk status of each computer.
 
    .EXAMPLE
        PS C:scripts> .Get-TargetDeviceVirtualDiskStatus -ComputerName ctx-pvs-m-001
 
 
        ComputerName : ctx-pvs-m-001
        BootFrom     : vDisk
        VirtualDisk  : W1021H2.3.avhdx
        Mode         : ReadWrite
        RamCacheMB   : 0
        CacheType    : None
        CacheSizeMB  : 0
        CacheUsedMB  : 0
        Version      : 7.24.25
 
        This example shows the results of the target device master machine and what its current status it is in.
 
    .INPUTS
        String[]
 
    .OUTPUTS
        PSCustomObject
 
    .LINK
        https://docs.citrix.com/en-us/provisioning/current-release/configure/configure-targets/target-status-tray-using.html#starting-the-virtual-disk-status-tray
        https://docs.citrix.com/en-us/provisioning/current-release/configure/configure-targets.html#target-device-operation-and-performance-statistics
       
    .NOTES
        NAME: Get-TargetDeviceVirtualDiskStatus
        AUTHOR: Baljit Birdi
        DATE: 2022/06/20
        CHANGE HISTORY:
            2022/06/20 0.1 : Created inital content.
            2022/06/26 0.2 : Added comment based help file.
            2022/06/26 1.0 : Finalized script cmdlet.
            2023/10/16 1.0 : Code and help file update.
#>
    [CmdletBinding(DefaultParameterSetName = 'ComputerName',
                   PositionalBinding = $true)]
Param
(
    [Parameter(Mandatory = $false,
                ValueFromPipeline = $true,
                ValueFromPipelineByPropertyName = $true,
                ParameterSetName = 'ComputerName',
                Position = 0)]
    [String[]]$ComputerName =  $env:COMPUTERNAME
)
Process
{
    ForEach ($Computer in $ComputerName)
    {
        Try
        {
            $PSCmdlet.WriteVerbose("Connect to $Computer.")
            $obj = Get-CimInstance -ComputerName $Computer -Namespace ROOTcitrixpvs -ClassName PVS_VDisk -ErrorAction Stop
            $wcobj = (Invoke-WmiMethod -ComputerName $Computer -Namespace rootdefault -Class StdRegProv -Name GetSTRINGValue -ArgumentList @(2147483650,"SYSTEMControlSet001ServicesbnistackPvsAgent","WriteCacheDrive") -ErrorAction Stop).sValue
            $psobject = [PSCustomObject]@{
                ComputerName = $Computer
                BootFrom = $obj.Boot_From
                VirtualDisk = $obj.VDisk_Name
                Mode = $(If($obj.Write_Cache_Type -match "None")
                {
                    'ReadWrite'
                }
                Else
                {
                    'ReadOnly'
                })
                RamCacheMB = $(If($obj.Write_Cache_Type -match "None")
                {
                    '0'
                }
                Else
                {
                    $([Math]::Round((Get-WmiObject -ComputerName $Computer -Class Win32_PerfRawData_PerfOS_Memory).PoolNonpagedBytes / 1MB))
                })
                CacheType = $obj.Write_Cache_Type
                CacheSizeMB = $obj.Write_Cache_Volume_Size
                CacheUsedMB = $(Get-CimInstance -ComputerName $Computer -ClassName CIM_DataFile -Filter "Name='$($wcObj)\vdiskdif.vhdx'" -ErrorAction Continue).FileSize /1MB
                Version = $(Get-CimInstance -ComputerName $Computer -Namespace ROOTcitrixpvs -ClassName PVS_Target -ErrorAction Continue).Target_Software_Version
            }
            $PSCmdlet.WriteObject($psobject)
        }
        Catch
        {
            $PSCmdlet.WriteWarning("Unable to get the virtual disk information from $Computer.")
            $PSCmdlet.WriteError($_)
            $psobject = [PSCustomObject]@{
                ComputerName = $Computer
            }
            $PSCmdlet.WriteObject($psobject)
        }
    }
}

 

DISCLAIMER: THIS SOFTWARE OR SCRIPT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Subscribe
Notify of
guest
1 Comment
Inline Feedbacks
View all comments
Greg Ramsaran
Greg Ramsaran
6 months ago

FYI the script has a spelling error, it should be sessionstate in the where filter, not sesssionstate

Redefine Your Approach to Technology and Innovation

Schedule a call to discover how customized solutions crafted for your success can drive exceptional outcomes, with Ferroque as your strategic ally.