public/Import-OSDeployOS.ps1

#Requires -PSEdition Core
#Requires -Version 7.4

function Import-OSDeployOS {
    <#
    .SYNOPSIS
        Imports Windows OS images from mounted Windows installation media to OSDeployCore.
 
    .DESCRIPTION
        The Import-OSDeployOS function extracts and imports Windows OS and Recovery
        Environment (WinRE) images from mounted Windows installation media ISO files.
 
        This function performs the following operations:
        1. Validates administrator privileges
        2. Scans for mounted Windows installation media ISO files
        3. Displays an Out-GridView selection dialog for available installation indexes
        4. Exports the selected Windows image (install.wim) and all media files
        5. Extracts WinRE, WinPE, and WinSE images with metadata
        6. Backs up registry hives, boot files, and OS system files
        7. Creates a parallel windows-re directory with WinRE-specific content
 
        The imported images are stored under $env:ProgramData\OSDeployCore with a naming
        convention of "version-architecture-editionid-language"
        (e.g., "26200.8037-amd64-enterprise-en-us"). The version is trimmed to only the
        build and revision numbers. Duplicate imports are detected and skipped.
 
        This function supports both Windows 11 amd64 (x64) and arm64 installation media.
 
    .EXAMPLE
        Import-OSDeployOS
 
        Scans for mounted Windows installation media ISO files and presents a selection dialog
        to choose which Windows version(s) to import.
 
    .EXAMPLE
        Import-OSDeployOS -Verbose
 
        Imports Windows OS images with detailed verbose output showing each step of the
        extraction and import process.
 
    .INPUTS
        None
 
        This function does not accept pipeline input.
 
    .OUTPUTS
        [System.IO.DirectoryInfo]
 
        Returns the destination directory object for each imported image.
 
    .NOTES
        Author: David Segura
        Version: 0.1.0
 
        Prerequisites:
            - PowerShell 7.6 or higher
            - Windows 10 or higher
            - Run as Administrator
            - Windows installation media ISO mounted (via File Explorer or third-party tools)
    #>


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

    begin {
        Write-OSDeployBanner
        Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Start"
        Initialize-OSDeployCoreBootImage

        # Requires Run as Administrator
        $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
        if (-not $IsAdmin) {
            Write-Warning "[$($MyInvocation.MyCommand.Name)] This function must be Run as Administrator"
            return
        }

        $WindowsMediaImages = Get-MountedWindowsImageIndex -GridView Multiple
    }

    process {
        if ($null -eq $WindowsMediaImages) {
            Write-Warning "[$($MyInvocation.MyCommand.Name)] WindowsImage on Windows Installation Media was not found. Mount a Windows Installation ISO and try again."
            Write-OSDeployCoreProgress 'Windows 11 x64 Download: https://www.microsoft.com/en-us/software-download/windows11'
            Write-OSDeployCoreProgress 'Windows 11 arm64 Download: https://www.microsoft.com/en-us/software-download/windows11arm64'
            return
        }

        $WindowsOSRoot = Join-Path $Script:OSDeployCorePath 'cache' 'windows-os'
        $WindowsRERoot = Join-Path $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 "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] DestinationName: $DestinationName"

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

            # Check for duplicate import
            if (Test-Path -Path $DestinationDirectory) {
                Write-Warning "[$($MyInvocation.MyCommand.Name)] '$DestinationName' already exists in windows-os. Skipping duplicate import."
                continue
            }
            if (Test-Path -Path $ImportWinREDirectory) {
                Write-Warning "[$($MyInvocation.MyCommand.Name)] '$DestinationName' already exists in windows-re. Skipping duplicate import."
                continue
            }

            if (-not $PSCmdlet.ShouldProcess($DestinationName, 'Import Windows OS image from mounted media')) {
                continue
            }

            Write-OSDeployCoreProgress "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'

            New-Item -Path $DestinationCore -ItemType Directory -Force -ErrorAction Stop | Out-Null
            New-Item -Path $DestinationLogs -ItemType Directory -Force -ErrorAction Stop | Out-Null
            New-Item -Path $DestinationWim  -ItemType Directory -Force -ErrorAction Stop | Out-Null
            New-Item -Path $DestinationMedia -ItemType Directory -Force -ErrorAction Stop | 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-OSDeployCoreProgress 'Copying Windows media files ...'
            robocopy "$($SourceWindowsImage.MediaRoot)" "$DestinationMedia" *.* /e /xf install.wim install.esd | Out-Null
            Get-ChildItem -Recurse -Path "$DestinationMedia\*" |
                Set-ItemProperty -Name IsReadOnly -Value $false -ErrorAction SilentlyContinue | Out-Null

            # 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-OSDeployCoreProgress '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 = Join-Path $env:TEMP "OSDeployCore-Mount-$([Guid]::NewGuid().ToString('N').Substring(0, 8))"
            New-Item -Path $MountPath -ItemType Directory -Force | Out-Null

            Write-OSDeployCoreProgress 'Mounting Windows image (read-only) ...'
            try {
                Mount-WindowsImage -ImagePath $DestinationImagePath -Index 1 -Path $MountPath -ReadOnly -ErrorAction Stop | Out-Null
                $MountDirectory = $MountPath

                #region WinRE extraction
                Write-OSDeployCoreProgress 'Extracting WinRE ...'
                $winreSource = Join-Path $MountDirectory 'Windows\System32\Recovery\winre.wim'
                $reagentSource = Join-Path $MountDirectory 'Windows\System32\Recovery\ReAgent.xml'

                if (Test-Path $reagentSource) {
                    Copy-Item -Path $reagentSource -Destination (Join-Path $DestinationTemp 'os-reagent.xml') | Out-Null
                }
                if (Test-Path $winreSource) {
                    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'
                if (Test-Path $BootWim) {
                    Write-OSDeployCoreProgress '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-OSDeployCoreProgress '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
                }
                Rename-Item -Path (Join-Path $DestinationTemp 'SOFTWARE') -NewName 'os-software.hive' -Force -ErrorAction SilentlyContinue
                Rename-Item -Path (Join-Path $DestinationTemp 'SYSTEM') -NewName 'os-system.hive' -Force -ErrorAction SilentlyContinue
                #endregion

                #region Boot files
                $BootPath = Join-Path $MountDirectory 'Windows\Boot'
                if (Test-Path $BootPath) {
                    Write-OSDeployCoreProgress '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-OSDeployCoreProgress '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-OSDeployCoreProgress 'Extracting Ethernet drivers ...'
                $EthernetClientMums = Get-ChildItem -Path "$MountDirectory\Windows\servicing\Packages\Microsoft-Windows-Ethernet-Client-*.mum" -ErrorAction SilentlyContinue
                Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Ethernet .mum files found: $(($EthernetClientMums | Measure-Object).Count)"
                if ($EthernetClientMums) {
                    $EthernetDrivers = foreach ($MumFile in $EthernetClientMums) {
                        Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Parsing Ethernet .mum: $($MumFile.FullName)"
                        $MumXml = [xml](Get-Content -Path $MumFile.FullName -Raw)
                        $Identity = $MumXml.assembly.assemblyIdentity
                        $DriverInf = $MumXml.assembly.package.update.driver.inf
                        Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Ethernet Identity.name: $($Identity.name) | version: $($Identity.version) | arch: $($Identity.processorArchitecture) | inf: $DriverInf"
                        if ($Identity -and $DriverInf) {
                            [PSCustomObject]@{
                                Name         = $Identity.name -replace '^Microsoft-Windows-Ethernet-Client-', '' -replace '-FOD-Package$', ''
                                Version      = [version]$Identity.version
                                Architecture = $Identity.processorArchitecture
                                InfFile      = $DriverInf
                            }
                        } else {
                            Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Ethernet .mum skipped — Identity or DriverInf is null"
                        }
                    }
                    $EthernetDrivers = $EthernetDrivers |
                        Group-Object Name |
                        ForEach-Object { $_.Group | Sort-Object Version -Descending | Select-Object -First 1 }
                    Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Ethernet unique drivers after dedup: $(($EthernetDrivers | Measure-Object).Count)"

                    foreach ($Driver in $EthernetDrivers) {
                        Write-Host -ForegroundColor DarkGray "$($Driver.Name)-$($Driver.Version)"
                        $InfFileWithoutExtension = [IO.Path]::GetFileNameWithoutExtension($Driver.InfFile)
                        Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Ethernet driver: $($Driver.Name) v$($Driver.Version) arch=$($Driver.Architecture) inf=$($Driver.InfFile) infBase=$InfFileWithoutExtension"
                        $DriverFolderSearch = "$MountDirectory\Windows\System32\DriverStore\FileRepository\$InfFileWithoutExtension*"
                        Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Searching DriverStore: $DriverFolderSearch"
                        $DriverFolder = Get-ChildItem -Path $DriverFolderSearch -Directory -ErrorAction SilentlyContinue | Select-Object -First 1
                        if ($DriverFolder) {
                            Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Ethernet driver folder found: $($DriverFolder.FullName)"
                            $EthernetDst = Join-Path $Script:OSDeployCorePath "Repository\winpe-drivers\$($Driver.Architecture)\microsoft-windows-ethernet-$($Driver.Version)\$($Driver.Name)"
                            Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Ethernet destination: $EthernetDst"
                            if (Test-Path "$EthernetDst\*") {
                                Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Skipping existing Ethernet driver: $($Driver.Name)-$($Driver.Version)"
                            } else {
                                Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Copying Ethernet driver: $($Driver.Name)-$($Driver.Version)"
                                robocopy "$($DriverFolder.FullName)" "$EthernetDst" *.* /e /r:0 /w:0 /log+:"$RobocopyLog" | Out-Null
                            }
                        } else {
                            Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Ethernet driver folder NOT found for inf base: $InfFileWithoutExtension"
                        }
                    }
                } else {
                    Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] No Ethernet .mum files found in: $MountDirectory\Windows\servicing\Packages"
                }

                # Wi-Fi drivers
                Write-OSDeployCoreProgress 'Extracting Wi-Fi drivers ...'
                $WifiClientMums = Get-ChildItem -Path "$MountDirectory\Windows\servicing\Packages\Microsoft-Windows-Wifi-Client-*.mum" -ErrorAction SilentlyContinue
                Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Wi-Fi .mum files found: $(($WifiClientMums | Measure-Object).Count)"
                if ($WifiClientMums) {
                    $WifiDrivers = foreach ($MumFile in $WifiClientMums) {
                        Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Parsing Wi-Fi .mum: $($MumFile.FullName)"
                        $MumXml = [xml](Get-Content -Path $MumFile.FullName -Raw)
                        $Identity = $MumXml.assembly.assemblyIdentity
                        $DriverInf = $MumXml.assembly.package.update.driver.inf
                        Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Wi-Fi Identity.name: $($Identity.name) | version: $($Identity.version) | arch: $($Identity.processorArchitecture) | inf: $DriverInf"
                        if ($Identity -and $DriverInf) {
                            [PSCustomObject]@{
                                Name         = $Identity.name -replace '^Microsoft-Windows-Wifi-Client-', '' -replace '-FOD-Package$', ''
                                Version      = [version]$Identity.version
                                Architecture = $Identity.processorArchitecture
                                InfFile      = $DriverInf
                            }
                        } else {
                            Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Wi-Fi .mum skipped — Identity or DriverInf is null"
                        }
                    }
                    $WifiDrivers = $WifiDrivers |
                        Group-Object Name |
                        ForEach-Object { $_.Group | Sort-Object Version -Descending | Select-Object -First 1 }
                    Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Wi-Fi unique drivers after dedup: $(($WifiDrivers | Measure-Object).Count)"

                    foreach ($Driver in $WifiDrivers) {
                        Write-Host -ForegroundColor DarkGray "$($Driver.Name)-$($Driver.Version)"
                        $InfFileWithoutExtension = [IO.Path]::GetFileNameWithoutExtension($Driver.InfFile)
                        Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Wi-Fi driver: $($Driver.Name) v$($Driver.Version) arch=$($Driver.Architecture) inf=$($Driver.InfFile) infBase=$InfFileWithoutExtension"
                        $DriverFolderSearch = "$MountDirectory\Windows\System32\DriverStore\FileRepository\$InfFileWithoutExtension*"
                        Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Searching DriverStore: $DriverFolderSearch"
                        $DriverFolder = Get-ChildItem -Path $DriverFolderSearch -Directory -ErrorAction SilentlyContinue | Select-Object -First 1
                        if ($DriverFolder) {
                            Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Wi-Fi driver folder found: $($DriverFolder.FullName)"
                            $WifiDst = Join-Path $Script:OSDeployCorePath "Repository\winpe-drivers\$($Driver.Architecture)\microsoft-windows-wifi-$($Driver.Version)\$($Driver.Name)"
                            Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Wi-Fi destination: $WifiDst"
                            if (Test-Path "$WifiDst\*") {
                                Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Skipping existing Wi-Fi driver: $($Driver.Name)-$($Driver.Version)"
                            } else {
                                Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Copying Wi-Fi driver: $($Driver.Name)-$($Driver.Version)"
                                robocopy "$($DriverFolder.FullName)" "$WifiDst" *.* /e /r:0 /w:0 /log+:"$RobocopyLog" | Out-Null
                            }
                        } else {
                            Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Wi-Fi driver folder NOT found for inf base: $InfFileWithoutExtension"
                        }
                    }
                } else {
                    Write-Verbose "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] No Wi-Fi .mum files found in: $MountDirectory\Windows\servicing\Packages"
                }

                # 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
                }
                #>

                #endregion
            }
            finally {
                # Always dismount to avoid orphaned mounts
                if (Test-Path $MountPath) {
                    Write-OSDeployCoreProgress 'Dismounting Windows image ...'
                    Dismount-WindowsImage -Path $MountPath -Discard -ErrorAction SilentlyContinue | Out-Null
                    Remove-Item -Path $MountPath -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
                }
            }

            # Remove Read-Only from all files
            Get-ChildItem -Path $DestinationDirectory -File -Recurse -Force | ForEach-Object {
                Set-ItemProperty -Path $_.FullName -Name IsReadOnly -Value $false -Force -ErrorAction Ignore | Out-Null
            }

            #region Build the WinRE directory
            Write-OSDeployCoreProgress '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'
            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-OSDeployCoreProgress "Import complete: $DestinationName"
            Get-Item -Path $DestinationDirectory
        }
    }

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