<< Back to Script Library

Show or disable unused Citrix Delivery Groups

Show how recently (in number of days) delivery groups have had sessions launched from them, by checking the last used property for each machine in a delivery group.
Specify the minimum number of days last launched to show only delivery groups which have not had sessions launched within that number of days and optionally disable them. Specify 0 days to show all delivery groups.

Run on a Delivery Controller or where the CVAD PowerShell snapins are available, e.g. Studio is installed.
Version: 2.6.19
Created: 2020-10-02
Modified: 2020-10-09
Creator: Guy Leech
Downloads: 75
Tags: citrix cvad delivery groups vdi xenapp xendesktop
The Script Copy Script Copied to clipboard
#requires -version 3

<#
.SYNOPSIS

Show Citrix Delivery Groups not used in the last n days and optionally disable them

.DETAILS

Delivery groups themselves do not have a last used property, this comes from the machines in a delivery group so a delivery group could have been used more recently than reported but the machine(s) used been deleted or moved to another deployment group

.PARAMETER daysNotAccessed

The number of days that the delivery group has not had a session launched from it

.PARAMETER disable

Disable delivery groups that have not had a session launched in the number of days specified by -daysNotAccessed

.PARAMETER ddc

The delivery controller to connect to. If not specified the local machine will be used.

.CONTEXT

Computer (but only on a Citrix Delivery Controller)

.NOTES


.MODIFICATION_HISTORY:

    @guyrleech 02/10/2020  Initial release
    @guyrleech 06/10/2020  Changes after feedback. Added -disable argument
    @guyrleech 07/10/2020  Added guard to stop all delivery groups being disabled
    @guyrleech 09/10/2020  Added most sessions/host metric
#>

[CmdletBinding()]

Param
(
    [double]$daysNotAccessed = 30 ,
    [ValidateSet('true','false')]
    [string]$disable = 'false' ,
    [string]$ddc 
)

$VerbosePreference = $(if( $PSBoundParameters[ 'verbose' ] ) { $VerbosePreference } else { 'SilentlyContinue' })
$DebugPreference = $(if( $PSBoundParameters[ 'debug' ] ) { $DebugPreference } else { 'SilentlyContinue' })
$ErrorActionPreference = $(if( $PSBoundParameters[ 'erroraction' ] ) { $ErrorActionPreference } else { 'Stop' })
$ProgressPreference = 'SilentlyContinue'

[int]$outputwidth = 400

Function Get-DeliveryGroupMachineDetails
{
    Param
    (
        [Parameter(Mandatory)]
        [array]$machinesByDeliveryGroup ,
        [datetime]$lastUsedBefore ,
        [AllowNull()]
        $deliveryGroupDetails
    )
    
    $lastUsed = $null
    [int]$machines = 0
    [int]$inMaintenanceMode = 0
    [int]$registered = 0
    [int]$sessionCount = 0
    [int]$mostSessions = 0
    [int]$logonsDisabled = 0
    [int]$available = 0
    [string]$provisioning = $null
    [string]$lastUsedBy = $null

    if( $deliveryGroupByMachine = $machinesByDeliveryGroup.Where( { ( ! $deliveryGroupDetails -and ! $_.Name ) -or ( $deliveryGroupDetails -and $_.Name -eq $deliveryGroupDetails.Name ) } ) )
    {
        ForEach( $machine in $deliveryGroupByMachine.Group )
        {
            $machines++
            if( ! $lastUsed -or $machine.LastConnectionTime -gt $lastUsed )
            {
                $lastUsed = $machine.LastConnectionTime
                $lastUsedBy = $machine.LastConnectionUser
            }
            if( $machine.InMaintenanceMode )
            {
                $inMaintenanceMode++
            }
            elseif( $machine.RegistrationState -eq 'Registered' -and $machine.WindowsConnectionSetting -eq 'LogonEnabled' )
            {
                $available++
            }
            if( $machine.WindowsConnectionSetting -ne 'LogonEnabled' )
            {
                $logonsDisabled++
            }
            if( $machine.RegistrationState -eq 'Registered' )
            {
                $registered++
            }
            if( $machine.SessionCount -gt $mostSessions )
            {
                $mostSessions = $machine.SessionCount
            }
            $sessionCount += $machine.SessionCount
            if( ! [string]::IsNullOrEmpty( $provisioning ) )
            {
                if( $provisioning -ne $machine.ProvisioningType )
                {
                    $provisioning = $provisioning + $machine.ProvisioningType
                }
            }
            else
            {
                $provisioning = $machine.ProvisioningType
            }
        }
    }
    if( ! $lastUsed -or $lastUsed -le $lastUsedBefore )
    {
        ## don't report machines not in delivery groups if no machines found
        if( $deliveryGroupDetails -or $machines -gt 0 )
        {
            [pscustomobject]@{
                'Delivery Group' = $deliveryGroupDetails | Select-Object -ExpandProperty Name
                'Maintenance Mode' = $deliveryGroupDetails | Select-Object -ExpandProperty InMaintenanceMode
                'Enabled' = $deliveryGroupDetails | Select-Object -ExpandProperty Enabled
                'Description' = $deliveryGroupDetails | Select-Object -ExpandProperty Description
                'Last Used (d)' = $(if( $lastused ) { [math]::Round( ([datetime]::Now - $lastUsed).TotalDays , 0 ) } else { 'No record' } )
                'Last User' = ($lastUsedBy -split '\\')[-1]
                'Provisioning' = $provisioning
                'Sessions' = $sessionCount
                'Densest' = $mostSessions
                'Machines' = $machines ## $deliveryGroupByMachine | Select-Object -ExpandProperty Group | Measure-Object | Select-Object -ExpandProperty Count
                'Available' = $available
                'Maintenance' = $inMaintenanceMode
                'Registered' = $registered
                'Logons Disabled' = $logonsDisabled
            }
        }
    }
}
if( ( $PSWindow = (Get-Host).UI.RawUI ) -and ( $WideDimensions = $PSWindow.BufferSize ) )
{
    $WideDimensions.Width = $outputWidth
    $PSWindow.BufferSize = $WideDimensions
}

