Creating a XenDesktop Delivery Group with PowerShell

My last article was on creating a XenDesktop machine catalog with PowerShell - in this article I’m going to create a Delivery Group which provides access to the virtual machines that a part of that catalog.

Like the last article, I’ve taken the PowerShell generated by Citrix Studio, banged my head against the wall a few times, and improved it to create the code presented in this article.

Linking the Code to the UI

To help explain the code, I’ll first run through the Create Delivery Group wizard and show how the code relates to options in the wizard and the Delivery Group properties.

Add-BrokerMachinesToDesktopGroup assigns virtual machines from a specified Machine Catalog to the new Delivery Group.

Selecting the Machine Catalog and the number of desktops - Add-BrokerMachinesToDesktopGroup -Catalog "Windows 8 x86" -Count 5

Selecting the Machine Catalog and the number of desktops - Add-BrokerMachinesToDesktopGroup -Catalog “Windows 8 x86” -Count 5

Specify the delivery type for this Delivery Group when using New-BrokerDesktopGroup.

Selecting the delivery type - New-BrokerDesktopGroup -DeliveryType 'DesktopsOnly'

Selecting the delivery type - New-BrokerDesktopGroup -DeliveryType ‘DesktopsOnly’

New-BrokerEntitlementPolicyRule is used to assign user or group accounts to the Delivery Group.

Assigning users to the Desktop Group - New-BrokerEntitlementPolicyRule -Name "Windows 8 x86_1" -IncludedUsers $brokerUsers -DesktopGroupUid 11

Assigning users to the Desktop Group - New-BrokerEntitlementPolicyRule -Name “Windows 8 x86_1” -IncludedUsers “HOME\Domain Users” -DesktopGroupUid 11

Add-BrokerMachineConfiguration adds StoreFront and UPM configurations to a Delivery Group. The function just adds a machine configuration - the configuration is setup separately. To avoid selecting a StoreFront server for the Delivery Group, don’t use this function.

Selecting a StoreFront server - Add-BrokerMachineConfiguration -DesktopGroup "Windows 8 x86" -InputObject @(1005)

Selecting a StoreFront server - Add-BrokerMachineConfiguration -DesktopGroup “Windows 8 x86” -InputObject @(1005)

When calling New-BrokerDesktopGroup, the Delivery Group name, display or published name and description is specified.

Group name, Display name and description - New-BrokerDesktopGroup -Name "Windows 8 x86" -PublishedName "Windows 8 x86" -Description "Windows 8 x86 with Office 2013, Pooled desktops"

Group name, Display name and description - New-BrokerDesktopGroup -Name “Windows 8 x86” -PublishedName “Windows 8 x86” -Description “Windows 8 x86 with Office 2013, Pooled desktops”*

The wizard does not expose all settings for the Delivery Group, so additional settings require opening the properties of the new group. These can be set during creation of the group when using PowerShell.

The same call to New-BrokerDesktopGroup is used to specify user settings including colour depth and time zone preferences.

Controlling various user settings - New-BrokerDesktopGroup -ColorDepth TwentyFourBit -TimeZone "AUS Eastern Standard Time" -SecureIcaRequired $False

Controlling various user settings - New-BrokerDesktopGroup -ColorDepth TwentyFourBit -TimeZone “AUS Eastern Standard Time” -SecureIcaRequired $False

New-BrokerDesktopGroup and New-BrokerPowerTimeScheme are both used to manage virtual machine power management settings. Setting or modifying the peak and off peak hours isn’t friendly either.

Virtual machine power management settings - New-BrokerPowerTimeScheme -DisplayName 'Weekdays' -DaysOfWeek 'Weekdays' -DesktopGroupUid 11; New-BrokerDesktopGroup -OffPeakDisconnectAction Suspend -OffPeakDisconnectTimeout 15

Virtual machine power management settings - New-BrokerPowerTimeScheme -DisplayName ‘Weekdays’ -DaysOfWeek ‘Weekdays’ -DesktopGroupUid 11; New-BrokerDesktopGroup -OffPeakDisconnectAction Suspend -OffPeakDisconnectTimeout 15

New-BrokerAccessPolicyRule modifies the access policies. This is called twice - once for connections through NetScaler Gateway and once for direct connections.

Modifying access policies - New-BrokerAccessPolicyRule -Name "Windows 8 x86_AG" -AllowedConnections 'ViaAG' -AllowedProtocols @('HDX','RDP') -DesktopGroupUid 11 -Enabled $True -IncludedSmartAccessFilterEnabled $True -IncludedSmartAccessTags @() -IncludedUserFilterEnabled $True

Modifying access policies - New-BrokerAccessPolicyRule -Name “Windows 8 x86_AG” -AllowedConnections ‘ViaAG’ -AllowedProtocols @(‘HDX’,’RDP’) -DesktopGroupUid 11 -Enabled $True -IncludedSmartAccessFilterEnabled $True -IncludedSmartAccessTags @() -IncludedUserFilterEnabled $True

