Machine Creation Services (MCS) is a new feature in XenDesktop 5 that is used to simplify XenDesktop deployments in POCs and Pilots in hypervisor-based environments. It generates virtual desktop VMs on the fly based on a “master” virtual machine image. It also manages the Active Directory accounts for these machines using an Identity Pool and performs other tasks within XenDesktop as well. This feature is leveraged when using the Quick Deploy setup wizard or when creating pooled (or dedicated) catalogs within the Desktop Studio console. The wizard for creating pooled catalogs manually within Desktop Studio is shown below. The purpose of this blog is to show how to automate the creation of pooled catalogs that leverage MCS using a PowerShell script.

This is the third blog in a series on how to use the XenDesktop 5 PowerShell SDK. In the first blog, I provided info on how to get started with the SDK, some approaches you can take for learning the cmdlets, some reference web pages that you can bookmark, and the tools you can use for creating your own scripts. If you haven’t read that article yet, please visit that blog first. For a complete list of topics that I will be covering in this series, see the bottom of this article.

Why automate Machine Creation Services?
I mentioned above that Machine Creation Services does a lot of automation already for us. If you run through the wizard in the Desktop Studio console, it’ll automatically generate a specified number of virtual desktops based on a master image and manage the Active Directory accounts for them. So why automate this process even further? Why automate the creation of the pooled catalog itself?

In our particular use case, we wanted to automate the configuration of XenDesktop 5 training environments hosted in the SoftLayer cloud. SoftLayer is one of the popular cloud-providers out there and we use SoftLayer for hosting the Virtual Computing Demo Center system. When working with the cloud-based hypervisors (SoftLayer calls them their “Bare Metal Cloud” servers), we are given XenServers that all have unique hostnames, IP addresses, and credentials. XenDesktop has a direct tie-in to the hypervisor through the configuration of hypervisor connections and hosts. Since the configuration of the hypervisor connections and hosts will be different on each hypervisor, we couldn’t “templatize” our DDC VM with the XenDesktop configuration already in place. Instead, we templatized an “empty” DDC that just needed to be configured afterwards through scripts. Since in the training world we were dealing with classes in the range of 100+ servers, the creation of the hypervisor connections/hosts/catalogs all needed to be automated in an unattended fashion when the DDC template was imported and brought online. Long story short – we needed to automate the creation of catalogs on the first DDC VM startup to get the environment ready for training class use.

PowerShell Script Goals
The PowerShell script sample below creates a pooled catalog called “Windows 7 Catalog” that leverages Machine Creation Services. To help you understand the script better, the details of this catalog are specified below. You will see these same values specified in the Global Variables section of the script. The hypervisor used within my environment is XenServer 5.6.

Configuration Value
Catalog Name Windows 7 Catalog
Catalog Description Windows 7 MCS Catalog
Device Naming Scheme Win7-#
Device Naming Scheme Type Numeric
Active Directory OU OU=Win7 VDA,DC=xendesktop,DC=lab
Active Directory Domain xendesktop.lab
Master VM used to create the devices Win7Master
Number of virtual desktops to create 3 virtual desktops
Starting index for the virtual desktops 1
Number of virtual cpus per device 2 vcpus
RAM per device 1024MB (1GB)
DDC Address ddc1.xendesktop.lab:80
XenServer host used by the catalog xsdev001 (see description below)

The last configuration mentioned in the table above is the XenServer host used by the catalog. In order to create a pooled catalog that leverages Machine Creation Services, the catalog needs to know the XenServer hosting unit to leverage for creating new virtual desktop machines. To find this name, go to the Hosts node of the Desktop Studio console as shown in the picture below. Feel free to reference my previous blog on how to automate the creation of the hypervisor connection and host configuration.

PowerShell Script for Creating Pooled Catalogs that leverage Machine Creation Services
The sample script below demonstrates how to create a pooled catalog that leverages Machine Creation Services.

