This script is a somewhat faster means of updating the Media deployment folder (\DeploymentMedia1) when certain kinds of changes are made:

  • New/modified applications
  • New/modified task sequences
  • New/modified drivers
  • New/modified packages
  • New/modified operating systems

If you need to rebuild the Windows PE image used in MDT (such as a new driver for Windows PE or a change to the IntroScreen.hta file), then you need to run the full update utility. This one, although pretty short, will drive the computer pretty hard while it runs (usually 2-3 minutes). This is, however, much faster than waiting the normal 20-30 minutes for the full media update process to complete.

# This updates an existing media folder

$MediaName = 'MEDIA001'
$Root = '.\DeploymentShare.ROOT'

Function CopyPath {
    Param([String]$RelPath, [String]$R, [String]$D)
    $P1 = $R + $RelPath
    $P2 = $D + $RelPath
    
    # Write-Host " Copying path" $RelPath
    Start-Process "Robocopy" -ArgumentList "`"$P1`" `"$P2`" /MIR /XF *_AM.bak *_PM.bak" -WindowStyle Hidden
}

# Get the medias.xml content in order to determine folder
[xml]$MediasXML = Get-Content($Root + '\Control\Medias.xml')
$Medias = $MediasXML.medias.media
$Media = $Medias | where { $_.Name -eq $MediaName }
$ProfileName = $Media.SelectionProfile
$MediaFldr = ($Media.Root + '\Content\Deploy')

# Get the selectionprofiles.xml to determine what we need to do
[xml]$SelectionProfilesXML = Get-Content($Root + '\Control\SelectionProfiles.xml')
$SelectionProfile = $SelectionProfilesXML.selectionProfiles.selectionProfile | where { $_.Name -eq $ProfileName }
[xml]$Definition = $SelectionProfile.Definition
$Includes = $Definition.SelectionProfile.Include
$Excludes = $Definition.SelectionProfile.Exclude
Write-Host "Found" $Includes.Count "include definitions"
Write-Host "Found" $Excludes.Count "exclude definitions"