Creating the Delivery Group is relatively straight-forward; however there are some additional steps, such as creating a StoreFront server and working out how to manage peak and off peak times, that require a bit more investigation.

The Code

Below is the full code listing with comments inline that should provide some detail on the process the code follows. At this point the code provides some error checking for the most important steps. There are still some additional steps and error checking that could be integrated into the code.

#---------------------------------------------------------------------------
# Author: Aaron Parker
# Desc:   Using PowerShell to create a XenDesktop 7.x Delivery Group
# Date:   Aug 23, 2014
# Site:   http://stealthpuppy.com
#---------------------------------------------------------------------------
# 

# Set variables for the target infrastructure
# ----------
$adminAddress = 'xd71.home.stealthpuppy.com' #The XD Controller we're going to execute against
$xdControllers = 'xd71.home.stealthpuppy.com'

# Desktop Group properties
$desktopGroupName = "Windows 8 desktops"
$desktopGroupPublishedName = "Windows 8 desktops"
$desktopGroupDesc = "Windows 8 x86 with Office 2013, Pooled desktops"
$colorDepth = 'TwentyFourBit'
$deliveryType = 'DesktopsOnly'
$desktopKind = 'Shared'
$sessionSupport = "SingleSession" #Also: MultiSession
$functionalLevel = 'L7'
$timeZone = 'AUS Eastern Standard Time'
$offPeakBuffer = 10
$peakBuffer = 10
$assignedGroup = "HOME\Domain Users"

#Machine Catalog
$machineCatalogName = "Windows 8 x86"
# ----------

# Change to SilentlyContinue to avoid verbose output
$VerbosePreference = "Continue"

# Create the Desktop Group
# http://support.citrix.com/proddocs/topic/citrix-broker-admin-v2-xd75/new-brokerdesktopgroup-xd75.html
If (!(Get-BrokerDesktopGroup -Name $desktopGroupName -ErrorAction SilentlyContinue)) {
    Write-Verbose "Creating new Desktop Group: $desktopGroupName"
    $desktopGroup = New-BrokerDesktopGroup -ErrorAction SilentlyContinue -AdminAddress $adminAddress -Name $desktopGroupName -DesktopKind $desktopKind -DeliveryType $deliveryType -Description $desktopGroupPublishedName -PublishedName $desktopGroupPublishedName  -MinimumFunctionalLevel $functionalLevel -ColorDepth $colorDepth -SessionSupport $sessionSupport -ShutdownDesktopsAfterUse $True -TimeZone $timeZone -InMaintenanceMode $False -IsRemotePC $False -OffPeakBufferSizePercent $offPeakBuffer -PeakBufferSizePercent $peakBuffer -SecureIcaRequired $False -TurnOnAddedMachine $False -OffPeakDisconnectAction Suspend -OffPeakDisconnectTimeout 15 -Scope @() 
}

# At this point, we have a Desktop Group, but no users or desktops assigned to it, no power management etc.
# Open the properties of the new Desktop Group to see what's missing.