#*****************************************************************************
#Write debug message to signify start of script
#*****************************************************************************
$objDateTime = Get-Date
Write-Host ""
Write-Host <span class="code-quote">"*****Script started at"</span> $objDateTime <span class="code-quote">"*****"</span>

#*****************************************************************************
#Add Citrix snapins to PowerShell session <span class="code-keyword">if</span> not already added
#*****************************************************************************
$snapins = Get-PSSnapin | where { $_.Name -like <span class="code-quote">"Citrix*"</span> }
<span class="code-keyword">if</span> (($snapins -eq $<span class="code-keyword">null</span>) -or ($snapins.Count -ne 8))
{
    Write-Host <span class="code-quote">"Loading the XenDesktop cmdlets into <span class="code-keyword">this</span> session..."</span>
    Get-PSSnapin -Registered <span class="code-quote">"Citrix*"</span> | Add-PSSnapin
    Add-PSSnapin <span class="code-quote">"PvsPsSnapin"</span>
}
<span class="code-keyword">else</span>
{
    Write-Host <span class="code-quote">"XenDesktop cmdlets are already loaded into <span class="code-keyword">this</span> session..."</span>
}

#*****************************************************************************
#Global variables <span class="code-keyword">for</span> entire script
#*****************************************************************************
$strCatalogName = <span class="code-quote">"Windows 7 Catalog"</span>               #Name of the catalog that will be created
$strCatalogDescription = <span class="code-quote">"Windows 7 MCS Catalog"</span>    #Description given to the catalog
$strDeviceNamingScheme = <span class="code-quote">"Win7-#"</span>                   #Naming scheme <span class="code-keyword">for</span> the MCS-created VMs
$strDeviceNamingSchemeType = <span class="code-quote">"Numeric"</span>              #Naming scheme type <span class="code-keyword">for</span> the MCS-created VMs
$strDeviceOU = <span class="code-quote">"OU=Win7 VDA,DC=xendesktop,DC=lab"</span>   #Active Directory OU where the MCS-created VMs will be placed
$strDeviceDomain = <span class="code-quote">"xendesktop.lab"</span>                 #Active Directory Domain where the MCS-created VMs will be managed
$strMasterVMName = <span class="code-quote">"Win7Master"</span>                     #Name of the <span class="code-quote">"Master VM"</span> that will be used with MCS
$intNumDeviceAccounts = 3                           #Create 3 VMs with MCS using the naming scheme defined above
$intDeviceStartIndex = 1                            #VM names should start with an index of 1
$intVMCpuCount = 2                                  #Two vcpu's <span class="code-keyword">for</span> each MCS-created VM
$intVMMemoryMB = 1024                               #1GB ram <span class="code-keyword">for</span> each MCS-created VM
$strHostUnitName = <span class="code-quote">"xsdev001"</span>                       #XenServer <span class="code-quote">"host"</span> as listed in the Hosts node of the Desktop Studio Console
$strDDCAddress = <span class="code-quote">"ddc1.xendesktop.lab:80"</span>           #DDC the cmdlets will connect to <span class="code-keyword">for</span> performing operations on the XenDesktop site

#*****************************************************************************
#Create <span class="code-quote">"Windows 7"</span> catalog that uses Machine Creation Services (MCS)
#*****************************************************************************

#Create the catalog container
$objCatalog = New-BrokerCatalog -AllocationType 'Random' -CatalogKind 'SingleImage' -Name $strCatalogName -Description $strCatalogDescription -PvsForVM @() -AdminAddress $strDDCAddress
<span class="code-keyword">if</span> ($objCatalog -ne $<span class="code-keyword">null</span>)
{
    #Write debug message
    Write-Host <span class="code-quote">"Successfully created a catalog container called"</span> $objCatalog.Name <span class="code-quote">"..."</span>
}
<span class="code-keyword">else</span>
{
    #Write debug message
    Write-Host <span class="code-quote">"ERROR: Could not create a catalog container called"</span> $strCatalogName <span class="code-quote">"..."</span>
}

