This article walks though the process of using the XenDesktop PowerShell SDK to create a Machine Creation Services based catalog.

Although the article is written referring to XenDesktop 5.5, it should work in any version 5.0 or later.

Why might you want to do this?

There are several reasons why you might want to script a catalog creation;

  • The type of catalog you want to create is not available via the Desktop Studio interface (i.e. Dedicated, Random)
  • To create XenDesktop catalogs from a separate system you want to integrate with via the PowerShell SDK
  • You just want to understand how the XenDesktop PowerShell SDKs work.

This is easy right?

This might all seem fairly simple, just look at the online help for the PowerShell SDKs ( and away you go.  However there are a few complications that you need to be aware of;

  • Desktop Studio writes some additional data into some of the SDK objects so it can keep track of the system. Without understanding this additional data Desktop Studio might not be able to operate properly on the Catalogs that are created via scripts
  • The SDK object model is split into several parts, allowing the separation of concerns.  This makes some of the sources of the data needed for commands to be less obvious that you might expect.

Object Model Overview

This object relationship diagram shows the entities that are involved in the SDK when creating a machine creation services based catalog.  The 4 different colours represent the different areas of the system that are involved, the Host objects are in dark blue, the Broker objects are in orange, the AD account management objects are in light blue and the provisioning objects are in purple.  This is just for reference really , and might aid understanding of the operations performed and discussed in more detail later in the article.

Where to Start?

A good start point for this is the information in the PowerShell section of Desktop Studio (located in the tabs on the top most level node in the Desktop Studio Console)

This allow you to undertake an operation in Desktop Studio and the see what PowerShell SDK commands were undertaken to perform the action requested.  This provides a script that gives some good guidance as a start point for a script, but it is not necessarily clear what all the commands are doing, and the source of all the parameters required.  Some of these commands might not be required for your own scripts as they are just provided for Desktop Studio to keep track of what is happening and your script has the context to understand this itself in a lot of cases.

This article will start with one of these Desktop Studio script fragments, and explain what the various parts do and why they are there.  It will then go on to turn the script into something a little bit more like something that could be used in a separate process flow to create catalogs.

Getting the initial site created

For the purposes of this article, it is assumed that the XenDesktop site is already installed and configured, this that means that

  • There is at least one controller in the site with the database created
  • A hypervisor is available for use
  • A host connection and Host object is defined in XenDesktop

All This can be done via the Desktop Studio  ‘Quick deploy’ or a ‘Desktop deployment’ options.


  • If using Desktop Deploy make sure you select the radio button to ‘use XenDesktop to Create virtual machines’.  This will ensure that you have the connection and host parameters set in your system ready for use.  If you want to use different Host, networks or storage for this article, you can use the configuration nodes in Desktop Studio to set these up, you just need to know the name of the host object that you created for this purpose.
  • The environment does not need to be completly clean, Desktop Studio can have been used further to create catalogs/desktop groups already and the processes described in this article will all still work.

Desktop Studio Catalog Creation Script

The script below was produced by Desktop Studio when asking for a new Machine Creation Services based Catalog (i.e. Pooled machines with a random allocation).  This was creating a Catalog using a golden Master Image that already had a snapshot made for it.  If a snapshot was not available and a Virtual Machine was selected rather than a snapshot then Desktop studio will create a snapshot for you first.  This adds an additional command to the script to take the snapshot (New-HypVMSnapshot see SDK reference guide for details).  The only change that I have made to the desktop studio output is to add line numbers so that the script can be referred to more easily in the article.

  <li>New-BrokerCatalog -AllocationType 'Random' -CatalogKind 'SingleImage' -Name 'TestCatalog' -PvsForVM @() -AdminAddress 'A1-DDC.A1.ian.local:80'</li>
  <li>New-AcctIdentityPool -IdentityPoolName 'TestCatalog' -NamingScheme 'Test###' -NamingSchemeType 'Numeric' -OU 'OU=MCS_machines,DC=A1,DC=ian,DC=local' -Domain 'A1.ian.local' -AllowUnicode -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Get-ConfigServiceGroup -ServiceType 'Broker' -MaxRecordCount 2147483647 -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ConfigServiceGroupMetadata -ServiceGroupUid ef76fed3-d97b-4cb4-b649-e03b540995b2 -Property 'Citrix_DesktopStudio_BrokerCatalogIdentityPoolReferencePrefix_1' -Value 'be2e8725-4290-4283-9a49-223b89c6355a' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>New-ProvScheme -ProvisioningSchemeName 'TestCatalog_TestHost' -HostingUnitName 'TestHost' -IdentityPoolName 'TestCatalog' -VMCpuCount 1 -VMMemoryMB 2024 -CleanOnBoot -MasterImageVM 'XDHyp:\hostingunits\TestHost\A1-DDC.vm\initial install - not configured.snapshot' -RunAsynchronously -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvTaskMetadata -TaskId 9a6e38f9-8cdf-4a5e-84cf-d8e239a92907 -Property 'Citrix_DesktopStudio_TaskGroupId' -Value '734f8e44-6ed8-4be2-b03e-3f330b48212a' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvTaskMetadata -TaskId 9a6e38f9-8cdf-4a5e-84cf-d8e239a92907 -Property 'Citrix_DesktopStudio_DesktopCatalogId' -Value '1' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvTaskMetadata -TaskId 9a6e38f9-8cdf-4a5e-84cf-d8e239a92907 -Property 'Citrix_DesktopStudio_ImagesToCopyCount' -Value '1' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvTaskMetadata -TaskId 9a6e38f9-8cdf-4a5e-84cf-d8e239a92907 -Property 'Citrix_DesktopStudio_VMsToCreateCount' -Value '1' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvTaskMetadata -TaskId 9a6e38f9-8cdf-4a5e-84cf-d8e239a92907 -Property 'Citrix_DesktopStudio_StartTime' -Value '634665492557161034' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Get-ProvTask -TaskId 9a6e38f9-8cdf-4a5e-84cf-d8e239a92907 -MaxRecordCount 2147483647 -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Get-ProvTask -TaskId 9a6e38f9-8cdf-4a5e-84cf-d8e239a92907 -MaxRecordCount 2147483647 -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Set-BrokerCatalog -Name 'TestCatalog' -PvsForVM @('af68fd46-242f-4228-9e41-2034760ae5fc:ab3eac6f-230a-4067-8133-2e50d150f8df') -AdminAddress 'A1-DDC.A1.ian.local:80'</li>
  <li>Get-ChildItem -Path 'xdhyp:\connections' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Get-BrokerHypervisorConnection -MaxRecordCount 2147483647 -AdminAddress 'A1-DDC.A1.ian.local:80'</li>
  <li>Get-AcctADAccount -IdentityPoolUid 00000000-0000-0000-0000-000000000000 -State 'Available' -Lock $False -MaxRecordCount 2147483647 -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>New-AcctADAccount -IdentityPoolName 'TestCatalog' -Count 1 -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvSchemeControllerAddress -ProvisioningSchemeName 'TestCatalog_TestHost' -ControllerAddress @('A1-DDC.A1.ian.local')</li>
  <li>New-ProvVM -ProvisioningSchemeName 'TestCatalog_TestHost' -ADAccountName @('A1\Test001$') -RunAsynchronously -AdminAddress 'A1-DDC.A1.ian.local:80'</li>
  <li>Add-ProvTaskMetadata -TaskId eb18debc-7d5c-4023-96e7-d4718137dbc2 -Property 'Citrix_DesktopStudio_TaskGroupId' -Value '734f8e44-6ed8-4be2-b03e-3f330b48212a' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvTaskMetadata -TaskId eb18debc-7d5c-4023-96e7-d4718137dbc2 -Property 'Citrix_DesktopStudio_DesktopCatalogId' -Value '1' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Lock-ProvVM -ProvisioningSchemeName 'TestCatalog_TestHost' -VMID @('6d76e801-464d-5166-66e2-bc271a1544fe') -Tag 'Brokered' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>New-BrokerMachine -CatalogUid 1 -HostedMachineId '6d76e801-464d-5166-66e2-bc271a1544fe' -HypervisorConnectionUid 1 -MachineName 'S-1-5-21-2034153655-595690533-2570515849-1105' -AdminAddress 'A1-DDC.A1.ian.local:80'</li>
  <li>Add-ProvTaskMetadata -TaskId 9a6e38f9-8cdf-4a5e-84cf-d8e239a92907 -Property 'Citrix_DesktopStudio_TimeTaken' -Value '12427570003' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvTaskMetadata -TaskId eb18debc-7d5c-4023-96e7-d4718137dbc2 -Property 'Citrix_DesktopStudio_ImagesToCopyCount' -Value '1' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvTaskMetadata -TaskId eb18debc-7d5c-4023-96e7-d4718137dbc2 -Property 'Citrix_DesktopStudio_VMsToCreateCount' -Value '1' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvTaskMetadata -TaskId eb18debc-7d5c-4023-96e7-d4718137dbc2 -Property 'Citrix_DesktopStudio_StartTime' -Value '634665492557161034' -AdminAddress 'a1-ddc.a1.ian.local'</li>
  <li>Add-ProvTaskMetadata -TaskId eb18debc-7d5c-4023-96e7-d4718137dbc2 -Property 'Citrix_DesktopStudio_TimeTaken' -Value '12427570003' -AdminAddress 'a1-ddc.a1.ian.local'</li>

Here are some things that are worth pointing out here:

  • Metadata – There are several objects that can hold data about them that is not interpreted or used by the Citrix services, but instead can store information that external consumers wish to relate to the objects.  Desktop Studio makes use of this to store information about the objects for its own purposes.  If you want to create catalogs that Desktop Studio will operate on afterwards then some of this metadata will need to be added by your scripts.
  • Tasks – These are long running operations, and can be run in 2 modes (Synchronously or asynchronously).

Line by line description of the Desktop Studio script

Each line is described separatly below:

  1. Creates a Broker Catalog, this is the catalog that will be populated with machines that are about to be created.
  2. Creates an Identity Pool, this defines the mechanism for creating AD computer accounts.  This will become a container for AD accounts created for the machines to be created.
  3. Retrieves the ServiceGroup identity for the Broker.  This is used in the step 4 below as an object to add metadata to (see step 4 below for details).
  4. Adds metadata to the ServiceGroup for the Broker that defines the association between the Catalog (created in step 1) and the Identity pool (created in step 2).  This is used only by desktop studio and is used to associate these 2 items together.  The objects will become connected via the Provisioning Scheme object (in step 5 below), but in the event of this object being deleted Desktop Studio needs to maintain this relationship to keep operating as expected.  If this is not setup this way, desktop studio will report the catalog as broken and no operations on the catalog will be able to be undertaken in this circumstance. Note: The metadata property must be made up as follows; the number on the end is the UID of the BrokerCatalog.  If this number is 2 digits long or shorter then the prefix should be ‘BrokerCatalogIdentityPoolReferencePrefix_’ otherwise it should be ‘CatalogIdentityPoolReference_’
  5. Creates a Provisioning Scheme object.  This is a ‘template’ for the machines that are to be created.  It specifies which hypervisor, network, storage, memory, how many cpu’s etc should be used. This takes parameters from the already set up system, such as the HostingUnit name and the path to the VM snapshot to be used for the machines that will be created.  This command makes a ‘consolidated’ copy of the VM snapshot being used, as a result this can take a while to complete so this is a long running process.  The Desktop studio script specified the -RunAsyncronous flag on this command.  This means that the command will return flow of control to the admin before it has completed and it is the administrators responsibility to wait for this to finish before performing any operations that require it to be complete.  If this flag is not specified, then the command will run synchronously in line and control will not return to the script until the command completes (either successfully or otherwise).
  6. Metadata is added to the task object created in step 5 (the task is the representation of the long running operation).  This is purely for Desktop Studio operation, there is no need to do this in your own scripts and there will be no detrimental affects on Desktop Studio if this is not done.
  7. As 6 above.
  8. As 6 above.
  9. As 6 above.
  10. As 6 above.
  11. Desktop Studio is retrieving status about the long running task created in step 5.  If you run your script in synchronous mode this is not needed, however this will need to be done in a loop until the new provisioning scheme operation is complete if running asyncrounously.
  12. see 11 above.
  13. This is updating the BrokerCatalog object to contain some information in the PvsForVM attribute.  This information is not used by the Citrix services, it is only used by Desktop Studio.  The attribute contains the provisioning scheme UID and hosting unit UID.  This must be provided if Desktop Studio is required to be used on the Catalog after it has been created.
  14. This line is obtaining the hosting unit to check the hypervisor connection is in maintenance mode.  Hypervisor connections in maintenance mode cannot be used so this check is done so that Desktop Studio can provide good error message to administrators.  There is no need to do this if your script is never expected to cope with this condition.
  15. see 14 above (actually the maintenance mode flag is on the hypervisor connection above but Desktop Studio always retrieves the broker connection object every time is fetches the hypervisor connection object for completeness.
  16. Desktop Studio is getting a list of available Machine Identities from the Identity pool.  This is done so that if there are existing accounts that have been created in the past but not used can be consumed instead of creating more account un-necassarily.  This is not needed in a script, as new accounts can be created instead (as long as the script is running in a context that has permissions to do this).  If the script does not have permissions to create accounts, then the script will have to be adjusted to consume available accounts, and it would be the responsibility of a separate process to provide a pool of accounts into the identity pool prior to the running of the script.
  17. This actually creates the required AD computer accounts in Active Directory.  The script creates one account, but can create more than that if required.  This is controlled via the ‘Count’ parameter of the command.  The accounts will be created into the OU defined in the provisioning Scheme created in step 5 above.
  18. This line adds a set of controller addresses to the provisioning Scheme object.  This is a list of addresses that the machines created can use to register with a broker when deployed.  The machines registration addresses can be supplied in many ways, however this information is required if the admin wants to use ‘quick deploy’ in the VDA metaInstaller.  Changes to this list will only affect machines created after the change, and not any existing machines.  Existing machines would need to be updated via the VDA GPOs or ListOfDDC registry keys (would require update to the master image).
  19. Creates virtual machines, based on the template definition in the Provisioning Scheme created in step 5 above.  This again is a long running operation, so the comments made in step 5 about these applies here too.
  20. See 6 above
  21. See 6 above
  22. This line locks the Provisioned virtual machines.  This is a simple setting to stop accidental modification of the virtual machine. This is for the use of consumers of the SDK, so that they can indicate that the virtual machine is in use and why it is locked.  The script locks the VM with a tag of ‘Broker’ to indicate the the virtual machine is created and added to a broker catalog and should not be deleted without being removed from the Catalog first.  Nothing makes use of the Tag name, so this can be set to anything that is required by the script writer.
  23. This line creates a Broker Machine object.  These are the objects that are stored in the catalog.  They join together the Provisioned machine and the catalog.
  24. See 6 above.
  25. See 6 above.
  26. See 6 above.
  27. See 6 above.
  28. See 6 above.

Things that we can do to make this more consumable as a script

Next thing to do is to turn this Desktop Studio script fragment into a script that is more consumable:

  • Use variables – As you can see there are some cmdlets that take parameters (like TaskId), it is not clear where the value used in these parameters comes from.  Desktop Studio is using values from the result objects from some of the earlier cmdlets, but there is no obvious way to see this.
  • Remove the un-required commands
  • Add machine creation into a loop so the number of machines being created can easily be controlled

Note: The line numbers are added to the script to improve readability, these will need to be removed before the script can be executed.

  <li># Set up some useful variables including the hostingUnit and connections and the number of Virtual machines to create and add to the catalog</li>
  <li># You will need to update the HostingUnit string and the number of Vms to create appropriately.</li>
  <li><code>$adminAddress = 'a1-ddc.a1.ian.local'
  • $numVMsToCreate = 1
  • $hostingUnit = get-item “XDHyp:\HostingUnits\TestHost”
  • $hostConnection = get-item $hostingUnit.hypervisorConnection
  • $brokerHypConnection = Get-BrokerHypervisorConnection -HypHypervisorConnectionUid $hostConnection.HypervisorConnectionUid
  • $brokerServiceGroup= Get-ConfigServiceGroup -ServiceType ‘Broker’ -MaxRecordCount 2147483647 -AdminAddress $adminAddress
  • # Create the broker catalog and the AC Identity account pool
  • $catalog = New-BrokerCatalog -AllocationType ‘Random’ -CatalogKind ‘SingleImage’ -Name ‘TestCatalogScripted’ -PvsForVM @() -AdminAddress $adminAddress
  • $adPool = New-AcctIdentityPool -IdentityPoolName ‘TestCatalogScript’ -NamingScheme ‘TestScript###’ -NamingSchemeType ‘Numeric’ -OU ‘OU=MCS_machines,DC=A1,DC=ian,DC=local’ -Domain ‘A1.ian.local’ -AllowUnicode -AdminAddress $adminAddress
  • ###################################################################
  • #### Add the required desktop Studio metadata to the Service Group.
  • $uidLength = $catalog.uid.ToString().Length
  • if ($uidLength -lt 3){
  • $ServiceGroupMetadataProperty = ‘Citrix_DesktopStudio_BrokerCatalogIdentityPoolReferencePrefix_’+$catalog.uid
  • }else{
  • $ServiceGroupMetadataProperty = ‘Citrix_DesktopStudio_CatalogIdentityPoolReference_’+$catalog.uid
  • }
  • Add-ConfigServiceGroupMetadata -ServiceGroupUid $brokerServiceGroup.ServiceGroupUid -Property $serviceGroupMetadataProperty -Value $adPool.IdentityPoolUid -AdminAddress $adminAddress
  • ###################################################################
  • #create the ProvisioningScheme and wait for it to complete (reporting progress)
  • $provSchemeTaskID = New-ProvScheme -ProvisioningSchemeName ‘TestCatalogScripted’ -HostingUnitUID $hostingUnit.HostingUnitUID -IdentityPoolUID $adpool.IdentityPoolUid -VMCpuCount 1 -VMMemoryMB 2024 -CleanOnBoot -MasterImageVM ‘XDHyp:\hostingunits\TestHost\A1-DDC.vm\initial install – not configured.snapshot’ -RunAsynchronously -AdminAddress $adminAddress
  • $ProvTask = get-provTask -TaskID $provSchemeTaskID -AdminAddress $adminAddress
  • $taskProgress = 0
  • while ($provTask.Active -eq $true){
  • if ($taskProgress -ne $provTask.TaskProgress){
  • $taskProgress = $provTask.TaskProgress
  • write-host “New ProvScheme Progress is $($provTask.TaskProgress)%”
  • }
  • $ProvTask = get-provTask -TaskID $provSchemeTaskID -AdminAddress $adminAddress
  • sleep 30
  • }
  • write-host “New ProvScheme Creation Finished”
  • $provScheme = get-provScheme -ProvisioningSchemeUID $provTask.ProvisioningSchemeUid
  • Add-ProvSchemeControllerAddress -ProvisioningSchemeUID $provScheme.ProvisioningSchemeUID -ControllerAddress @(‘A1-DDC.A1.ian.local’)
  • ###################################################################
  • # add the PVSForVM information to the Catalog
  • $pvsForVm =”$($provScheme.ProvisioningSchemeUID):$($hostingUnit.HostingUnitUID)”
  • Set-BrokerCatalog -InputObject $catalog -PvsForVM @($pvsForVm ) -AdminAddress $adminAddress
  • ###################################################################
  • # create the AD accounts required and then create the Virtual machines (reporting progress)
  • $accts = New-AcctADAccount -IdentityPoolUid $adPool.IdentityPoolUid -Count $numVMsToCreate -AdminAddress $adminAddress
  • $provVMTaskID = New-ProvVM -ProvisioningSchemeUID $provScheme.ProvisioningSchemeUID -ADAccountName $accts.SuccessfulAccounts -RunAsynchronously -AdminAddress $adminAddress
  • # wait for the VMS tp finish Provisioning
  • $ProvTask = get-provTask -TaskID $provVMTaskID
  • $CreatedVirtualMachines = @()
  • while ($provTask.Active -eq $true){
  • sleep 5
  • if ($CreatedVirtualMachines.Count -ne $provTask.CreatedVirtualMachines.Count){
  • $CreatedVirtualMachines = $provTask.CreatedVirtualMachines
  • write-host “Created $($provTask.CreatedVirtualMachines.Count) Machines”
  • }
  • $ProvTask = get-provTask -TaskID $provVMTaskID
  • }
  • write-host “VM Creation Finished”
  • # Lock the VMs and add them to the broker Catalog
  • $provisionedVMs = get-ProvVM -ProvisioningSchemeUID $provScheme.ProvisioningSchemeUID -AdminAddress $adminAddress
  • $provisionedVMs | Lock-ProvVM -ProvisioningSchemeUID $provScheme.ProvisioningSchemeUID -Tag ‘Brokered’ -AdminAddress $adminAddress
  • $provisionedVMs | ForEach-Object {New-BrokerMachine -CatalogUid $catalog.UID -HostedMachineId $_.VMId -HypervisorConnectionUid $brokerHypConnection.UID -MachineName $_.ADAccountSid -AdminAddress $adminAddress}
  • What this script does not do.

    This script does not do any validation. For example if the AD accounts fail to create (perhaps because accounts with this name already exist) then the script will fail with errors.  If the script fails part way though some objects will have created and if you want to retry the script you will need to clean the environment back again if you want to avoid further errors.

    What happens now?

    The catalog will appear in Desktop Studio, along with the machines that have been created.  You will be able to use desktop Studio in exactly the same way as if the catalog had been created in Desktop Studio, going on to use it as normal i.e. you can still do anything including:

    • Add more machines to the catalog
    • Create desktop groups using the catalog
    • Remove machines from the catalog
    • Delete the catalog