Sorting the contents of an MDT 2010 deployment share

Quite some time ago (well over a year ago – time flies) I posted a script at https://blogs.technet.com/mniehaus/archive/2008/06/20/sorting-mdt-s-lists-of-applications-task-sequences-patches-etc.aspx that described how to sort the content in an MDT 2008 distribution share.  Now that MDT 2010 has been released, it’s important to first point out that this script will not work with MDT 2010 for a variety of reasons.

Since MDT 2010 has the same behavior, showing the items in the order that they were added in Workbench, that means you are now in need of a new script.  Fortunately, this isn’t too hard to do using a PowerShell script (and a little bit of knowledge of the underlying workings of the MDT 2010 PowerShell provider).  Here’s the script (also attached in a Zip file for those interested in a simpler download):

# ***************************************************************************
#
# File:      DeploymentShareSorter.ps1
#
# Author:    Michael Niehaus
#
# Purpose:   This PowerShell script will sort the existing files and folders
#            in a deployment share.
#
#            Note that there should be no one actively adding items to the
#            deployment share while running this script, as some of the
#            operations performed could cause these items to be lost.
#
#            This requires PowerShell 2.0 CTP3 or later.
#
# Usage:     Copy this file to an appropriate location.  Edit the file to
#            change the $rootPath variable below, pointing to your
#            deployment share. (This can be a local path or a UNC path.)
#
# ------------- DISCLAIMER --------------------------------------------------
# This script code is provided as is with no guarantee or warranty concerning
# the usability or impact on systems and may be used, distributed, and
# modified in any way provided the parties agree and acknowledge the
# Microsoft or Microsoft Partners have neither accountability or
# responsibility for results produced by use of this script.
#
# Microsoft will not provide any support through any means.
# ------------- DISCLAIMER --------------------------------------------------
#
# ***************************************************************************

# Constants

$rootPath = "\\mniehaus-t61p-7\DeploymentShare$"

# Conect to the deployment share

Add-PSSnapIn Microsoft.BDD.PSSnapIn -ErrorAction SilentlyContinue
New-PSDrive -Name DeploymentPointSorter -PSProvider MDTProvider -Root "$rootPath"

# Functions

function Sort-MDTFolderItems {

    [CmdletBinding()]
    PARAM
    (
        [Parameter(Position=1, ValueFromPipeline=$true)] $folder
    )
    Process
    {
        Write-Host "Sort-MDTFolderItems: Processing folder $($folder.Name)"
        # Initialize the sorted array
        $sorted = @()
        # Get the list of immediate subfolders, sort by name, and add to the array
        $folderPath = $folder.PSPath.Substring($folder.PSPath.IndexOf("::")+2)
        Get-ChildItem $folderPath | ? {-not $_.PSIsContainer} | Sort Name | % { $sorted += $_.Guid }
        # If there were any items found, process them.
        if ($sorted.Count -gt 0)
        {
            # See if the list is already sorted.  If it is, we don't need to make any updates.
            $compareResults = compare-object $sorted $folder.Item("Member") -SyncWindow 0
            if ($compareResults -eq $null)
            {
                Write-Host "Already sorted."
            }
            else
            {
                Write-Host "Saving sorted list."
                # First remove all members of the list because the PowerShell provider will "optimize" the change by seeing there
                # were no items added or removed.  Then put all the members back in the sorted order.  (This is actually quite
                # dangerous to do as it could orphan the items, so we need to immediately put them back.)
                $folder.Item("Member") = @()
                $folder.Item("Member") = $sorted
            }
        }
    }
}

function Sort-MDTFolderSubfolders {

    [CmdletBinding()]
    PARAM
    (
        [Parameter(Position=1, ValueFromPipeline=$true)] $folder
    )
    Process
    {
        Write-Host "Sort-MDTFolderSubfolders: Processing folder $($folder.Name)"
        # Initialize the arrays
        $sorted = @()
        $unsorted = @()
        # Get the list of immediate child folders, sorted and unsorted
        $folderPath = $folder.PSPath.Substring($folder.PSPath.IndexOf("::")+2)
        $folderList = Get-ChildItem $folderPath | ? {$_.PSIsContainer}
        $folderList | % { $unsorted += $_.Name }
        $folderList | Sort Name | % { $sorted += $_.Name }
        # If there were any subfolders found, process them.
        if ($sorted.Count -gt 0)
        {
            # See if the list is already sorted.  If it is, we don't need to make any updates.
            $compareResults = compare-object $sorted $unsorted -SyncWindow 0
            if ($compareResults -eq $null)
            {
                Write-Host "Already sorted."
            }
            else
            {
                Write-Host "Sorting folders."
                # Create the temporary folder
                $null = New-Item "$folderPath\__TEMP__" -ItemType Folder
                # Move the folders into the temporary folder
                $sorted | % { Move-Item "$folderPath\$_" "$folderPath\__TEMP__" }
                # Move the folders back
                $sorted | % { Move-Item "$folderPath\__TEMP__\$_" "$folderPath" }

                # Remove the temporary folder
                Remove-Item "$folderPath\__TEMP__"
            }
        }
    }
}

# Enumerate the folders and call the functions above to process each one

Get-ChildItem DeploymentPointSorter: -Recurse | ? {$_.PSIsContainer} | Sort-MDTFolderItems

Get-ChildItem DeploymentPointSorter: -Recurse | ? {$_.PSIsContainer} | Sort-MDTFolderSubfolders

Save this as “DeploymentShareSorter.ps1” and you are all set.  A few notes on this script:

  • This script requires PowerShell 2.0 – that’s all I use any more.
  • As provided, this will only sort one deployment share at a time, but it would be fairly simple to do multiple: just duplicate the final two lines, passing each path (e.g. DS001:) that you want to sort.
  • There is one operation in this script that is somewhat dangerous:
  •                  $folder.Item("Member") = @()
  •                 $folder.Item("Member") = $sorted
  • I would suggest avoiding adding other items (drivers, applications, etc.) to the deployment share while this script is running, because those two lines could possibly cause those new items to be lost.  They first remove all items from the current folder and then add them back again (working around an optimization in the PowerShell provider code that tries to be smart about modified items – if you took out the first line the second line would have absolutely no effect).
  • This could take a while the first time through, but it should get faster after that first time because most of the content will already be sorted.
  • Don’t use a folder name of “__TEMP__” because that’s used by the script for sorting purposes.
  • Remember to edit the value assigned to the $rootPath variable.
  • If you want to schedule this script to run using the Windows Task Scheduler, just specify an action command line of “powershell.exe -File C:\Scripts\DeploymentShareSorter.ps1” (assuming the script was saved in C:\Scripts).

DeploymentShareSorter.zip