#Check <span class="code-keyword">if</span> the identity pool already exists. If not, create <span class="code-keyword">new</span> one.
$objIdentityPool = Get-AcctIdentityPool -IdentityPoolName $strCatalogName -AdminAddress $strDDCAddress
<span class="code-keyword">if</span> ($objIdentityPool -eq $<span class="code-keyword">null</span>)
{
    #Create an identity pool to store the AD computer account info <span class="code-keyword">for</span> <span class="code-keyword">this</span> catalog.
    #We are giving the identity pool the same name as the catalog to keep better track of it.
    $objIdentityPool = New-AcctIdentityPool -IdentityPoolName $strCatalogName -NamingScheme $strDeviceNamingScheme -NamingSchemeType $strDeviceNamingSchemeType -OU $strDeviceOU -Domain $strDeviceDomain -AllowUnicode -AdminAddress $strDDCAddress
    <span class="code-keyword">if</span> ($objIdentityPool -ne $<span class="code-keyword">null</span>)
    {
  #Write debug message
       Write-Host <span class="code-quote">"Successfully created an Identity Pool called"</span> $objIdentityPool.IdentityPoolName <span class="code-quote">"..."</span>
    }
    <span class="code-keyword">else</span>
    {
  #Write debug message
       Write-Host <span class="code-quote">"ERROR: Could not create an Identity Pool called"</span> $strCatalogName <span class="code-quote">"..."</span>
    }
}
<span class="code-keyword">else</span>
{
    #Write debug message
    Write-Host <span class="code-quote">"Identity Pool called"</span> $objIdentityPool.IdentityPoolName <span class="code-quote">"already exists. Proceeding with script..."</span>
}

#Get reference to the broker service group
$objBrokerServiceGroup = Get-ConfigServiceGroup -ServiceType <span class="code-quote">"Broker"</span> -AdminAddress $strDDCAddress
<span class="code-keyword">if</span> ($objBrokerServiceGroup -ne $<span class="code-keyword">null</span>)
{
    #Write debug message
    Write-Host <span class="code-quote">"Successfully retrieved reference to the broker service group..."</span>
}
<span class="code-keyword">else</span>
{
    #Write debug message
    Write-Host <span class="code-quote">"ERROR: Could not retrieve reference to the broker service group..."</span>
}

#Add metadata to the broker service group to tie the catalog with the identity pool it is using
$strMetadataPropertyName = <span class="code-quote">"Citrix_DesktopStudio_BrokerCatalogIdentityPoolReferencePrefix_"</span> + $objCatalog.Uid
$strMetadataPropertyValue = $objIdentityPool.IdentityPoolUid
$objNewMetadata = Add-ConfigServiceGroupMetadata -ServiceGroupUid $objBrokerServiceGroup.ServiceGroupUid -Property $strMetadataPropertyName -Value $strMetadataPropertyValue -AdminAddress $strDDCAddress
<span class="code-keyword">if</span> ($objNewMetadata -ne $<span class="code-keyword">null</span>)
{
    #Write debug message
    Write-Host <span class="code-quote">"Successfully added <span class="code-keyword">new</span> metadata to the broker service group..."</span>
}
<span class="code-keyword">else</span>
{
    #Write debug message
    Write-Host <span class="code-quote">"ERROR:  Could not add <span class="code-keyword">new</span> metadata to the broker service group..."</span>
}

#Get reference to the hosting unit and hypervisor connection it uses
$strHostUnitPath = 'xdhyp:\hostingunits\' + $strHostUnitName
$objHostUnit = Get-Item -Path $strHostUnitPath
<span class="code-keyword">if</span> ($objHostUnit -ne $<span class="code-keyword">null</span>)
{
    #Write debug message
    Write-Host <span class="code-quote">"Successfully retrieved reference to the"</span> $strHostUnitName <span class="code-quote">"hosting unit..."</span>
}
<span class="code-keyword">else</span>
{
    #Write debug message
    Write-Host <span class="code-quote">"ERROR: Could not retrieve reference to the"</span> $strHostUnitName <span class="code-quote">"hosting unit..."</span>
}
$objHypConnection = Get-BrokerHypervisorConnection -Name $objHostUnit.HypervisorConnection.HypervisorConnectionName -AdminAddress $strDDCAddress
<span class="code-keyword">if</span> ($objHypConnection -ne $<span class="code-keyword">null</span>)
{
    #Write debug message
    Write-Host <span class="code-quote">"Successfully retrieved reference to the"</span> $objHypConnection.Name <span class="code-quote">"hypervisor connection..."</span>
}
<span class="code-keyword">else</span>
{
    #Write debug message
    Write-Host <span class="code-quote">"ERROR: Could not retrieve reference to the hypervisor connection..."</span>
}

