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