private/Import-OSDeployCoreOS.ps1

#Requires -PSEdition Core
#Requires -Version 7.4

function Import-OSDeployCoreOS {
    <#
    .SYNOPSIS
        Imports Windows images from mounted installation media into OSDeployCore
 
    .DESCRIPTION
        Finds mounted Windows installation media and opens an Out-GridView selector for one
        or more image indexes. For each selection, the function exports install.wim, copies
        the installation media, extracts WinRE, WinPE, and WinSE content, and writes image,
        registry, boot, driver, and operating-system metadata.
 
        OS content is stored below the OSDeployCore cache\windows-os directory. Matching
        recovery content is stored below cache\windows-re. Destination names use the build,
        revision, architecture, edition ID, and first image language. Existing OS or recovery
        destinations are not overwritten.
 
        The function initializes OSDeployCore paths before media selection. WhatIf and Confirm
        apply to each selected image import, but do not prevent that path initialization.
 
    .EXAMPLE
        PS> Import-OSDeployCoreOS
 
        Opens the image selector and imports the selected Windows images.
 
    .EXAMPLE
        PS> Import-OSDeployCoreOS -WhatIf
 
        Selects mounted-media images and shows which imports would run without creating their
        destination content.
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        System.IO.DirectoryInfo. Returns the OS destination directory for each image imported.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Version: 0.1.0
        Date: 2026-08-28
 
        Requires PowerShell 7.4 or later, Administrator rights, mounted Windows installation
        media, Out-GridView, DISM, curl.exe, makecab.exe, and robocopy.exe. The Windows ADK
        Windows PE add-on content used by the extraction workflow must be available.
    #>


    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([System.IO.DirectoryInfo])]
    param ()

    begin {
        Write-HostOSDeployBanner
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Start"
        Initialize-OSDeployCorePaths

        # Requires Run as Administrator
        if (-not (Test-IsAdministrator)) {
            Write-Warning "[$(Get-Date -Format s)] This function must be Run as Administrator"
            return
        }

        $WindowsMediaImages = Get-MountedWindowsImageIndex -GridView Multiple
    }

    process {
        # Stop before creating cache directories when discovery returned no selectable images.
        if ($null -eq $WindowsMediaImages) {
            Write-Warning "[$(Get-Date -Format s)] WindowsImage on Windows Installation Media was not found. Mount a Windows Installation ISO and try again."
            Write-HostDateTimeDarkGray 'Windows 11 x64 Download: https://www.microsoft.com/en-us/software-download/windows11'
            Write-HostDateTimeDarkGray 'Windows 11 arm64 Download: https://www.microsoft.com/en-us/software-download/windows11arm64'
            return
        }

        $WindowsOSRoot = [System.IO.Path]::Combine($Script:OSDeployCorePath, 'cache', 'windows-os')
        $WindowsRERoot = [System.IO.Path]::Combine($Script:OSDeployCorePath, 'cache', 'windows-re')

        foreach ($SourceWindowsImage in $WindowsMediaImages) {
            # Build destination name: version-architecture-editionid-language (all lowercase)
            $versionParts = $SourceWindowsImage.Version.ToString().Split('.')
            $trimmedVersion = "$($versionParts[2]).$($versionParts[3])"
            $Architecture = $SourceWindowsImage.Architecture.ToLower()
            $EditionId = $SourceWindowsImage.EditionId.ToLower()
            $Language = ($SourceWindowsImage.Languages | Select-Object -First 1).ToLower()
            $DestinationName = "$trimmedVersion-$Architecture-$EditionId-$Language"

            Write-Verbose "[$($MyInvocation.MyCommand.Name)] DestinationName: $DestinationName"

            $DestinationDirectory = Join-Path $WindowsOSRoot $DestinationName
            $ImportWinREDirectory = Join-Path $WindowsRERoot $DestinationName

            # Keep the OS and WinRE caches paired by refusing partial or duplicate destination names.
            if ([System.IO.Directory]::Exists($DestinationDirectory)) {
                Write-Warning "[$(Get-Date -Format s)] '$DestinationName' already exists in windows-os. Skipping duplicate import."
                continue
            }
            if ([System.IO.Directory]::Exists($ImportWinREDirectory)) {
                Write-Warning "[$(Get-Date -Format s)] '$DestinationName' already exists in windows-re. Skipping duplicate import."
                continue
            }

            # Gate all directory creation and image extraction behind WhatIf/Confirm handling.
            if (-not $PSCmdlet.ShouldProcess($DestinationName, 'Import Windows OS image from mounted media')) {
                continue
            }

            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Importing $($SourceWindowsImage.ImageName) ($Architecture) ..."

            $DestinationCore = Join-Path $DestinationDirectory '.core'
            $DestinationTemp = Join-Path $DestinationDirectory '.temp'
            $DestinationLogs = Join-Path $DestinationTemp 'logs'
            $DestinationWim  = Join-Path $DestinationDirectory '.wim'
            $DestinationMedia = Join-Path $DestinationDirectory 'WinOS-Media'

            [System.IO.Directory]::CreateDirectory($DestinationCore) | Out-Null
            [System.IO.Directory]::CreateDirectory($DestinationLogs) | Out-Null
            [System.IO.Directory]::CreateDirectory($DestinationWim)  | Out-Null
            [System.IO.Directory]::CreateDirectory($DestinationMedia) | Out-Null

            # Write id.json
            @{ id = $DestinationName } | ConvertTo-Json -Depth 5 |
                Out-File (Join-Path $DestinationCore 'id.json') -Encoding utf8 -Force

            # Copy media files (excluding install.wim and install.esd)
            Write-HostDateTimeDarkGray 'Copying Windows media files ...'
            robocopy "$($SourceWindowsImage.MediaRoot)" "$DestinationMedia" *.* /e /xf install.wim install.esd | Out-Null
            # Remove read-only attributes set by media copy
            foreach ($filePath in [System.IO.Directory]::EnumerateFiles($DestinationMedia, '*', [System.IO.SearchOption]::AllDirectories)) {
                $fi = [System.IO.FileInfo]::new($filePath)
                if ($fi.IsReadOnly) { $fi.IsReadOnly = $false }
            }

            # Export the selected Windows image
            $DestinationImagePath = Join-Path $DestinationMedia 'sources\install.wim'
            $CurrentLog = Join-Path $DestinationLogs "$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Export-windowsimage.log"
            Write-HostDateTimeDarkGray 'Exporting Windows image ...'
            Export-WindowsImage -SourceImagePath $SourceWindowsImage.ImagePath -SourceIndex $SourceWindowsImage.ImageIndex -DestinationImagePath $DestinationImagePath -LogPath $CurrentLog | Out-Null

            # Export OS image metadata
            $Image = Get-WindowsImage -ImagePath $DestinationImagePath -Index 1
            $Image | Export-Clixml -Path (Join-Path $DestinationCore 'winos-windowsimage.xml')
            $Image | ConvertTo-Json -Depth 5 | Out-File (Join-Path $DestinationCore 'winos-windowsimage.json') -Encoding utf8
            $ImageContent = Get-WindowsImageContent -ImagePath $DestinationImagePath -Index 1
            $ImageContent | Out-File (Join-Path $DestinationCore 'winos-windowsimagecontent.txt') -Encoding ascii -Force

            # Write windows-os properties.json
            $WinOSProperties = [ordered]@{
                Type                = 'WinOS'
                Id                  = $DestinationName
                Name                = $DestinationName
                CreatedTime         = $Image.CreatedTime
                ModifiedTime        = $Image.ModifiedTime
                InstallationType    = $Image.InstallationType
                Version             = $Image.Version.ToString()
                Architecture        = $Architecture
                Languages           = @($Image.Languages)
                ImageSize           = $Image.ImageSize
                DirectoryCount      = $Image.DirectoryCount
                FileCount           = $Image.FileCount
                ImageName           = $Image.ImageName
                EditionId           = $Image.EditionId
                Path                = $DestinationDirectory
                ImagePath           = $DestinationImagePath
                ImageIndex          = 1
                ImageDescription    = $Image.ImageDescription
                WIMBoot             = $Image.WIMBoot
                ImageType           = $Image.ImageType
                ProductName         = $Image.ProductName
                Hal                 = $Image.Hal
                ProductType         = $Image.ProductType
                ProductSuite        = $Image.ProductSuite
                MajorVersion        = $Image.MajorVersion
                MinorVersion        = $Image.MinorVersion
                Build               = $Image.Build
                SPBuild             = $Image.SPBuild
                SPLevel             = $Image.SPLevel
                ImageBootable       = $Image.ImageBootable
                SystemRoot          = $Image.SystemRoot
                DefaultLanguageIndex = $Image.DefaultLanguageIndex
            }
            $WinOSProperties | ConvertTo-Json -Depth 5 |
                Out-File (Join-Path $DestinationDirectory 'properties.json') -Encoding utf8 -Force

            # Mount the Windows image read-only to extract files
            $MountPath = [System.IO.Path]::Combine($env:TEMP, "OSDeployCore-Mount-$([Guid]::NewGuid().ToString('N').Substring(0, 8))")
            [System.IO.Directory]::CreateDirectory($MountPath) | Out-Null

            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Mounting Windows image (read-only) to $MountPath"
            try {
                Mount-WindowsImage -ImagePath $DestinationImagePath -Index 1 -Path $MountPath -ReadOnly -ErrorAction Stop | Out-Null
                $MountDirectory = $MountPath

                #region WinRE extraction
                $winreSource = Join-Path $MountDirectory 'Windows\System32\Recovery\winre.wim'
                $reagentSource = Join-Path $MountDirectory 'Windows\System32\Recovery\ReAgent.xml'
                Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Extracting WinRE from $winreSource"

                # ReAgent.xml is optional on some media, so copy it only when the image contains it.
                if (Test-Path $reagentSource) {
                    Copy-Item -Path $reagentSource -Destination (Join-Path $DestinationTemp 'os-reagent.xml') | Out-Null
                }
                if (Test-Path $winreSource) {
                    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Copying WinRE to $DestinationWim"
                    Copy-Item -Path $winreSource -Destination (Join-Path $DestinationWim 'winre.wim') | Out-Null

                    $WinreWimPath = Join-Path $DestinationWim 'winre.wim'
                    $WinreImage = Get-WindowsImage -ImagePath $WinreWimPath -Index 1
                    $WinreImage | ConvertTo-Json -Depth 5 | Out-File (Join-Path $DestinationCore 'winre-windowsimage.json') -Encoding utf8 -Force
                    $WinreImage | Export-Clixml -Path (Join-Path $DestinationCore 'winre-windowsimage.xml')
                    $WinreImageContent = Get-WindowsImageContent -ImagePath $WinreWimPath -Index 1
                    $WinreImageContent | Out-File (Join-Path $DestinationCore 'winre-windowsimagecontent.txt') -Encoding ascii -Force
                }
                #endregion

                #region WinPE and WinSE extraction
                $BootWim = Join-Path $SourceWindowsImage.MediaRoot 'sources\boot.wim'
                # Installation media without boot.wim can still provide a valid OS and WinRE import.
                if (Test-Path $BootWim) {
                    Write-HostDateTimeDarkGray 'Exporting WinPE and WinSE ...'

                    # WinPE (boot.wim index 1)
                    $CurrentLog = Join-Path $DestinationLogs "$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Export-WinPE.log"
                    Export-WindowsImage -SourceImagePath $BootWim -SourceIndex 1 -DestinationImagePath (Join-Path $DestinationWim 'winpe.wim') -LogPath $CurrentLog | Out-Null
                    $WinpeWimPath = Join-Path $DestinationWim 'winpe.wim'
                    $WinpeImage = Get-WindowsImage -ImagePath $WinpeWimPath -Index 1
                    $WinpeImage | ConvertTo-Json -Depth 5 | Out-File (Join-Path $DestinationCore 'winpe-windowsimage.json') -Encoding utf8 -Force
                    $WinpeImage | Export-Clixml -Path (Join-Path $DestinationCore 'winpe-windowsimage.xml')
                    $WinpeImageContent = Get-WindowsImageContent -ImagePath $WinpeWimPath -Index 1
                    $WinpeImageContent | Out-File (Join-Path $DestinationCore 'winpe-windowsimagecontent.txt') -Encoding ascii -Force

                    # WinSE (boot.wim index 2)
                    $CurrentLog = Join-Path $DestinationLogs "$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Export-WinSE.log"
                    Export-WindowsImage -SourceImagePath $BootWim -SourceIndex 2 -DestinationImagePath (Join-Path $DestinationWim 'winse.wim') -LogPath $CurrentLog | Out-Null
                    $WinseWimPath = Join-Path $DestinationWim 'winse.wim'
                    $WinseImage = Get-WindowsImage -ImagePath $WinseWimPath -Index 1
                    $WinseImage | ConvertTo-Json -Depth 5 | Out-File (Join-Path $DestinationCore 'winse-windowsimage.json') -Encoding utf8 -Force
                    $WinseImage | Export-Clixml -Path (Join-Path $DestinationCore 'winse-windowsimage.xml')
                    $WinseImageContent = Get-WindowsImageContent -ImagePath $WinseWimPath -Index 1
                    $WinseImageContent | Out-File (Join-Path $DestinationCore 'winse-windowsimagecontent.txt') -Encoding ascii -Force
                }
                #endregion

                #region Registry hives
                Write-HostDateTimeDarkGray 'Backing up registry hives ...'
                $RegistryHives = @('SOFTWARE', 'SYSTEM')
                $RobocopyLog = Join-Path $DestinationLogs 'os-registry.log'
                foreach ($Item in $RegistryHives) {
                    robocopy "$MountDirectory\Windows\System32\config" "$DestinationTemp" $Item /b /np /ts /tee /r:0 /w:0 /log+:"$RobocopyLog" | Out-Null
                }
                $softwareSrc = [System.IO.Path]::Combine($DestinationTemp, 'SOFTWARE')
                $systemSrc   = [System.IO.Path]::Combine($DestinationTemp, 'SYSTEM')
                if ([System.IO.File]::Exists($softwareSrc)) { [System.IO.File]::Move($softwareSrc, [System.IO.Path]::Combine($DestinationTemp, 'os-software.hive'), $true) }
                if ([System.IO.File]::Exists($systemSrc))   { [System.IO.File]::Move($systemSrc,   [System.IO.Path]::Combine($DestinationTemp, 'os-system.hive'),   $true) }
                #endregion

                #region Boot files
                $BootPath = Join-Path $MountDirectory 'Windows\Boot'
                if (Test-Path $BootPath) {
                    Write-HostDateTimeDarkGray 'Backing up boot files ...'
                    $RobocopyLog = Join-Path $DestinationLogs 'os-boot.log'
                    robocopy "$BootPath" (Join-Path $DestinationCore 'os-boot') *.* /e /tee /r:0 /w:0 /log+:"$RobocopyLog" | Out-Null
                }
                #endregion

                #region Windows executables and subdirectories
                Write-HostDateTimeDarkGray 'Backing up OS system files ...'
                $BackupOSFiles = @(
                    'aerolite*.*'
                    'bcp47*.dll'
                    'bits*.*'
                    'BitsTransfer*.*'
                    'BranchCache*.*'
                    'cacls.exe*'
                    'choice.exe*'
                    'comp.exe*.*'
                    'credssp*.*'
                    'curl.exe'
                    'ddp*.*'
                    'defrag.exe*'
                    'djoin*.*'
                    'dmcmnutils*.*'
                    'dssec*.*'
                    'dsuiext*.*'
                    'edputil*.*'
                    'es.dll*'
                    'explorerframe*.*'
                    'forfiles*.*'
                    'getmac*.*'
                    'gpedit*.*'
                    'hyyp.sys*'
                    'magnification*.*'
                    'magnify*.*'
                    'makecab.*'
                    'mdmpostprocessevaluator*.*'
                    'mdmregistration*.*'
                    'mscms*.*'
                    'msinfo32.*'
                    'mstsc*.*'
                    'netprofm*.*'
                    'npmproxy*.*'
                    'nslookup.*'
                    'osk*.*'
                    'PCPKsp.dll*'
                    'pdh.dll*'
                    'PeerDist*.*'
                    'perfmon*.*'
                    'setx.*'
                    'shellstyle*.*'
                    'shutdown.*'
                    'shutdownext.*'
                    'shutdownux.*'
                    'srpapi.dll*'
                    'ssdpapi*.*'
                    'StructuredQuery*.*'
                    'systeminfo.*'
                    'tar.exe'
                    'tskill.*'
                    'w32tm*.*'
                    'winver.*'
                    'WSDApi*.*'
                )
                $RobocopyLog = Join-Path $DestinationLogs 'os-files.log'
                $System32Src = Join-Path $MountDirectory 'Windows\System32'
                $System32Dst = Join-Path $DestinationCore 'os-files\Windows\System32'
                foreach ($Item in $BackupOSFiles) {
                    robocopy "$System32Src" "$System32Dst" $Item /s /xd rescache servicing /ndl /b /np /ts /tee /r:0 /w:0 /log+:"$RobocopyLog" | Out-Null
                }

                # PowerShell Modules
                $PsModuleSrc = Join-Path $MountDirectory 'Program Files\WindowsPowerShell'
                $PsModuleDst = Join-Path $DestinationCore 'os-files\Program Files\WindowsPowerShell'
                robocopy "$PsModuleSrc" "$PsModuleDst" *.* /e /tee /r:0 /w:0 /log+:"$RobocopyLog" | Out-Null

                # Ethernet drivers
                Write-HostDateTimeDarkGray 'Extracting Ethernet drivers ...'
                $packagesPath    = [System.IO.Path]::Combine($MountDirectory, 'Windows', 'servicing', 'Packages')
                $driverStoreRepo = [System.IO.Path]::Combine($MountDirectory, 'Windows', 'System32', 'DriverStore', 'FileRepository')
                $EthernetClientMums = if ([System.IO.Directory]::Exists($packagesPath)) {
                    [System.IO.Directory]::GetFiles($packagesPath, 'Microsoft-Windows-Ethernet-Client-*.mum')
                } else { @() }
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Ethernet .mum files found: $($EthernetClientMums.Length)"
                if ($EthernetClientMums.Length -gt 0) {
                    $EthernetDrivers = foreach ($mumPath in $EthernetClientMums) {
                        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Parsing Ethernet .mum: $mumPath"
                        $MumXml    = [System.Xml.Linq.XDocument]::Load($mumPath)
                        $ns        = $MumXml.Root.Name.Namespace
                        $Identity  = $MumXml.Root.Element($ns + 'assemblyIdentity')
                        $infEl     = $MumXml.Root.Descendants($ns + 'inf') | Select-Object -First 1
                        $DriverInf = $infEl?.Value
                        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Ethernet Identity.name: $($Identity?.Attribute('name')?.Value) | version: $($Identity?.Attribute('version')?.Value) | arch: $($Identity?.Attribute('processorArchitecture')?.Value) | inf: $DriverInf"
                        # Ignore incomplete package manifests because they cannot identify a DriverStore folder.
                        if ($Identity -and $DriverInf) {
                            [PSCustomObject]@{
                                Name         = $Identity.Attribute('name').Value -replace '^Microsoft-Windows-Ethernet-Client-', '' -replace '-FOD-Package$', ''
                                Version      = [version]$Identity.Attribute('version').Value
                                Architecture = $Identity.Attribute('processorArchitecture').Value
                                InfFile      = $DriverInf
                            }
                        } else {
                            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Ethernet .mum skipped — Identity or DriverInf is null"
                        }
                    }
                    # Deduplicate: keep highest version per driver name (O(n) hashtable vs O(n log n) Group-Object)
                    $dedupHash = @{}
                    foreach ($d in $EthernetDrivers) {
                        if (-not $dedupHash.ContainsKey($d.Name) -or $d.Version -gt $dedupHash[$d.Name].Version) {
                            $dedupHash[$d.Name] = $d
                        }
                    }
                    $EthernetDrivers = $dedupHash.Values
                    Write-Verbose "[$($MyInvocation.MyCommand.Name)] Ethernet unique drivers after dedup: $($EthernetDrivers.Count)"

                    foreach ($Driver in $EthernetDrivers) {
                        $InfFileWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($Driver.InfFile)
                        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Ethernet driver: $($Driver.Name) v$($Driver.Version) arch=$($Driver.Architecture) inf=$($Driver.InfFile) infBase=$InfFileWithoutExtension"
                        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Searching DriverStore: $driverStoreRepo\$InfFileWithoutExtension*"
                        $DriverFolder = [System.IO.Directory]::EnumerateDirectories($driverStoreRepo, "$InfFileWithoutExtension*") | Select-Object -First 1
                        # Copy only drivers whose package INF can be matched to an installed DriverStore folder.
                        if ($DriverFolder) {
                            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Ethernet driver folder found: $DriverFolder"
                            $EthernetDst = [System.IO.Path]::Combine($Script:OSDeployBootAssetsPath, "winpedrivers-$($Driver.Architecture)", "microsoft-windows-ethernet-$($Driver.Version)", $Driver.Name)
                            Write-Host -ForegroundColor DarkGray $EthernetDst
                            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Ethernet destination: $EthernetDst"
                            if (Test-Path "$EthernetDst\*") {
                                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Skipping existing Ethernet driver: $($Driver.Name)-$($Driver.Version)"
                            } else {
                                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Copying Ethernet driver: $($Driver.Name)-$($Driver.Version)"
                                robocopy "$DriverFolder" "$EthernetDst" *.* /e /r:0 /w:0 /log+:"$RobocopyLog" | Out-Null
                            }
                        } else {
                            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Ethernet driver folder NOT found for inf base: $InfFileWithoutExtension"
                        }
                    }
                } else {
                    Write-Verbose "[$($MyInvocation.MyCommand.Name)] No Ethernet .mum files found in: $packagesPath"
                }

                # Wi-Fi drivers
                Write-HostDateTimeDarkGray 'Extracting Wi-Fi drivers ...'
                $WifiClientMums = if ([System.IO.Directory]::Exists($packagesPath)) {
                    [System.IO.Directory]::GetFiles($packagesPath, 'Microsoft-Windows-Wifi-Client-*.mum')
                } else { @() }
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Wi-Fi .mum files found: $($WifiClientMums.Length)"
                if ($WifiClientMums.Length -gt 0) {
                    $WifiDrivers = foreach ($mumPath in $WifiClientMums) {
                        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Parsing Wi-Fi .mum: $mumPath"
                        $MumXml    = [System.Xml.Linq.XDocument]::Load($mumPath)
                        $ns        = $MumXml.Root.Name.Namespace
                        $Identity  = $MumXml.Root.Element($ns + 'assemblyIdentity')
                        $infEl     = $MumXml.Root.Descendants($ns + 'inf') | Select-Object -First 1
                        $DriverInf = $infEl?.Value
                        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Wi-Fi Identity.name: $($Identity?.Attribute('name')?.Value) | version: $($Identity?.Attribute('version')?.Value) | arch: $($Identity?.Attribute('processorArchitecture')?.Value) | inf: $DriverInf"
                        # Ignore incomplete package manifests because they cannot identify a DriverStore folder.
                        if ($Identity -and $DriverInf) {
                            [PSCustomObject]@{
                                Name         = $Identity.Attribute('name').Value -replace '^Microsoft-Windows-Wifi-Client-', '' -replace '-FOD-Package$', ''
                                Version      = [version]$Identity.Attribute('version').Value
                                Architecture = $Identity.Attribute('processorArchitecture').Value
                                InfFile      = $DriverInf
                            }
                        } else {
                            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Wi-Fi .mum skipped — Identity or DriverInf is null"
                        }
                    }
                    # Deduplicate: keep highest version per driver name (O(n) hashtable vs O(n log n) Group-Object)
                    $dedupHashWifi = @{}
                    foreach ($d in $WifiDrivers) {
                        if (-not $dedupHashWifi.ContainsKey($d.Name) -or $d.Version -gt $dedupHashWifi[$d.Name].Version) {
                            $dedupHashWifi[$d.Name] = $d
                        }
                    }
                    $WifiDrivers = $dedupHashWifi.Values
                    Write-Verbose "[$($MyInvocation.MyCommand.Name)] Wi-Fi unique drivers after dedup: $($WifiDrivers.Count)"

                    foreach ($Driver in $WifiDrivers) {
                        $InfFileWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($Driver.InfFile)
                        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Wi-Fi driver: $($Driver.Name) v$($Driver.Version) arch=$($Driver.Architecture) inf=$($Driver.InfFile) infBase=$InfFileWithoutExtension"
                        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Searching DriverStore: $driverStoreRepo\$InfFileWithoutExtension*"
                        $DriverFolder = [System.IO.Directory]::EnumerateDirectories($driverStoreRepo, "$InfFileWithoutExtension*") | Select-Object -First 1
                        # Copy only drivers whose package INF can be matched to an installed DriverStore folder.
                        if ($DriverFolder) {
                            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Wi-Fi driver folder found: $DriverFolder"
                            $WifiDst = [System.IO.Path]::Combine($Script:OSDeployBootAssetsPath, "winpedrivers-$($Driver.Architecture)", "microsoft-windows-wifi-$($Driver.Version)", $Driver.Name)
                            Write-Host -ForegroundColor DarkGray $WifiDst
                            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Wi-Fi destination: $WifiDst"
                            if (Test-Path "$WifiDst\*") {
                                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Skipping existing Wi-Fi driver: $($Driver.Name)-$($Driver.Version)"
                            } else {
                                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Copying Wi-Fi driver: $($Driver.Name)-$($Driver.Version)"
                                robocopy "$DriverFolder" "$WifiDst" *.* /e /r:0 /w:0 /log+:"$RobocopyLog" | Out-Null
                            }
                        } else {
                            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Wi-Fi driver folder NOT found for inf base: $InfFileWithoutExtension"
                        }
                    }
                } else {
                    Write-Verbose "[$($MyInvocation.MyCommand.Name)] No Wi-Fi .mum files found in: $packagesPath"
                }

                # OpenSSH
                <#
                $OpenSSHPath = Join-Path $MountDirectory 'Windows\System32\OpenSSH'
                if (Test-Path $OpenSSHPath) {
                    $OpenSSHDst = Join-Path $DestinationCore 'os-files\Windows\System32\OpenSSH'
                    robocopy "$OpenSSHPath" "$OpenSSHDst" *.* /e /tee /r:0 /w:0 /log+:"$RobocopyLog" | Out-Null
                }
 
    .NOTES
 
    Dependencies:
      Module Functions: Get-MountedWindowsImageIndex, Initialize-OSDeployCorePaths, Test-IsAdministrator, Write-HostOSDeployBanner, Write-HostDateTimeDarkGray
      Executables: curl.exe, makecab.exe, robocopy.exe
      Windows Features: DISM, Windows ADK WinPE Addon
      DotNet Classes: [IO.Path], [System.IO.DirectoryInfo]
                #>

                #endregion
            }
            finally {
                # Always dismount to avoid orphaned mounts
                if ([System.IO.Directory]::Exists($MountPath)) {
                    Write-HostDateTimeDarkGray 'Dismounting Windows image ...'
                    Dismount-WindowsImage -Path $MountPath -Discard -ErrorAction SilentlyContinue | Out-Null
                    try { [System.IO.Directory]::Delete($MountPath, $true) } catch { }
                }
            }

            # Remove Read-Only from all files
            foreach ($filePath in [System.IO.Directory]::EnumerateFiles($DestinationDirectory, '*', [System.IO.SearchOption]::AllDirectories)) {
                $fi = [System.IO.FileInfo]::new($filePath)
                if ($fi.IsReadOnly) { $fi.IsReadOnly = $false }
            }

            #region Build the WinRE directory
            Write-HostDateTimeDarkGray 'Building WinRE directory ...'
            robocopy (Join-Path $DestinationDirectory '.core') (Join-Path $ImportWinREDirectory '.core') *.* /e /xf OSImage.* winpe-windowsimage* winse-windowsimage* /tee /r:0 /w:0 | Out-Null
            robocopy (Join-Path $DestinationDirectory '.temp') (Join-Path $ImportWinREDirectory '.temp') *.* /e /xd logs /tee /r:0 /w:0 | Out-Null
            robocopy (Join-Path $DestinationDirectory '.wim') (Join-Path $ImportWinREDirectory '.wim') winre.wim /e /tee /r:0 /w:0 | Out-Null

            # Write windows-re properties.json
            $WinreWimPath = Join-Path $ImportWinREDirectory '.wim\winre.wim'
            # Emit WinRE metadata only when recovery media was present in the selected OS image.
            if (Test-Path $WinreWimPath) {
                $WinreImageForProps = Get-WindowsImage -ImagePath $WinreWimPath -Index 1
                $WinREProperties = [ordered]@{
                    Type                = 'WinRE'
                    Id                  = $DestinationName
                    Name                = $DestinationName
                    CreatedTime         = $WinreImageForProps.CreatedTime
                    ModifiedTime        = $WinreImageForProps.ModifiedTime
                    InstallationType    = $WinreImageForProps.InstallationType
                    Version             = $WinreImageForProps.Version.ToString()
                    Architecture        = $Architecture
                    Languages           = @($WinreImageForProps.Languages)
                    ImageSize           = $WinreImageForProps.ImageSize
                    DirectoryCount      = $WinreImageForProps.DirectoryCount
                    FileCount           = $WinreImageForProps.FileCount
                    ImageName           = $WinreImageForProps.ImageName
                    OSImageName         = $Image.ImageName
                    OSEditionId         = $Image.EditionId
                    OSVersion           = $Image.Version.ToString()
                    OSCreatedTime       = $Image.CreatedTime
                    OSModifiedTime      = $Image.ModifiedTime
                    Path                = $ImportWinREDirectory
                    ImagePath           = $WinreWimPath
                    ImageIndex          = 1
                }
                $WinREProperties | ConvertTo-Json -Depth 5 |
                    Out-File (Join-Path $ImportWinREDirectory 'properties.json') -Encoding utf8 -Force
            }
            #endregion

            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Import complete: $DestinationName"
            Get-Item -Path $DestinationDirectory
        }
    }

    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] End"
    }
}