#Create a snapshot of the master VM image
$strSnapshotName = <span class="code-quote">"Citrix_XD_"</span> + $strCatalogName
$strMasterVMPath = <span class="code-quote">"XDHyp:\hostingunits\"</span> + $objHostUnit.HostingUnitName + <span class="code-quote">"\"</span> + $strMasterVMName + <span class="code-quote">".vm"</span>
$strSnapshot = New-HypVMSnapshot -SnapshotName $strSnapshotName -LiteralPath $strMasterVMPath -AdminAddress $strDDCAddress
<span class="code-keyword">if</span> (($strSnapshot -ne $<span class="code-keyword">null</span>) -and ($strSnapshot -ne ""))
{
    #Write debug message
    Write-Host <span class="code-quote">"Successfully created snapshot <span class="code-keyword">for</span> the master VM image called"</span> $strSnapshotName
}
<span class="code-keyword">else</span>
{
    #Write debug message
    Write-Host <span class="code-quote">"ERROR: Could not create snapshot <span class="code-keyword">for</span> the master VM image..."</span>
}

#Create a provisioning scheme that defines the details of the MCS cloned VMs.
#This process creates a copy of the snapshot and can take several minutes to complete.
#Note: New-ProvScheme returns a GUID that represents the TaskID <span class="code-keyword">for</span> the copy task.
$strProvSchemeName = $strCatalogName + <span class="code-quote">"_"</span> + $objHostUnit.HostingUnitName
$strSnapshotPath = $strMasterVMPath + <span class="code-quote">"\"</span> + $strSnapshotName + <span class="code-quote">".snapshot"</span>
$objProvSchemeTask = New-ProvScheme -ProvisioningSchemeName $strProvSchemeName -HostingUnitName $objHostUnit.HostingUnitName -IdentityPoolName $objIdentityPool.IdentityPoolName -VMCpuCount $intVMCpuCount -VMMemoryMB $intVMMemoryMB -CleanOnBoot -MasterImageVM $strSnapshotPath -RunAsynchronously -AdminAddress $strDDCAddress
<span class="code-keyword">if</span> ($objProvSchemeTask -ne $<span class="code-keyword">null</span>)
{
    #Write debug message
    Write-Host <span class="code-quote">"Successfully started task to copy the snapshot of the Master VM image. This process could take several minutes to complete..."</span>
}
<span class="code-keyword">else</span>
{
    #Write debug message
    Write-Host <span class="code-quote">"ERROR: Could not start the task to copy the snapshot of the Master VM image."</span>
}

#Wait <span class="code-keyword">for</span> the copy process to complete
$blnSnapshotCopyComplete = $<span class="code-keyword">false</span>
<span class="code-keyword">while</span> ($blnSnapshotCopyComplete -eq $<span class="code-keyword">false</span>)
{
    #Get latest provisioning task details
    $objProvTask = Get-ProvTask -TaskId $objProvSchemeTask.Guid -AdminAddress $strDDCAddress

    #Check task status
    <span class="code-keyword">if</span> (($objProvTask.TaskState -eq <span class="code-quote">"Finished"</span>) -or ($objProvTask.TaskState -eq <span class="code-quote">"ProvisioningSchemeAlreadyExists"</span>))
    {
        #Task is done - <span class="code-keyword">break</span> out of loop
  $blnSnapshotCopyComplete = $<span class="code-keyword">true</span>

  #Write debug message
  Write-Host <span class="code-quote">"Copy snapshot process is complete.  Proceeding with script..."</span>
    }
    <span class="code-keyword">else</span>
    {
  #Write debug message to indicate progress
  Write-Host <span class="code-quote">"Copy snapshot process is at"</span> $objProvTask.TaskProgress.ToString() <span class="code-quote">"%.  Checking again in 20 seconds..."</span>

  #Sleep 20 seconds and check again
  Start-Sleep -Seconds 20
    }
}

