Troubleshooting CITRIX Virtual Apps & Desktops Using PowerShell
Table of Contents

When I am health-checking or otherwise working in customerCitrix Virtual Apps and Desktops (CVAD) environments, I often use a PowerShell prompt and scripts to get the information I need and to perform tasks as I find it easier and quicker than using the various Citrix, or Microsoft, consoles for many operations.
In this post I will show you some of the commands, with parameters, that I use. The beauty of being proficient in PowerShell is that once you know how to filter, sort, display, etc with one type of object, returned by a PowerShell function or cmdlet, you can use the exact same knowledge to work with practically every other cmdlet from every other provider – well worth the sometimes steep learning curve!
Prerequisites
I typically will have these installed on a “jump box” rather than using a delivery controller directly as that risks resource and stability issues (“oops, I rebooted the wrong machine” said no admin ever).
The easiest way to get the cmdlets you need is to just load everything although in a script I would recommend just adding the specific modules that you need (Get-Command will tell you what module a specified cmdlet resides in). In 1912 LTSR and above we run:
Import-Module -Name Citrix*
(or ipmo Citrix* for short where “ipmo” is the alias for “Import-Module”)
For earlier releases, where modules weren’t available, we run:
Add-PSSnapin -Name Citrix*
(or asnp Citrix* for short where “asnp” is the alias for “Add-PSSnapin”)
Note that if you are using Citrix Cloud, you need to use the Remote PowerShell SDK rather than Studio or the MSIs from the CVAD ISO.
One other thing to bear in mind if you’re not running this on a delivery controller, which I don’t recommend, is that you will need to pass -AdminAddress to the CVAD cmdlets together with the name/address of a delivery controller.
If using Citrix Cloud, the argument to -AdminAddress needs to be an on-premises cloud connector. I’m sure I’ve read somewhere in Citrix documentation that you only need to specify it once, but I’ve had problems with that so specify it for every CVAD cmdlet called, albeit usually via a hash table using PowerShell’s “splatting” feature so if I need to change it, or add an extra common parameter, I can do it in one place.
Another potential “gotcha” is that by default the Citrix cmdlets only return 250 records so if working in a large environment, either in terms of users, VDAs, delivery groups, etc, you may need to use -MaxRecordCount (remember you can tab complete so type -max and hit tab or leave it as “-max” as it is not ambiguous – but not in scripts as it can make them difficult to understand when abbreviations are used) and enter a sufficiently large number, like shown below in figure 1.

Fig. 1: Using -MaxRecordCount you can return more than the default 250 records
In some of the examples I use a variable of type string called ‘ddc’ which I have set as one of the delivery controllers so that I don’t reveal its name in the screenshots.
Getting Information
Get-Command Get-Broker*
Pipe the above through Out-GridView (alias ogv) to see in a handy, filterable and sortable, table.
Alternatively, enter Get-Broker and either hit the tab key to cycle through the list of commands or hit ctrl+space to bring a cursor selectable list up on screen as seen in Figure 2.

Fig. 2: Get-Broker brings up a cursor selectable list of all available cmdlets
You can also use the Get-Help cmdlet on them, with optional -ShowWindow or -OnLine parameters, to get more detail and to see examples of their usage.
Q1. How many sessions are there in total, including connected and disconnected?
Get-BrokerSession -AdminAddress $ddc -MaxRecordCount 10000 | measure | select count
Q2. What is the breakdown of active versus disconnected sessions?
Get-BrokerSession @commonCVADparameters | Group-Object -Property SessionState

Fig. 3: Output of the Command described above
The “Connected” state is where there is a logon in progress.
Q3. What connected sessions have been idle for more than 30 minutes?
$activeSessions = Get-BrokerSession @commonCVADparameters -SessionState Active
$activeSessions|where IdleDuration -gt "00:30:00"|select machinename,username,client*,starttime,idle* | Out-GridView
Note that here we get all active sessions, as in not disconnected, and store them in a variable. This way, we can perform analysis of this data without having to fetch it repeatedly, although the downside is that the data does not auto refresh.
Also note, that we filter in the cmdlet itself (-SessionState), where possible, rather than piping through Where-Object (alias where or ?) as that is faster because using Where-Object retrieves the entire result set and then filters it (figure 4).
Q4. When and where did user billybob logon?
Get-BrokerSession -UserName guyrleechbillybob @commonCVADparameters | select machinename,client*,DesktopGroupName,CatalogName,starttime

Fig. 5: With the command described above you can retrieve information about the last login of a certain user
Q5. How many machines are registered and not in maintenance mode per delivery group?
Get-BrokerMachine -InMaintenanceMode $false -RegistrationState Registered @commonCVADparameters | group DesktopGroupName|select count,name|sort count

Fig. 6: List of all CVAD machines that are registered and not in maintenance mode, sorted by delivery group
Q6. Which machines have the most user sessions, e.g., do we have an overloaded machine or is load balancing work ok?
Get-BrokerMachine @commonCVADparameters | Sort SessionCount -Descending|select -first 10 machinename, sessioncount, DesktopGroupName, last*|Out-GridView
Q7. Show Delivery Group Statistics
Get-BrokerDesktopGroup | select Name,Desktop*,Enabled,InMaintenanceMode,Total*|ogv -PassThru
Note the use of -PassThru to Out-GridView so the selected items in the grid view – when “OK” is pressed – will be made available where the cmdlet was called from.
Alternatively, they can be piped through Set-ClipBoard (alias scb) so that the data can be pasted into Notepad, an email, OneNote, change control notes, etc.
Performing Actions
Enable Maintenance Mode
Set-BrokerMachine -MachineName GUYRLEECH\GLXA19PVS41 -InMaintenanceMode $true
One can also pipe the results of Get-BrokerMachine into Set-BrokerMachine.Message Users
You can even create custom Notification via Send-BrokerSessionMessage, as can be seen in figure 10.
Get-BrokerSession -MachineName GUYRLEECH\GLXA19PVS41 @commonCVADparameters | Send-BrokerSessionMessage -Title "Message from Guy Leech" -MessageStyle Critical -Text "Please logoff & back on as an urgent reboot is required"
Note that this will not prompt the users nor give them a grace period so ensure they are expecting their session to be logged off!
Logoff All Sessions for a Specific User
Get-BrokerSession -UserName guyrleech\billybob @commonCVADparameters | Stop-BrokerSession
As above, this will not prompt the user nor give them a grace period.Related posts
7 min read
How ChatGPT and AI Will Change the Way We Build PowerShell Scripts – Forever
May 4, 2023 by Doug Finke
6 min read
The best PowerCLI commands for admins – part 1: VM inventory management
May 4, 2023 by Philip Lorenz
About the author:
Consultant, Software Developer, Troubleshooter. Current Citrix CTP, VMware vExpert, and Microsoft MVP. Inventor of AppSense Application Manager. PowerShell addict.
Latest posts:
- Automate snapshots and templates with PowerCLI – Part 2
- How ChatGPT and AI Will Change the Way We Build PowerShell Scripts – Forever
- The best PowerCLI commands for admins – part 1: VM inventory management
- The Tools You Need When Troubleshooting Active Directory
- ScriptRunner Portal Edition R5 – Mission Accomplished