Private/Get-HDTBootImageComponentDependency.ps1

function Get-HDTBootImageComponentDependency {
    <#
        .SYNOPSIS
            The WinPE optional component dependency table, with a citation on
            every row.
 
        .DESCRIPTION
            One row per component that declares a dependency on ANOTHER OPTIONAL
            COMPONENT. Each row carries Requires (the component names) and Source
            (where the row came from).
 
            EVERY ROW IS TRANSCRIBED FROM MICROSOFT'S OWN PACKAGE METADATA. Each
            WinPE_OCs cab contains an `update.mum` - the package manifest -
            declaring the packages it is a child of:
 
                <package identifier="WinPE-PowerShell" releaseType="Feature Pack">
                  <parent integrate="delegate">
                    <assemblyIdentity name="WinPE-NetFx-Package" ... />
                  </parent>
 
            That is the citation for every row below, and it is re-derivable on
            any machine with the ADK installed:
 
                $oc = Get-HDTAdkPath -Asset WinPeOptionalComponent
                foreach ($cab in Get-ChildItem $oc -Filter *.cab) {
                    $dir = Join-Path $env:TEMP ([IO.Path]::GetFileNameWithoutExtension($cab.Name))
                    & "$env:SystemRoot\System32\expand.exe" -f:*.mum $cab.FullName $dir
                    # //package/parent/assemblyIdentity/@name
                }
 
            A DEPENDENCY THIS REPOSITORY CANNOT CITE IS OMITTED, NEVER GUESSED.
            An invented dependency refuses a build that would have worked, and
            does it in the name of documentation that does not say so. Omission
            is safe: a component absent from this table is allowed with no
            dependencies, which is already the shipped default for the twenty-odd
            components not named here.
 
            THREE KINDS OF PARENT ARE DELIBERATELY NOT ROWS:
 
            1. `Microsoft-Windows-WinPE-Package`, `Microsoft-Windows-Foundation-Package`
               and `Microsoft-Windows-ServerCore-Package` are WinPE itself. They
               are in the base image before any optional component is applied, so
               a row for them could only ever refuse a build that was fine.
            2. A dependency satisfied by the REQUIRED SIX can never refuse
               anything either, because those six are always present and always
               first - but the rows are kept anyway, because they are what proves
               The boot-verified order agrees with Microsoft's declared
               one (see the test of that name).
            3. Components whose manifest declares no parent at all. Note in
               particular that `WinPE-Setup-Server` and `WinPE-Setup-ASZ` declare
               none while `WinPE-Setup-Client` declares `WinPE-Setup`. That
               asymmetry is recorded as it is, not tidied into a pattern.
 
            Read from ADK 10.1.26100.2454, amd64.
 
        .INPUTS
            None. This command does not accept pipeline input.
 
        .OUTPUTS
            System.Collections.Hashtable - component name -> @{ Requires; Source }.
            Case-insensitive on the key, as component names are everywhere else.
 
        .EXAMPLE
            (Get-HDTBootImageComponentDependency)['WinPE-PowerShell'].Requires
 
            Returns WinPE-NetFx.
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param()

    Set-StrictMode -Version Latest
    $ErrorActionPreference = 'Stop'

    $source = 'ADK 10.1.26100.2454 amd64 WinPE_OCs\{0}.cab, update.mum: //package/parent/assemblyIdentity[@name="{1}-Package"]'

    $table = [System.Collections.Hashtable]::new([System.StringComparer]::OrdinalIgnoreCase)

    $table['WinPE-PowerShell'] = @{
        Requires = @('WinPE-NetFx')
        Source   = ($source -f 'WinPE-PowerShell', 'WinPE-NetFx')
    }

    $table['WinPE-DismCmdlets'] = @{
        Requires = @('WinPE-PowerShell')
        Source   = ($source -f 'WinPE-DismCmdlets', 'WinPE-PowerShell')
    }

    $table['WinPE-StorageWMI'] = @{
        Requires = @('WinPE-WMI')
        Source   = ($source -f 'WinPE-StorageWMI', 'WinPE-WMI')
    }

    $table['WinPE-PmemCmdlets'] = @{
        Requires = @('WinPE-PowerShell')
        Source   = ($source -f 'WinPE-PmemCmdlets', 'WinPE-PowerShell')
    }

    $table['WinPE-SecureBootCmdlets'] = @{
        Requires = @('WinPE-PowerShell')
        Source   = ($source -f 'WinPE-SecureBootCmdlets', 'WinPE-PowerShell')
    }

    # The only row where both sides are OPTIONAL, so the only row that can refuse
    # a build on its own. WinPE-Setup-Server and WinPE-Setup-ASZ declare no
    # parent - see the note above.
    $table['WinPE-Setup-Client'] = @{
        Requires = @('WinPE-Setup')
        Source   = ($source -f 'WinPE-Setup-Client', 'WinPE-Setup')
    }

    return $table
}