#Update the catalog with what provisioning scheme and hosting unit it is using
$objProvScheme = Get-ProvScheme -ProvisioningSchemeName $strProvSchemeName -AdminAddress $strDDCAddress
$strPvsForVM = $objProvScheme.ProvisioningSchemeUid.ToString() + <span class="code-quote">":"</span> + $objProvScheme.HostingUnitUid.ToString()
Set-BrokerCatalog -Name $strCatalogName -PvsForVM $strPvsForVM -AdminAddress $strDDCAddress

#Create machine accounts in Active Directory from info stored in the identity pool.
#After running <span class="code-keyword">this</span> command, the machine accounts should be visible within the Active Directory <span class="code-quote">"Users and Computers"</span> snap-in.
$objAccounts = New-AcctADAccount -IdentityPoolName $objIdentityPool.IdentityPoolName -Count $intNumDeviceAccounts -StartCount $intDeviceStartIndex -AdminAddress $strDDCAddress
<span class="code-keyword">if</span> ($objAccounts -ne $<span class="code-keyword">null</span>)
{
    #Write debug message
    Write-Host <span class="code-quote">"Successfully created"</span> $objAccounts.SuccessfulAccountsCount <span class="code-quote">"machine accounts within Active Directory based on info stored within the"</span> $objIdentityPool.IdentityPoolName <span class="code-quote">"identity pool."</span>
}
<span class="code-keyword">else</span>
{
    #Write debug message
    Write-Host <span class="code-quote">"ERROR: Could not create machine accounts within Active Directory from info stored within the identity pool..."</span>
}

#Associate controllers (DDC Servers) to the provisioning scheme.
#If there is one DDC,   set $arrDDCAddresses = $strDDCAddress
#If there are two DDCs, set $arrDDCAddresses = $strDDC1Address, $strDDC2Address
#Note: Add-ProvSchemeControllerAddress returns an updated provisioning scheme object with the latest ControllerAddress data.
$arrDDCAddresses = $strDDCAddress
$objProvScheme = Add-ProvSchemeControllerAddress -ProvisioningSchemeName $objProvScheme.ProvisioningSchemeName -ControllerAddress $arrDDCAddresses -AdminAddress $strDDCAddress

#Create virtual desktop VMs from the info specified by the provisioning scheme.  After executing <span class="code-keyword">this</span> command, the VMs will show up in the hypervisor.
#Note: New-ProvVM returns a GUID that represents the TaskID <span class="code-keyword">for</span> the create VM task.
$objProvVMTask = New-ProvVM -ProvisioningSchemeName $objProvScheme.ProvisioningSchemeName -ADAccountName $objAccounts.SuccessfulAccounts -RunAsynchronously -AdminAddress $strDDCAddress
<span class="code-keyword">if</span> ($objProvVMTask -ne $<span class="code-keyword">null</span>)
{
    #Write debug message
    Write-Host <span class="code-quote">"Successfully started task to create the VMs within the hypervisor.  Checking task status..."</span>
}
<span class="code-keyword">else</span>
{
    #Write debug message
    Write-Host <span class="code-quote">"ERROR: Could not start the task to create the VMs within the hypervisor..."</span>
}

