Citrix Virtual Apps and Desktops 2411 SDK

Creating a Catalog

The following example shows how to create a catalog for a set of Machine Creation Services (MCS) machines.

Before you begin, make sure you follow the steps detailed in Get started with the SDK.

This document tells you how to use Studio to perform the operation you want to script (in this case, to create a catalog for a set of Machine Creation Services machines) and collect the log of SDK operations that Studio made to perform the task. This output can then be customized to produce a script for automating catalog creation.

Note:

To ensure you always get the latest enhancements and fixes, Citrix recommends you follow the procedure described in this document, rather than copying and pasting the example script. Line breaks have been added to the script for readability.

Understand the script

The following section explains what each part of the script produced by Studio is doing. This will help you with the customization of your own script. Line numbers have been added for readability.

Start-LogHighLevelOperation -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -Source 'Studio' -StartTime 29/05/2023 14:43:08 `
   -Text 'Create Machine Catalog `'ExampleMachines`''
<!--NeedCopy-->

Starts a logged operation and returns a log ID which is supplied to subsequent operations to associate them with the larger task.

New-BrokerCatalog -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -AllocationType 'Permanent' -Description 'Example Machines' `
   -IsRemotePC $False -LoggingId f39a2792-064a-43eb-97c7-397cc1238e46 -MinimumFunctionalLevel 'L7' -Name 'ExampleMachines' `
   -PersistUserChanges 'OnPvd' -ProvisioningType 'MCS' -Scope @() -SessionSupport 'SingleSession'
<!--NeedCopy-->

Creates a Broker catalog. This catalog is populated with machines which are about to be created.

New-AcctIdentityPool -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -AllowUnicode -Domain 'dumdev.internal.citrix.com' `
   -IdentityPoolName 'ExampleMachines' -LoggingId f39a2792-064a-43eb-97c7-397cc1238e46 -NamingScheme 'Example-####' `
   -NamingSchemeType 'Numeric' -OU 'OU=DUMVMs,DC=dumdev,DC=internal,DC=citrix,DC=com' -Scope @()
<!--NeedCopy-->

Creates an Identity Pool. This defines the mechanism for creating AD computer accounts. This becomes a container for AD accounts created for the machines that are to be created.

Set-BrokerCatalogMetadata -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -CatalogId 1 `
   -LoggingId 39a2792-064a-43eb-97c7-397cc1238e46 -Name 'Citrix_DesktopStudio_IdentityPoolUid' `
   -Value 'b99aee6d-8772-4dbc-978b-8eb9a26e2407'
<!--NeedCopy-->

Sets metadata on the Broker catalog with details of the Identity Pool. This is not essential.

Test-ProvSchemeNameAvailable -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -ProvisioningSchemeName @('ExampleMachines')
<!--NeedCopy-->

Checks that the requested name is available. This is not essential.

New-ProvScheme -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -CleanOnBoot -HostingUnitName 'SharedNFS' `
   -IdentityPoolName 'ExampleMachines' -LoggingId f39a2792-064a-43eb-97c7-397cc1238e46 `
   -MasterImageVM 'XDHyp:\hostingunits\SharedNFS\BaseVM.vm\Base OS,domain joined and activated.snapshot \Pre-reqs installed.snapshot\\Updates Applied.snapshot\VDA75-no agent.snapshot\Updated Agent.snapshot' `
   -NetworkMapping @{0='xdhyp:\hostingunits\SharedNFS\Network0.network'} -ProvisioningSchemeName 'ExampleMachines' `
   -RunAsynchronously -Scope @() -UsePersonalVDiskStorage -VMCpuCount 1 -VMMemoryMB 1024
<!--NeedCopy-->

Creates a provisioning scheme object. This is a template for the machines that are to be created. It specifies the hypervisor, network, storage, memory, number of CPUs to be used etc. It takes parameters from the system already set up, such as the HostingUnit name and the path to the VM snapshot to be used for the machines to be created. This command makes a ‘consolidated’ copy of the VM snapshot being used and, as a result, the process can take time to complete.

