Public/Convert-Wim2VHD.ps1
function Convert-Wim2VHD { <# .SYNOPSIS Creates a VHD or VHDX and populates it from a WIM or ISO image. .DESCRIPTION Creates a VHD or VHDX file formatted for UEFI (Gen 2/GPT), BIOS (Gen 1/MBR), or Windows To Go (MBR). You must supply the path to the VHD/VHDX file and a valid WIM or ISO image. Optionally, specify the index number for the Windows edition to install. Additional options allow customization of disk layout, partition sizes, features, drivers, packages, and more. .PARAMETER Path The path to the new VHD or VHDX file. Must end in .vhd or .vhdx. .PARAMETER SourcePath The path to the WIM or ISO file used to populate the VHD(X). .PARAMETER DiskLayout Specifies the disk layout: BIOS (MBR), UEFI (GPT), or WindowsToGo (MBR). .PARAMETER Size The size of the VHD(X) in bytes. Default is 40GB. .PARAMETER Index The index of the image inside the WIM. Default is 1. .PARAMETER Dynamic If specified, creates a dynamic disk. .PARAMETER NoRecoveryTools If specified, skips creation of the recovery tools partition. .PARAMETER SystemSize Size of the system (boot loader) partition. .PARAMETER ReservedSize Size of the MS Reserved partition. .PARAMETER RecoverySize Size of the recovery tools partition. .PARAMETER Force If specified, overwrites existing files. .PARAMETER Unattend Path to an unattend.xml file to copy into the VHD(X). .PARAMETER NativeBoot If specified, prepares the VHD(X) for native boot. .PARAMETER Feature Features to enable (in DISM format). .PARAMETER RemoveFeature Features to remove (in DISM format). .PARAMETER FeatureSource Path to feature source files or folders. .PARAMETER FeatureSourceIndex Index for feature source WIM. Default is 1. .PARAMETER Driver Paths to drivers to inject. .PARAMETER AddPayloadForRemovedFeature If specified, adds payload for all removed features. .PARAMETER Package Paths to packages to install via DISM. .PARAMETER filesToInject Files or folders to copy to the root of the Windows drive. .PARAMETER UseDismExpansion If specified, uses DISM.exe for image expansion instead of native PowerShell. .EXAMPLE Convert-Wim2VHD -Path c:\windows8.vhdx -SourcePath d:\Source\install.wim -DiskLayout UEFI Creates a VHDX of the default size with GPT partitions for UEFI (Gen2). .EXAMPLE Convert-Wim2VHD -Path c:\windowsServer.vhdx -SourcePath d:\Source\install.wim -Index 3 -Size 40GB -Force -DiskLayout UEFI Creates a 40GB VHDX using index 3 with GPT partitions for UEFI (Gen2), overwriting any existing file. .EXAMPLE Convert-Wim2VHD -Path c:\win2go.vhd -SourcePath d:\Source\install.wim -DiskLayout WindowsToGo Creates a Windows To Go VHD image that can boot in UEFI or BIOS mode. .EXAMPLE Convert-Wim2VHD -Path c:\windows8.vhdx -SourcePath d:\Source\install.wim -DiskLayout UEFI -UseDismExpansion $true Creates a VHDX using DISM.exe for image expansion. This is required due to CrowdStrike blocking PowerShell from writing core files like SAM to the target disk. .NOTES Author: WindowsImageTools Team Requires: Administrator privileges #> [CmdletBinding(SupportsShouldProcess = $true, PositionalBinding = $false, ConfirmImpact = 'Medium')] Param ( # Path to the new VHDX file (Must end in .vhdx) [Parameter(Position = 0, Mandatory = $true, HelpMessage = 'Enter the path for the new VHDX file')] [ValidateNotNullOrEmpty()] [ValidatePattern(".\.vhdx?$")] [ValidateScript( { if (Get-FullFilePath -Path $_ | Split-Path | Resolve-Path ) { $true } else { Throw "Parent folder for $_ does not exist." } })] [string]$Path, # Size in Bytes (Default 40B) [ValidateRange(35GB, 64TB)] [long]$Size = 40GB, # Create Dynamic disk [switch]$Dynamic, # Specifies whether to build the image for BIOS (MBR), UEFI (GPT), or WindowsToGo (MBR). # Generation 1 VMs require BIOS (MBR) images. Generation 2 VMs require UEFI (GPT) images. # Windows To Go images will boot in UEFI or BIOS [Parameter(Mandatory = $true)] [Alias('Layout')] [string] [ValidateNotNullOrEmpty()] [ValidateSet('BIOS', 'UEFI', 'WindowsToGo')] $DiskLayout, # Skip the creation of the Recovery Environment Tools Partition. [switch]$NoRecoveryTools, # System (boot loader) Partition Size (Default : 260MB) [int]$SystemSize, # MS Reserved Partition Size (Default : 128MB) [int]$ReservedSize, # Recovery Tools Partition Size (Default : 905MB) [int]$RecoverySize, # Force the overwrite of existing files [switch]$force, # Path to WIM or ISO used to populate VHDX [parameter(Position = 1, Mandatory = $true, HelpMessage = 'Enter the path to the WIM/ISO file')] [ValidateScript( { Test-Path -Path (Get-FullFilePath -Path $_ ) })] [string]$SourcePath, # Index of image inside of WIM (Default 1) [int]$Index = 1, # Path to file to copy inside of VHD(X) as C:\unattend.xml [ValidateScript( { if ($_) { Test-Path -Path $_ } else { $true } })] [string]$Unattend, # Native Boot does not have the boot code inside the VHD(x) it must exist on the physical disk. [switch]$NativeBoot, # Features to turn on (in DISM format) [ValidateNotNullOrEmpty()] [string[]]$Feature, # Feature to remove (in DISM format) [ValidateNotNullOrEmpty()] [string[]]$RemoveFeature, # Feature Source path. If not provided, all ISO and WIM images in $sourcePath searched [ValidateNotNullOrEmpty()] [ValidateScript( { ($_ -eq 'NONE') -or (Test-Path -Path $(Resolve-Path $_)) })] [string]$FeatureSource, # Feature Source index. If the source is a .wim provide an index Default =1 [int]$FeatureSourceIndex = 1, # Path to drivers to inject [ValidateNotNullOrEmpty()] [ValidateScript( { foreach ($Path in $_) { Test-Path -Path $(Resolve-Path $Path) } })] [string[]]$Driver, # Add payload for all removed features [switch]$AddPayloadForRemovedFeature, # Path of packages to install via DISM [ValidateNotNullOrEmpty()] [ValidateScript( { foreach ($Path in $_) { Test-Path -Path $(Resolve-Path $Path) } })] [string[]]$Package, # Files/Folders to copy to root of Windows Drive (to place files in directories mimic the directory structure off of C:\) [ValidateNotNullOrEmpty()] [ValidateScript( { foreach ($Path in $_) { Test-Path -Path $(Resolve-Path $Path) } })] [string[]]$filesToInject, # Use DISM for expansion instead of native PowerShell [Parameter(HelpMessage = 'Use DISM for expansion instead of native PowerShell')] [switch]$UseDismExpansion ) $Path = $Path | Get-FullFilePath $SourcePath = $SourcePath | Get-FullFilePath #$VhdxFileName = Split-Path -Leaf -Path $Path if ($psCmdlet.ShouldProcess("[$($MyInvocation.MyCommand)] : Overwrite partitions inside [$Path] with content of [$SourcePath]", "Overwrite partitions inside [$Path] with content of [$SourcePath]? ", 'Overwrite WARNING!')) { if ((-not (Test-Path $Path)) -Or $force -Or $psCmdlet.ShouldContinue('Are you sure? Any existing data will be lost!', 'Warning')) { $ParametersToPass = @{ } foreach ($key in ('WhatIf', 'Verbose', 'Debug')) { if ($PSBoundParameters.ContainsKey($key)) { $ParametersToPass[$key] = $PSBoundParameters[$key] } } $InitializeVHDPartitionParam = @{ 'Size' = $Size 'Path' = $Path 'force' = $true 'DiskLayout' = $DiskLayout } if ($NoRecoveryTools) { $InitializeVHDPartitionParam.add('NoRecoveryTools', $true) } if ($Dynamic) { $InitializeVHDPartitionParam.add('Dynamic', $true) } if ($SystemSize) { $InitializeVHDPartitionParam.add('SystemSize', $SystemSize) } if ($ReservedSize) { $InitializeVHDPartitionParam.add('ReservedSize', $ReservedSize) } if ($RecoverySize) { $InitializeVHDPartitionParam.add('RecoverySize', $RecoverySize) } $SetVHDPartitionParam = @{ 'SourcePath' = $SourcePath 'Path' = $Path 'Index' = $Index 'force' = $true 'Confirm' = $false } if ($Unattend) { $SetVHDPartitionParam.add('Unattend', $Unattend) } if ($NativeBoot) { $SetVHDPartitionParam.add('NativeBoot', $NativeBoot) } if ($Feature) { $SetVHDPartitionParam.add('Feature', $Feature) } if ($RemoveFeature) { $SetVHDPartitionParam.add('RemoveFeature', $RemoveFeature) } if ($FeatureSource) { $SetVHDPartitionParam.add('FeatureSource', $FeatureSource) } if ($FeatureSourceIndex) { $SetVHDPartitionParam.add('FeatureSourceIndex', $FeatureSourceIndex) } if ($AddPayloadForRemovedFeature) { $SetVHDPartitionParam.add('AddPayloadForRemovedFeature', $AddPayloadForRemovedFeature) } if ($Driver) { $SetVHDPartitionParam.add('Driver', $Driver) } if ($Package) { $SetVHDPartitionParam.add('Package', $Package) } if ($filesToInject) { $SetVHDPartitionParam.add('filesToInject', $filesToInject) } if ($PSBoundParameters.ContainsKey('UseDismExpansion')) { $SetVHDPartitionParam.add('UseDismExpansion', $UseDismExpansion.IsPresent) } Write-Verbose -Message "[$($MyInvocation.MyCommand)] : InitializeVHDPartitionParam" Write-Verbose -Message ($InitializeVHDPartitionParam | Out-String) Write-Verbose -Message "[$($MyInvocation.MyCommand)] : SetVHDPartitionParam" Write-Verbose -Message ($SetVHDPartitionParam | Out-String) Write-Verbose -Message "[$($MyInvocation.MyCommand)] : ParametersToPass" Write-Verbose -Message ($ParametersToPass | Out-String) Try { Initialize-VHDPartition @InitializeVHDPartitionParam @ParametersToPass Set-VHDPartition @SetVHDPartitionParam @ParametersToPass } Catch { throw "$($_.Exception.Message) at $($_.Exception.InvocationInfo.ScriptLineNumber)" } } } } |