$Items = @{}
# Parse through each of our Include paths as the "root" of the include operation
$Includes | foreach { 
    $P = $_.path 
    Write-Host "`nProcessing Include Path" $P
    
    # Interpret the path to break apart the type ($T) and path for that type ($IncludePath)
    if ($P.IndexOf('\') -eq -1) { 
        $IncludePath = "\" 
        $T = $P
    } else {
        $T = $P.SubString(0,$P.IndexOf('\'))
        $IncludePath = $P.Substring($P.IndexOf('\'))
    }
    Write-Host " IncludePath" $IncludePath
    
    # Based on type, specify the proper group and main files for data
    switch ($T) {
        'Applications' { $MFN = ($Root + '\Control\Applications.xml'); $MGFN = ($Root + '\Control\ApplicationGroups.xml') }
        'Operating Systems' { $MFN = ($Root + '\Control\OperatingSystems.xml'); $MGFN = ($Root + '\Control\OperatingSystemGroups.xml') } 
        'Out-of-Box Drivers' { $MFN = ($Root + '\Control\Drivers.xml'); $MGFN = ($Root + '\Control\DriverGroups.xml') } 
        'Packages' { $MFN = ($Root + '\Control\Packages.xml'); $MGFN = ($Root + '\Control\PackageGroups.xml') } 
        'Task Sequences' { $MFN = ($Root + '\Control\TaskSequences.xml'); $MGFN = ($Root + '\Control\TaskSequenceGroups.xml') }
    }
    Write-Host " MGFN" $MGFN
    Write-Host " MFN" $MFN
    
    # Open up the type's group file and process group entries, looking for matches
    # based on our $IncludePath
    [xml]$GroupsXML = Get-Content($MGFN)
    $Groups = $GroupsXML.groups.group | where { $_.enable -eq "True" }
    $Groups | foreach {
        if ($_.Name -eq "default") {
            $GroupPath = "\"
        } else {
            $GroupPath = ("\" + $_.Name)
        }
        
        # Reconsitute the type and path together to compare against Excludes
        $ExcludeTest = ($T + $GroupPath)
        # Default to not including the group
        $IncludeThisGroup = $False
        # If the group starts with our include path, mark it as included
        if ($GroupPath.IndexOf($IncludePath) -eq 0) { $IncludeThisGroup = $True }
        # Exclude the \hidden directory
        if ($GroupPath -eq "\hidden") { $IncludeThisGroup = $False }
        # Parse the exclusions, looking for a match, if found, exclude the group
        if ($Excludes.Count -gt 0) { $Excludes | foreach { if ($_.path.IndexOf($ExcludeTest) -eq 0) { $IncludeThisGroup = $False } } }
        
        # If we are including this group, add the GUIDs of items to be included to our Items array
        if ($IncludeThisGroup) { 
            Write-Host " Including group" $GroupPath
            $Member = $_.Member
            if ($Member.Count -gt 0) {
                $Member | foreach { 
                    if (!($Items.ContainsKey($T + '\' + $_))) { 
                        $Items.Add($T + '\' + $_, $_)
                    }
                }
            }
        }
    }
}
Write-Host "`nLocated" $Items.Count "items to be copied"
$ItemsSorted = $Items.GetEnumerator() | Sort-Object Name
$T = ""
$ItemsSorted | foreach { 
    $TN = $_.Name.SubString(0,$_.Name.IndexOf('\'))
    $MG = $_.Value
    if ($T -ne $TN) {
        $T = $TN
        switch ($T) {
            'Applications' { $MFN = ($Root + '\Control\Applications.xml') }
            'Operating Systems' { $MFN = ($Root + '\Control\OperatingSystems.xml') }
            'Out-of-Box Drivers' { $MFN = ($Root + '\Control\Drivers.xml') }
            'Packages' { $MFN = ($Root + '\Control\Packages.xml') }
            'Task Sequences' { $MFN = ($Root + '\Control\TaskSequences.xml') }
        }
        Write-Host "`n Loading" $MFN
        
        [xml]$DataXML = Get-Content($MFN)
        $Data = @{}
        $DataXML.GetEnumerator() | foreach {
            $_.GetEnumerator() | foreach {
                $Data.Add($_.guid,($_.Source + $_.ID))
            }
        }
        $Z = $Data.Count
        $Y = 0
    }
    
    if ($Data.ContainsKey($MG)) {
        switch ($T) {
            'Applications' { if ($Data.Get_Item($MG)) { CopyPath $Data.Get_Item($MG).SubString(1) $Root $MediaFldr } } 
            'Operating Systems' { If ($Data.Get_Item($MG)) { CopyPath $Data.Get_Item($MG).SubString(1) $Root $MediaFldr } } 
            'Out-of-Box Drivers' { $Y+=1; if ($Data.Get_Item($MG)) { CopyPath $Data.Get_Item($MG).SubString(1).SubString(0,$Data.Get_Item($MG).LastIndexOf('\')-1) $Root $MediaFldr }; if (($Y % 100) -eq 0) { Write-Host $Y "/" $Z; Start-Sleep -Seconds 1 } }
            'Packages' { if ($Data.Get_Item($MG)) { CopyPath $Data.Get_Item($MG).SubString(1).SubString(0,$Data.Get_Item($MG).LastIndexOf('\')-1) $Root $MediaFldr } }
            'Task Sequences' { if ($Data.Get_Item($MG)) { CopyPath ('\Control\' + $Data.Get_Item($MG)) $Root $MediaFldr } }
        }
    }
}

Write-Host "`nCopying XML control files..."
Copy-Item -Path ($Root + '\Control\*.xml') -Destination ($MediaFldr + '\Control')

Write-Host "`nWaiting for copies to finish..."
$X = (Get-Process | where { $_.ProcessName -eq "Robocopy" }).Count 
while ($X -ne 0) { Write-Host $X "outstanding job(s)..."; Start-Sleep -Seconds 1; $X = (Get-Process | where { $_.ProcessName -eq "Robocopy" }).Count } 

Write-Host "All finished"