Public/Expand-DpWindowsImage.ps1

function Expand-DpWindowsImage {
<#
    .SYNOPSIS
    Applies a Windows image from a WIM file to a target directory using DISM.
 
    .DESCRIPTION
    Wrapper for DISM.exe to apply a specific image index from a WIM file to a target directory. Mimics Expand-WindowsImage. Optionally uses /Compact for compact OS deployment.
 
    .PARAMETER ImagePath
    Path to the WIM file containing the Windows image.
 
    .PARAMETER Index
    Index of the image inside the WIM to apply.
 
    .PARAMETER ApplyPath
    Target directory where the image will be applied.
 
    .PARAMETER Compact
    If specified, applies the image using the /Compact option for compact OS deployment.
 
    .EXAMPLE
    Expand-DpWindowsImage -ImagePath 'C:\images\install.wim' -Index 1 -ApplyPath 'D:\Windows'
 
    Applies the first image from install.wim to the D:\Windows directory using DISM.
 
    .EXAMPLE
    Expand-DpWindowsImage -ImagePath 'C:\images\install.wim' -Index 1 -ApplyPath 'D:\Windows' -Compact
 
    Applies the first image from install.wim to the D:\Windows directory using DISM with the /Compact option for compact OS deployment.
 
    .NOTES
    Author: WindowsImageTools Team
    Requires: Administrator privileges
#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [Parameter(Mandatory, Position = 0, HelpMessage = 'Path to the WIM file')]
        [ValidateNotNullOrEmpty()]
        [string]$ImagePath,

        [Parameter(Mandatory, Position = 1, HelpMessage = 'Index of the image inside the WIM')]
        [ValidateNotNullOrEmpty()]
        [int]$Index,

        [Parameter(Mandatory, Position = 2, HelpMessage = 'Target directory to apply the image')]
        [ValidateNotNullOrEmpty()]
        [string]$ApplyPath,

        [switch]$Compact
    )

    $ImagePath = Get-FullFilePath -Path $ImagePath
    $ApplyPath = Get-FullFilePath -Path $ApplyPath

    $dismArgs = @(
        '/Apply-Image',
        "/ImageFile:$ImagePath",
        "/Index:$Index",
        "/ApplyDir:$ApplyPath"
    )
    if ($Compact) {
        $dismArgs += '/Compact'
    }

    if ($PSCmdlet.ShouldProcess("Apply image index $Index from $ImagePath to $ApplyPath")) {
        $psi = New-Object System.Diagnostics.ProcessStartInfo
        $psi.FileName = 'dism.exe'
        $psi.Arguments = $dismArgs -join ' '
        $psi.RedirectStandardOutput = $true
        $psi.RedirectStandardError = $true
        $psi.UseShellExecute = $false
        $psi.CreateNoWindow = $true
        $process = [System.Diagnostics.Process]::Start($psi)
        $progressActivity = 'Expand Windows Image'
        $progressId = 0
        $lastPercent = 0
        while (-not $process.HasExited) {
            while ($null -ne ($line = $process.StandardOutput.ReadLine())) {
                if ($line -match '(\d{1,3}\.\d)%') {
                    $percent = [double]$Matches[1]
                    $lastPercent = $percent
                    Write-Progress -Id $progressId -Activity $progressActivity -Status "$percent% Complete" -PercentComplete $percent
                }
            }
            Start-Sleep -Milliseconds 100
        }
        # Read any remaining output after exit
        while ($null -ne ($line = $process.StandardOutput.ReadLine())) {
            if ($line -match '(\d{1,3}\.\d)%') {
                $percent = [double]$Matches[1]
                $lastPercent = $percent
                Write-Progress -Id $progressId -Activity $progressActivity -Status "$percent% Complete" -PercentComplete $percent
            }
        }
        # Always write 100% on exit
        Write-Progress -Id $progressId -Activity $progressActivity -Status '100% Complete' -PercentComplete 100 -Completed
        $stderr = $process.StandardError.ReadToEnd()
        if ($process.ExitCode -ne 0) {
            throw "DISM failed with exit code $($process.ExitCode): $stderr"
        }
    }
}