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.
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.
-
Baljit Birdi
Baljit is a digital workspaces consultant specializing in Citrix, with a unique skillset developed through extensive engineering and automation work in financial institutions, as a consultant with Citrix’s internal consulting team, and as an Escalation Engineer within Citrix Technical Support.
FYI the script has a spelling error, it should be sessionstate in the where filter, not sesssionstate