Two days ago I provided a PowerShell script that could be used to generate an import CSV file for Provisioning Services (PVS). However, after importing the devices, a vDisk still needed to be assigned (if you didn’t configure a template) and more importantly, an account still needed to be created in Active Directory for each device. These extra steps increased the complexity of the import process.

I got to thinking that the MCLI command-line interface could be used to do all three tasks, but the PowerShell script would have to generate all the MCLI commands necessary for each desktop import. I had a bit of spare time, so I sat down and modified the earlier import script into a new PowerShell script that outputs a file of MCLI commands for the desktop import. As with the prior script, this one should be run from the SCVMM server that manages the XenDesktops that you are importing into Provisioning Server.

This new PowerShell script creates a CMD file that contains three MCLI commands for each desktop. The commands are as follows:

1. Adds the device to the configured device collection
2. Assigns the vDisk specified on the command-line to the device
3. Adds the device to the domain

After the PowerShell script has successfully completed, the only step left is to take and run it from the %ProgramFiles%\Citrix\Provisioning Server folder on one of the provisioning servers. The MCLI commands should then execute sequentially the three commands listed above for each desktop.

GenPVSMCLI Syntax

Usage: GenPVSMCLI.ps1 SiteName CollectionName Description StoreName vDiskName CmdFileName VMMatchCriteria [OUName]

Where:
SiteName=Provisioning Services Site where the device collection resides
CollectionName=Provisioning Services Collection where the devices will be created
Description=Generic description added to the devices. All devices will have the same description
StoreName=Provisioning Services store name where the vDisk resides
vDiskName=Provisioning Services vDisk name to assign to all the devices imported
CmdFileName=File name that will contain the MCLI commands when the script completes
VMMatchCriteria=Name match criteria for which guests to include in the file
OUName=Name of the Active Directory OU where the computer accounts should be created. The default is Computers if none is specified.

Example: .\GenPVSMCLI.ps1 “Dell” “XenDesktops” “XD Desktop” “Local Disk” “Win7Base” “c:\PVSMCLI.cmd” HVDesktop “XenDesktop/VMs”

The example above will create a file called PVSMCLI.cmd that will have all the MCLI commands for hosts matching the name HVDesktop (HVDesktop1, 23HVDesktop, etc.) in it to add devices to the XenDesktops device collection in the Dell site. The vdisk Win7Base found in the store called “Local Disk” will be assigned to all the devices imported and the computer accounts will be created in the VMs OU found in the XenDesktop parent OU.

GenPVSMCLI PowerShell Script
# Purpose:    Create a MCLI cmd file to add devices to Provisioning Services, assign a vDisk
#             and add the device to Active Directory. The cmd file can then be executed from
#             any Provisioning Services server.
# Date:       1 July 2010
# Author:     Paul Wilson
# Notes:      The file may need to be opened and saved in ANSI format on some computers.
#             This script automatically appends information to any existing file in <span class="code-keyword">case</span> you
#             need to run the command multiple times with different match criteria

########################################################################################################################
# Special     Organizational Unit to add the Device(s) to is an optional parameter.                                    #
# Use         If it is not specifed, the device is added to the builtin Computers container                            #
# Notes       Child OU's should be delimited with forward slashes, e.g. <span class="code-quote">"ParentOU/ChildOU"</span>.                            #
#             Special characters in an OU name, such as '"', '#', '+', ',', ';', '&gt;', '=', must be                     #
#             escaped with a backslash. For example, an OU called <span class="code-quote">"commaIn,TheMiddle"</span> must be specified                #
#             as <span class="code-quote">"commaIn\,TheMiddle"</span>. The old syntax of delimiting child OU's with a comma is still                   #
#             supported, but deprecated. When delimiting with commas,the child OU comes first, e.g. <span class="code-quote">"ChildOU,ParentOU"</span>.#
########################################################################################################################