In this example, the Studio script specified the -RunAsyncronous flag on this command. This means the command will return control to the administrator before it has completed, so you must wait for it to finish before performing any operations that require it to be complete. If this flag is not specified, the command runs synchronously in-line and control is not returned until the command completes (successfully or otherwise). You can check the status of an asynchronous task using the Get-ProvTask cmdlet. Supply the task ID returned from the operation that started the task; in this case, the New-ProvScheme cmdlet.

Set-BrokerCatalog -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -LoggingId f39a2792-064a-43eb-97c7-397cc1238e46 `
   -Name 'ExampleMachines' -ProvisioningSchemeId 76125e3a-9001-4993-86b6-eefc85c87880
<!--NeedCopy-->

Updates the BrokerCatalog with the unique Id of the provisioning scheme created above.

Add-ProvSchemeControllerAddress -AdminAddress 'ddc.dumdev.internal.citrix.com:80' `
   -ControllerAddress @('DDC.dumdev.internal.citrix.com') `
   -LoggingId f39a2792-064a-43eb-97c7-397cc1238e46 -ProvisioningSchemeName 'ExampleMachines'
<!--NeedCopy-->

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 Controller (broker) when deployed. The machines’ registration addresses can be supplied in many ways; however, this information is required if the administrator wants to use the ‘Allow Machine Creation Service to supply this’ in the VDA installer. Changes to this list affect only machines created after the change, not existing machines.

Get-AcctADAccount -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -IdentityPoolUid b99aee6d-8772-4dbc-978b-8eb9a26e2407 `
   -Lock $False -MaxRecordCount 2147483647 -State 'Available'
<!--NeedCopy-->

Studio gets a list of available Machine Identities from the Identity Pool so that, if existing accounts have been created in the past but are unused, these can be consumed instead of creating new accounts. Note that this is not required in a script because new accounts can be created instead, provided the script is running in a context that has permissions to do this. However, if the script does not have permissions to create accounts, change the script to consume available accounts (a separate process will be required to provide a pool of accounts into the Identity Pool, before running the script).

New-AcctADAccount -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -Count 2 `
   -IdentityPoolUid b99aee6d-8772-4dbc-978b-8eb9a26e2407 -LoggingId f39a2792-064a-43eb-97c7-397cc1238e46
<!--NeedCopy-->

Creates the required AD computer accounts in Active Directory. The script creates one account but, if required, it can create more using the ‘Count’ parameter of the command. The accounts are created into the OU defined in the provisioning scheme created above.

New-ProvVM -ADAccountName @('DUMDEV\Example-0001\$','DUMDEV\Example-0002\$') -AdminAddress 'ddc.dumdev.internal.citrix.com:80' `
   -LoggingId f39a2792-064a-43eb-97c7-397cc1238e46 -ProvisioningSchemeName 'ExampleMachines' -RunAsynchronously
<!--NeedCopy-->

Creates virtual machines, based on the template definition in the provisioning scheme created above. This process may take time to complete.

Lock-ProvVM -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -LoggingId f39a2792-064a-43eb-97c7-397cc1238e46 `
    -ProvisioningSchemeName 'ExampleMachines' -Tag 'Brokered' -VMID @('0710bb77-d01f-d006-4d67-5472e5cd349f')
<!--NeedCopy-->

Locks the provisioned virtual machines and prevents accidental modification of the virtual machine. Consumers of the SDK can use this to indicate that the virtual machine is in use and why it is locked. The script locks the VM with a tag of ‘Brokered’ to indicate the virtual machine is created and added to a Broker catalog and must not be deleted without first being removed from the catalog. You can set the Tag name to whatever is required.

New-BrokerMachine -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -CatalogUid 1 `
   -HostedMachineId '0710bb77-d01f-d006-4d67-5472e5cd349f' `
   -HypervisorConnectionUid 1 -LoggingId f39a2792-064a-43eb-97c7-397cc1238e46 `
   -MachineName 'S-1-5-21-3918710733-2340574387-1999698698-109114'
<!--NeedCopy-->

Creates a Broker Machine object. These are objects stored in the catalog which join the provisioned machine with the catalog.