# If creation of the desktop group was successful, continue modifying its properties
If ($desktopGroup) {

    # Add a machine configuration to the new desktop group; This line adds an existing StoreFront server to the desktop group
    # Where does Input Object 1005 come from?
    # http://support.citrix.com/proddocs/topic/citrix-broker-admin-v2-xd75/add-brokermachineconfiguration-xd75.html
    # Write-Verbose "Adding machine configuration to the Desktop Group: $desktopGroupName"
    # Add-BrokerMachineConfiguration -AdminAddress $adminAddress -DesktopGroup $desktopGroup -InputObject @(1005)

    # Add machines to the new desktop group. Uses the number of machines available in the target machine catalog
    # http://support.citrix.com/proddocs/topic/citrix-broker-admin-v2-xd75/add-brokermachinestodesktopgroup-xd75.html
    Write-Verbose "Getting details for the Machine Catalog: $machineCatalogName"
    $machineCatalog = Get-BrokerCatalog -AdminAddress $adminAddress -Name $machineCatalogName
    Write-Verbose "Adding $machineCatalog.UnassignedCount machines to the Desktop Group: $desktopGroupName"
    $machinesCount = Add-BrokerMachinesToDesktopGroup -AdminAddress $adminAddress -Catalog $machineCatalog -Count $machineCatalog.UnassignedCount -DesktopGroup $desktopGroup

    # Create a new broker user/group object if it doesn't already exist
    # http://support.citrix.com/proddocs/topic/citrix-broker-admin-v2-xd75/new-brokeruser-xd75.html
    Write-Verbose "Creating user/group object in the broker for $assignedGroup"
    If (!(Get-BrokerUser -AdminAddress $adminAddress -Name $assignedGroup -ErrorAction SilentlyContinue)) {
        $brokerUsers = New-BrokerUser -AdminAddress $adminAddress -Name $assignedGroup
    } Else {
        $brokerUsers = Get-BrokerUser -AdminAddress $adminAddress -Name $assignedGroup
    }

    # Create an entitlement policy for the new desktop group. Assigned users to the desktop group
    # First check that we have an entitlement name available. Increment until we do.
    $Num = 1
    Do {
        # http://support.citrix.com/proddocs/topic/citrix-broker-admin-v2-xd75/test-brokerentitlementpolicyrulenameavailable-xd75.html
        $Test = Test-BrokerEntitlementPolicyRuleNameAvailable -AdminAddress $adminAddress -Name @($desktopGroupName + "_" + $Num.ToString()) -ErrorAction SilentlyContinue
        If ($Test.Available -eq $False) { $Num = $Num + 1 }
    } While ($Test.Available -eq $False)
    #http://support.citrix.com/proddocs/topic/citrix-broker-admin-v2-xd75/new-brokerentitlementpolicyrule-xd75.html
    Write-Verbose "Assigning $brokerUsers.Name to Desktop Catalog: $machineCatalogName"
    $EntPolicyRule = New-BrokerEntitlementPolicyRule -AdminAddress $adminAddress  -Name ($desktopGroupName + "_" + $Num.ToString()) -IncludedUsers $brokerUsers -DesktopGroupUid $desktopGroup.Uid -Enabled $True -IncludedUserFilterEnabled $False

    # Check whether access rules exist and then create rules for direct access and via Access Gateway
    # http://support.citrix.com/proddocs/topic/citrix-broker-admin-v2-xd75/new-brokeraccesspolicyrule-xd75.html
    $accessPolicyRule = $desktopGroupName + "_Direct"
    If (Test-BrokerAccessPolicyRuleNameAvailable -AdminAddress $adminAddress -Name @($accessPolicyRule) -ErrorAction SilentlyContinue) {
        Write-Verbose "Allowing direct access rule to the Desktop Catalog: $machineCatalogName"
        New-BrokerAccessPolicyRule -AdminAddress $adminAddress -Name $accessPolicyRule  -IncludedUsers @($brokerUsers.Name) -AllowedConnections 'NotViaAG' -AllowedProtocols @('HDX','RDP') -AllowRestart $True -DesktopGroupUid $desktopGroup.Uid -Enabled $True -IncludedSmartAccessFilterEnabled $True -IncludedUserFilterEnabled $True
    } Else {
        Write-Error "Failed to add direct access rule $accessPolicyRule. It already exists."
    }
    $accessPolicyRule = $desktopGroupName + "_AG"
    If (Test-BrokerAccessPolicyRuleNameAvailable -AdminAddress $adminAddress -Name @($accessPolicyRule) -ErrorAction SilentlyContinue) {
        Write-Verbose "Allowing access via Access Gateway rule to the Desktop Catalog: $machineCatalogName"
        New-BrokerAccessPolicyRule -AdminAddress $adminAddress -Name $accessPolicyRule -IncludedUsers @($brokerUsers.Name) -AllowedConnections 'ViaAG' -AllowedProtocols @('HDX','RDP') -AllowRestart $True -DesktopGroupUid $desktopGroup.Uid -Enabled $True -IncludedSmartAccessFilterEnabled $True -IncludedSmartAccessTags @() -IncludedUserFilterEnabled $True
    } Else {
        Write-Error "Failed to add Access Gateway rule $accessPolicyRule. It already exists."
    }

    # Create weekday and weekend access rules
    # http://support.citrix.com/proddocs/topic/citrix-broker-admin-v2-xd75/new-brokerpowertimescheme-xd75.html
    $powerTimeScheme = "Windows 8 Pooled Desktop_Weekdays"
    If (Test-BrokerPowerTimeSchemeNameAvailable -AdminAddress $adminAddress -Name @($powerTimeScheme) -ErrorAction SilentlyContinue) {
        Write-Verbose "Adding new power scheme $powerTimeScheme"
        New-BrokerPowerTimeScheme -AdminAddress $adminAddress -DisplayName 'Weekdays' -Name $powerTimeScheme -DaysOfWeek 'Weekdays' -DesktopGroupUid $desktopGroup.Uid -PeakHours @($False,$False,$False,$False,$False,$False,$False,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$False,$False,$False,$False,$False) -PoolSize @(0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0)
    } Else {
        Write-Error "Failed to add power scheme rule $powerTimeScheme. It already exists."
    }
    $powerTimeScheme = "Windows 8 Pooled Desktop_Weekend"
    If (Test-BrokerPowerTimeSchemeNameAvailable -AdminAddress $adminAddress -Name @($powerTimeScheme) -ErrorAction SilentlyContinue) {
        Write-Verbose "Adding new power scheme $powerTimeScheme"
        New-BrokerPowerTimeScheme -AdminAddress $adminAddress -DisplayName 'Weekend' -Name $powerTimeScheme -DaysOfWeek 'Weekend' -DesktopGroupUid $desktopGroup.Uid -PeakHours @($False,$False,$False,$False,$False,$False,$False,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$False,$False,$False,$False,$False) -PoolSize @(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
    } Else {
        Write-Error "Failed to add power scheme rule $powerTimeScheme. It already exists."
    }

} #End If DesktopGroup

Comments or feedback on bugs, better ways to do things or additional steps is welcome.