# Parse the command-line and verify the 7 required parameters are present
<span class="code-keyword">if</span> ($args -eq $<span class="code-keyword">null</span> -or $args.Count -lt 7)
{
    write-output <span class="code-quote">"Usage: GenPVSMCLI.ps1 SiteName CollectionName Description StoreName vDiskName CmdFileName VMNameMatches [OUName]"</span>
    write-output <span class="code-quote">"Example: .\GenPVSMCLI.ps1 "</span><span class="code-quote">"Site"</span><span class="code-quote">" "</span><span class="code-quote">"Collection"</span><span class="code-quote">" "</span><span class="code-quote">"XD Desktop"</span><span class="code-quote">" "</span><span class="code-quote">"Local Disk"</span><span class="code-quote">" "</span><span class="code-quote">"Win7Base"</span><span class="code-quote">" "</span><span class="code-quote">"c:\PVSMCLI.cmd"</span><span class="code-quote">" HVDesktop "</span><span class="code-quote">"ParentOU/ChildOU"</span><span class="code-quote">" "</span>
    exit 1
}

# Place the command-line parameters into named variables <span class="code-keyword">for</span> later use.
$SName=$args[0]
$CName=$args[1]
$DString=$args[2]
$StName=$args[3]
$VName=$args[4]
$CmdFName=$args[5]
$VMNameMatches=$args[6]
$OUName=$args[7]

# Connect to the local server as the SCVMM host to process the PowerShell commands
$VMMServer = Get-VMMServer -Computername <span class="code-quote">"localhost"</span>

# Retrieve a list of all the VMs that match the name matching criteria supplied and sort them by name
$AllVMs = Get-VM | where { $_.Name -match <span class="code-quote">"$VMNameMatches"</span> } | sort Name

# For each VM found the loop below does the following:
# 1. Locates the MAC address of the primary network adapter
# 2. Appends to the cmd file supplied the MCLI command-line to add the device with the appropriate information.
# 3. Appends to the cmd file supplied the MCLI command-line to assign the provided vdisk to the device.
# 4. Appends to the cmd file supplied the MCLI command-line to create the computer account in the domain.

foreach ($vm in $AllVms)
{
    $nicDetails = Get-VirtualNetworkAdapter -VM $vm
    $MacAddr = $nicDetails[0].PhysicalAddress
    $AddString=<span class="code-quote">"MCLI Add Device -r deviceName={0} collectionName="</span><span class="code-quote">"{1}"</span><span class="code-quote">" siteName="</span><span class="code-quote">"{2}"</span><span class="code-quote">" description="</span><span class="code-quote">"{3}"</span><span class="code-quote">" deviceMac={4} bootFrom=1"</span> -f $vm.Name, $CName, $SName, $DString, $MacAddr
    $AssignString=<span class="code-quote">"MCLI Run AssignDiskLocator -p deviceMac={0} diskLocatorName="</span><span class="code-quote">"{1}"</span><span class="code-quote">" siteName="</span><span class="code-quote">"{2}"</span><span class="code-quote">" storeName="</span><span class="code-quote">"{3}"</span><span class="code-quote">" "</span> -f $MacAddr, $VName, $SName, $StName
    <span class="code-keyword">if</span> ($OUName -eq $<span class="code-keyword">null</span>)
    {
        $DomainString=<span class="code-quote">"MCLI Run AddDeviceToDomain -p deviceMac={0}"</span> -f $MacAddr
    }
    <span class="code-keyword">else</span>
    {
        $DomainString=<span class="code-quote">"MCLI Run AddDeviceToDomain -p deviceMac={0} organizationUnit="</span><span class="code-quote">"{1}"</span><span class="code-quote">" "</span> -f $MacAddr, $OUName
    }
    write $AddString | out-File $CmdFName -Append
    write $AssignString | out-File $CmdFName -Append
    write $DomainString | out-File $CmdFName -Append
}

Now, I have noticed that when adding hundreds of devices at a time that the domain controller gets busy and the AddDeviceToDomain command sometimes fails. I suggest for large deployments that you pipe the output to a text file and then later review it for possible failed commands. Re-running the failed commands should be sufficient since they are mostly independent of one another. The only exception being the Add Device command must succeed or the other two will fail.

If you found this information useful and would like to be notified of future blog posts, please follow me on Twitter @pwilson98 or visit my XenDesktop on Microsoft website.