Start-BrokerMachinePvdImagePrepare -AdminAddress 'ddc.dumdev.internal.citrix.com:80' -InputObject @(2) `
   -LoggingId f39a2792-064a-43eb-97c7-397cc1238e46
<!--NeedCopy-->

Requests the Broker Service to initiate a preparation operation for Personal vDisk. This is required to allow the machine to initialize the storage for Personal vDisk.

Stop-LogHighLevelOperation -AdminAddress 'ddc.dumdev.internal.citrix.com:80' `
   -HighLevelOperationId f39a2792-064a-43eb-97c7-397cc1238e46 -IsSuccessful $true
<!--NeedCopy-->

Stops the logged operation begun in the first step and indicates it was successful.

Customize the script

The following section shows how to convert and adapt the Studio output into a script that is more consumable. In addition to using variables and removing commands that are not required, it shows how to add machine creation into a loop so that you can control the number of machines created.

[CmdletBinding()]
param
(
   [Parameter(Mandatory=$true)] [string] $hostingUnitPath,
   [Parameter(Mandatory=$true)] [string] $catalogName,
   [string] $catalogDescription,
   [Parameter(Mandatory=$true)] [int] $numVmsToCreate,
   [string] $adminAddress,
   [Parameter(Mandatory=$true)] [string] $namingScheme,
   [string] $OU,
   [Parameter(Mandatory=$true)] [string] $domain,
   [Parameter(Mandatory=$true)] [string] $masterImagePath
)

Set-HypAdminConnection -AdminAddress $adminAddress

$hostingUnit = get-item $hostingUnitPath
$hostConnection = $hostingUnit.hypervisorConnection

$brokerHypConnection = Get-BrokerHypervisorConnection -HypHypervisorConnectionUid $hostConnection.HypervisorConnectionUid

# Start logged operation
$loggingOp = Start-LogHighLevelOperation -AdminAddress $adminAddress -Source 'Scripted' -Text "Create Machine Catalog `'$catalogName`'"
$loggingId = $loggingOp.Id

# Create the broker catalog and the AD Identity account pool
$catalog = New-BrokerCatalog -AllocationType 'Permanent' -Description $catalogDescription -IsRemotePC $False `
   -MinimumFunctionalLevel 'L7' -Name $catalogName -PersistUserChanges 'OnPvd' -ProvisioningType 'MCS' `
   -Scope @() -SessionSupport 'SingleSession' -LoggingId $loggingId -AdminAddress $adminAddress

$adPool = New-AcctIdentityPool -IdentityPoolName $catalogName -NamingScheme $namingScheme `
   -NamingSchemeType 'Numeric' -OU $OU -Domain $domain -AllowUnicode `
   -LoggingId $loggingId -AdminAddress $adminAddress

Set-BrokerCatalogMetadata -CatalogId $catalog.Uid -Name 'Citrix_DesktopStudio_IdentityPoolUid' `
   -Value $adPool.IdentityPoolUid -LoggingId $loggingId -AdminAddress $adminAddress

###################################################################
# Create the ProvisioningScheme and wait for it to complete (reporting progress)

$provSchemeTaskID = New-ProvScheme -ProvisioningSchemeName $catalogName -HostingUnitUID $hostingUnit.HostingUnitUID `
   -IdentityPoolUID $adpool.IdentityPoolUid -CleanOnBoot -MasterImageVM $masterImagePath -UsePersonalVDiskStorage `
   -RunAsynchronously -LoggingId $loggingId -AdminAddress $adminAddress

$ProvTask = get-provTask -TaskID $provSchemeTaskID -AdminAddress $adminAddress
$taskProgress = 0

write-host "Creating New ProvScheme"
while ($provTask.Active -eq $true)
{
   # catch an uninitialized task progress, this occurs until the product initialized the value
   try {$totalPercent = if ($provTask.TaskProgress){$provTask.TaskProgress} else {0}} catch {}
   Write-Progress -activity "Creating Provisioning Scheme:" -status "$totalPercent% Complete:" -percentcomplete $totalPercent
   sleep 30
   $ProvTask = get-provTask -TaskID $provSchemeTaskID -AdminAddress $adminAddress
}
write-host "New ProvScheme Creation Finished"

$provScheme = get-provScheme -ProvisioningSchemeUID $provTask.ProvisioningSchemeUid
$controllers = Get-BrokerController | select DNSName
Add-ProvSchemeControllerAddress -ProvisioningSchemeUID $provScheme.ProvisioningSchemeUID -ControllerAddress $controllers `
   -LoggingId $loggingId -AdminAddress $adminAddress