#Wait <span class="code-keyword">for</span> the creation process to complete
$blnCreateVMComplete = $<span class="code-keyword">false</span>
<span class="code-keyword">while</span> ($blnCreateVMComplete -eq $<span class="code-keyword">false</span>)
{
    #Get latest provisioning task details
    $objProvTask = Get-ProvTask -TaskId $objProvVMTask.Guid -AdminAddress $strDDCAddress

    #Check task status
    <span class="code-keyword">if</span> (($objProvTask.TaskState -eq <span class="code-quote">"Finished"</span>) -or ($objProvTask.TaskState -eq <span class="code-quote">"FinishedWithErrors"</span>))
    {
  #Task is done - <span class="code-keyword">break</span> out of loop
  $blnCreateVMComplete = $<span class="code-keyword">true</span>

  #Write debug message
  Write-Host <span class="code-quote">"Create VM process is complete. Proceeding with script..."</span>
    }
    <span class="code-keyword">else</span>
    {
  #Write debug message to indicate progress
  Write-Host <span class="code-quote">"Create VM process not completed yet. Current TaskState is"</span> $objProvTask.TaskState<span class="code-quote">". Checking again in 5 seconds..."</span>

  #Sleep 5 seconds and check again
  Start-Sleep -Seconds 5
    }
}

#Loop though the newly provisioned VMs, <span class="code-quote">"lock"</span> them, and add them to the catalog
$objProvVMs = Get-ProvVM -ProvisioningSchemeName $objProvScheme.ProvisioningSchemeName -AdminAddress $strDDCAddress
<span class="code-keyword">if</span> ($objProvVMs -ne $<span class="code-keyword">null</span>)
{
    foreach ($ProvVM in $objProvVMs)
    {
      #Write debug message
   Write-Host <span class="code-quote">"Locking"</span> $ProvVM.ADAccountName <span class="code-quote">"within the provisioning scheme with a tag called 'Brokered'..."</span>

  #Lock VM in the provisioning scheme with a tag called <span class="code-quote">"Brokered"</span> (so it cannot be removed without being unlocked first)
    $objLockVM = Lock-ProvVM -ProvisioningSchemeName $objProvScheme.ProvisioningSchemeName -VMID $ProvVM.VMId -Tag <span class="code-quote">"Brokered"</span> -AdminAddress $strDDCAddress

  #Add VM to the catalog created above
     $objBrokerMachine = New-BrokerMachine -CatalogUid $objCatalog.Uid -HostedMachineId $ProvVM.VMId -HypervisorConnectionUid $objHypConnection.Uid -MachineName $ProvVM.ADAccountSid -AdminAddress $strDDCAddress
  <span class="code-keyword">if</span> ($objBrokerMachine -ne $<span class="code-keyword">null</span>)
  {
      #Write debug message
       Write-Host <span class="code-quote">"Successfully added"</span> $ProvVM.ADAccountName <span class="code-quote">"to the"</span> $objCatalog.Name <span class="code-quote">"catalog..."</span>
  }
  <span class="code-keyword">else</span>
  {
         #Write debug message
       Write-Host <span class="code-quote">"ERROR: Could not add"</span> $ProvVM.ADAccountName <span class="code-quote">"to the"</span> $objCatalog.Name <span class="code-quote">"catalog..."</span>
  }
    }
}

#*****************************************************************************
#Write debug message to signify end of script
#*****************************************************************************
$objDateTime = Get-Date
Write-Host <span class="code-quote">"*****Script completed at"</span> $objDateTime <span class="code-quote">"*****"</span>
Write-Host ""

Note: The sample script above performs some basic error handling to help you keep track of the automation status. It outputs the success or failure of key sections of the script to the PowerShell window. The expected output is shown below. If you run into any issues, you can use a PowerShell editor such as PowerGui.exe to step through each line of the script.

Verifying the script executed successfully
Machine Creation Services performs a lot of actions behind the scenes, so I usually check the following key areas to ensure the script performed its duty.