[hashtable]$brokerParameters = @{}

if( $PSBoundParameters[ 'ddc' ] )
{
    $brokerParameters.Add( 'AdminAddress' , $ddc )
}

## new CVAD have modules so use these in preference to snapins which are there for backward compatibility
if( ! (  Import-Module -Name Citrix.DelegatedAdmin.Commands -ErrorAction SilentlyContinue -PassThru -Verbose:$false) `
    -and ! ( Add-PSSnapin -Name Citrix.Broker.Admin.* -ErrorAction SilentlyContinue -PassThru -Verbose:$false) )
{
    Throw 'Failed to load Citrix PowerShell cmdlets - is this a Delivery Controller or have Studio or the PowerShell SDK installed ?'
}

[array]$machines = @( Get-BrokerMachine @brokerParameters )
[array]$machinesByDeliveryGroup = @( $machines | Group-Object -Property DesktopGroupName )

if( ! $machinesByDeliveryGroup -or ! $machinesByDeliveryGroup.Count )
{
    Throw "Got no machines"
}

[array]$deliveryGroups = @( Get-BrokerDesktopGroup @brokerParameters )

if( ! $deliveryGroups -or ! $deliveryGroups.Count )
{
    Throw "Got no delivery groups"
}

Write-Verbose -Message "Got $($deliveryGroups.Count) delivery groups and $($machines.Count) machines"

[datetime]$lastUsedDate = (Get-Date).AddDays( -$daysNotAccessed )

## look at all delivery groups but also looks for machines not in delivery groups
[array]$results = @( ForEach( $deliveryGroupDetails in $deliveryGroups )
{
    Get-DeliveryGroupMachineDetails -deliveryGroupDetails $deliveryGroupDetails -machinesByDeliveryGroup $machinesByDeliveryGroup -lastUsedBefore $lastUsedDate
}
    ## Report machines which aren't in a Delivery Group
    Get-DeliveryGroupMachineDetails -machinesByDeliveryGroup $machinesByDeliveryGroup -lastUsedBefore $lastUsedDate
)

Write-Output -InputObject "Found $($deliveryGroups.Count) delivery groups and $($machines.Count) machines in total, including $($results.Where( { -not $_.'Delivery Group' } )|Select-Object -ExpandProperty 'Machines') machines not assigned to any delivery group"
Write-Output -InputObject "Found $($results.Where( { $_.'Machines' -eq 0 } ).Count) delivery groups containing no machines"

if( $results -and $results.Count )
{
    [int]$deliveryGroupsOverAge = $results.Where( { $_.'Delivery Group' } ).Count ## ignore those just in a machine catalogue but not a delivery group

    Write-Output -InputObject "Found $deliveryGroupsOverAge delivery groups not used in last $daysNotAccessed days" ## already filtered in the function that builds the results

    $results | Sort-Object -Property 'Last Used (d)' | Format-Table -AutoSize -Property *

    if( $disable -eq 'true' )
    {
        if( $deliveryGroups.Count -eq $deliveryGroupsOverAge )
        {
            Throw "Disabling is prohibited because all $($deliveryGroups.Count) delivery groups are targeted meaning there would be no enabled ones"
        }

        [int]$disabled = 0
        [int]$actuallyDisabled = 0

        $results.Where( { ! [string]::IsNullOrEmpty( $_.'Delivery Group' ) -and $_.Enabled } ).ForEach(
        {
            $disabled++
            Write-Verbose "Disabling `"$($_.'Delivery Group')`""
            if( ! ( $result = Set-BrokerDesktopGroup -Name $_.'Delivery Group' -Enabled $false @brokerParameters  -PassThru ) -or $result.Enabled -ne $false )
            {
                Write-Warning -Message "Failed to disable delivery group `"$($_.'Delivery Group')`""
            }
            else
            {
                $actuallyDisabled++
            }
        })
        if( $disabled -eq 0 )
        {
            Write-Warning -Message "Found no delivery groups not used in the last $daysNotAccessed days not already disabled"
        }
        else
        {
            Write-Output -InputObject "Successfully disabled $actuallyDisabled delivery groups out of $disabled not used in the last $daysNotAccessed days"
        }
    }
}
else
{
    Write-Output -InputObject "Found no delivery groups not used in the last $daysNotAccessed days"
}