###################################################################
# Set the provisioning scheme id for the broker catalog

Set-BrokerCatalog -InputObject $catalog -ProvisioningSchemeId $provTask.ProvisioningSchemeUid -LoggingId $loggingId -AdminAddress $adminAddress

###################################################################
# create the AD accounts required and then create the Virtual machines (reporting progress)
$accts = New-AcctADAccount -IdentityPoolUid $adPool.IdentityPoolUid -Count $numVMsToCreate -LoggingId $loggingId `
   -AdminAddress $adminAddress

$provVMTaskID = New-ProvVM -ProvisioningSchemeUID $provScheme.ProvisioningSchemeUID `
   -ADAccountName $accts.SuccessfulAccounts -RunAsynchronously -LoggingId $loggingId -AdminAddress $adminAddress

# wait for the VMS to finish Provisioning
$ProvTask = get-provTask -TaskID $provVMTaskID -AdminAddress $adminAddress
while ($provTask.Active -eq $true)
{
   # catch an uninitialized task progress, this occurs until the product initialized the value
   try {$totalPercent = if ($provTask.TaskProgress){$provTask.TaskProgress} else {0}} catch {}
   Write-Progress -activity "Creating Machines:" -status "$totalPercent% Complete:" -percentcomplete $totalPercent
   sleep 5
   $ProvTask = get-provTask -TaskID $provVMTaskID -AdminAddress $adminAddress
}

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' -LoggingId $loggingId -AdminAddress $adminAddress
$provisionedVMs | ForEach-Object {New-BrokerMachine -CatalogUid $catalog.UID -HostedMachineId $_.VMId `
   -HypervisorConnectionUid $brokerHypConnection.UID -MachineName $_.ADAccountSid -LoggingId $loggingId `
   -AdminAddress $adminAddress}

Stop-LogHighLevelOperation -IsSuccessful $true -HighLevelOperationId $loggingId -AdminAddress $adminAddress
<!--NeedCopy-->

Configure support for non-domain joined catalogs

Using the Citrix Virtual Apps and Desktops service, you can create catalogs based on workgroup, or, non-domain joined machines. Creating non-domain joined machines depends on how the account identity pool is created. The account identity pool is the mechanism used by MCS to create and track machine names during catalog provisioning.

For example, in past releases all Active Directory fields were supplied in a single instance:

New-AcctIdentityPool AllowUnicode -Domain "awsdevexample.local" -IdentityPoolName "DedicatedHostCatalog" -NamingScheme "MH-DHost##" `
   -NamingSchemeType "Numeric" *-OU "CN=Computers,DC= awsdevexample,DC=local"* -Scope @() `
   -ZoneUid "81291221-d2f2-49d2-ab12-bae5bbd0df05"
<!--NeedCopy-->

MCS uses a new PoSH parameter, WorkgroupMachine, to create a workgroup catalog. Using the same example, noted above, this parameter removes the requirement to specify all the AD-specific parameters, including domain administrator credentials:

New-AcctIdentityPool AllowUnicode -WorkgroupMachine -IdentityPoolName "DedicatedHostCatalog" -NamingScheme "MH-DHOst##" `
   -NamingSchemeType "Numeric" -Scope @() -ZoneUid "81291221-d2f2-49d2-ab12-bae5bbd0df05"
<!--NeedCopy-->

Note:

When using the WorkgroupMachine parameter, consider that non-domain joined machine catalogs are only supported through Powershell for all catalog lifecycle events including provisioning, adding/removing machines from the catalog, updating, and power management.

Creating a Catalog