Location Item Description
Desktop Studio Console Windows 7 Catalog Open Desktop Studio on the DDC machine and verify that the Windows 7 catalog is present (see screen shot below)
Active Directory Win7 Machine Accounts Open the Active Directory “Users and Computers” snap-in and verify the machine accounts are present in the specified OU
XenCenter Win7 VMs Verify all of the created Windows 7 VMs appear in XenCenter. Also go to the “Storage” tab for these VMs and verify that two virtual disks are present (a diff disk and identity disk).
XenCenter Win7Master VM A snapshot is taken of the Master VM as part of the MCS process. Go to the “Snapshots” tab of the Win7Master VM and verify that a snapshot is present.

Analyzing the PowerShell Script
The Global Variables section defines the configuration information for the catalog that I wanted to create. This is the information that you would have normally configured if you were to run through the wizard in Desktop Studio. You’ll want to change the values here to reference your own environment.

The remainder of the script leverages quite a few cmdlets from the XenDesktop 5 SDK. The table below provides a summary of them:

Cmdlet Name Description
New-BrokerCatalog Creates a new catalog container within XenDesktop. I call this just a “container” since you still need to perform more actions on it for it to be valid, such as defining the virtual desktops that it contains.
Get-AcctIdentityPool Gets a reference to an existing identity pool. I use this cmdlet in the script to check if the Identity Pool already exists before trying to create a new one.
New-AcctIdentityPool Creates a new identity pool with a specified name. An identity pool stores the device naming scheme and OU details for the devices that you want to create with MCS.
Get-ConfigServiceGroup Gets a reference to the specified service group. I’m using this cmdlet to get a reference to the “broker” service group.
Add-ConfigServiceGroupMetadata Adds metadata to a specified service group. I’m using this cmdlet to add metadata to the broker service group (to tie the catalog with the identity pool it is using). Without this call, the “Manage AD Accounts” menu option for a catalog won’t work properly.
Get-BrokerHypervisorConnection Gets a reference to a hypervisor connection with the specified conneciton name.
New-HypVMShapshot Creates a snapshot of the Master VM. This particular call just takes a few seconds to complete.
New-ProvScheme Creates a provisioning scheme for kicking off the copy process of the Master VM. The output of this cmdlet is the GUID for the task that it started. This task will take several minutes to complete and the time will depend on the disk size of the Master VM. Before proceeding further with the script, you want to ensure that this task is fully complete. I have a loop set up in the script to ensure it completes before proceeding with the next step.
Get-ProvTask Gets the status of a task using the specified Task ID (GUID). This cmdlet is used in the loop for checking the status of the Master VM copy process (and the creation of the Win7 VMs).
Get-ProvScheme Gets a reference to an existing provisioning scheme.
Set-BrokerCatalog Used to update the properties of a catalog. The script calls this cmdlet to update the catalog with what provisioning scheme and hosting unit it is using (via the PvsForVM property).
New-AcctADAccount Creates machine accounts in Active Directory using info stored in the specified identity pool.
Add-ProvSchemeControllerAddress Associates controllers (DDC servers) to the specified provisioning scheme. These are the controllers that can leverage the provisioning scheme.
New-ProvVM Creates virtual desktop VMs from the info specified in the supplied provisioning scheme. The output of this cmdlet is the GUID for the task that it started. This task will usually take a minute or less but will depend on the number of VMs that is has to create. Before proceeding further with the script, you want to ensure that this task is fully complete. I have a loop set up in the script to ensure it completes before proceeding with the next step.
Get-ProvVM Gets a reference to the VMs that were provisioned by a specified provisioning scheme.
Lock-ProvVM Adds a “lock” to a VM that was created from a provisioning scheme to provide some safeguards to it. The lock is really just a tag name that indicates it is locked in the provisioning scheme. Use Unlock-ProvVM to remove the lock if needed.
New-BrokerMachine Adds a provisioned VM to the specified catalog.

Blogs in this series
I have several blogs planned for this series based on the recent projects that I’ve had. In the next blog I’ll discuss how to automate a different type of catalog – the physical catalog. Stay tuned!

Ed York – Senior Architect – Worldwide Technical Readiness
Ask-the-Architect Site: http://community.citrix.com/p/product-automation#home
Follow Me on twitter: http://twitter.com/citrixedy