Created
January 17, 2020 16:25
-
-
Save milolav/3e296ed6a9f8a6c431a8553060f7514b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright (c) milolav | |
# MIT License | |
param ( | |
[string]$WinIso, | |
[string]$VhdFile, | |
[string]$WinEdition = "Windows 10 Enterprise", | |
[long]$VhdSize = 40GB, | |
[char]$EfiLetter = 'R', | |
[char]$VirtualWinLetter = 'W', | |
[string]$UnattendFile = "unattend.xml" | |
) | |
#stop on errors | |
$ErrorActionPreference = 'Stop' | |
#dism and diskpart require elevated privileges | |
if(-not [bool]([Security.Principal.WindowsIdentity]::GetCurrent().Groups -contains 'S-1-5-32-544')) { | |
Write-Error "Script must be started as an Administrator" | |
} | |
if($UnattendFile -and $UnattendFile -ne $false -and -not (Test-Path $UnattendFile)) { | |
Write-Error "Unattend file is set to '$UnattendFile' but the file does not exists." | |
} | |
#region Iso mounting | |
$WinIso = Resolve-Path $WinIso | |
Write-Progress "Mounting $WinIso ..." | |
$MountResult = Mount-DiskImage -ImagePath $WinIso -StorageType ISO -PassThru | |
$DriveLetter = ($MountResult | Get-Volume).DriveLetter | |
if (-not $DriveLetter) { | |
Write-Error "ISO file not loaded correctly" -ErrorAction Continue | |
Dismount-DiskImage -ImagePath $WinIso | Out-Null | |
return | |
} | |
Write-Progress "Mounting $WinIso ... Done" | |
#endregion | |
#region Enumerating installation images | |
$WimFile = "$($DriveLetter):\sources\install.wim" | |
Write-Host "Inspecting $WimFile" | |
$WimOutput = dism /get-wiminfo /wimfile:`"$WimFile`" | Out-String | |
$WimInfo = $WimOutput | Select-String "(?smi)Index : (?<Id>\d+).*?Name : (?<Name>[^`r`n]+)" -AllMatches | |
if (!$WimInfo.Matches) { | |
Write-Error "Images not found in install.wim`r`n$WimOutput" -ErrorAction Continue | |
Dismount-DiskImage -ImagePath $WinIso | Out-Null | |
return | |
} | |
$Items = @{ } | |
$Menu = "" | |
$DefaultIndex = 1 | |
$WimInfo.Matches | ForEach-Object { | |
$Items.Add([int]$_.Groups["Id"].Value, $_.Groups["Name"].Value) | |
$Menu += $_.Groups["Id"].Value + ") " + $_.Groups["Name"].Value + "`r`n" | |
if ($_.Groups["Name"].Value -eq $WinEdition) { | |
$DefaultIndex = [int]$_.Groups["Id"].Value | |
} | |
} | |
Write-Output $Menu | |
do { | |
try { | |
$err = $false | |
$WimIdx = if (([int]$val = Read-Host "Please select version [$DefaultIndex]") -eq "") { $DefaultIndex } else { $val } | |
if (-not $Items.ContainsKey($WimIdx)) { $err = $true } | |
} | |
catch { | |
$err = $true; | |
} | |
} while ($err) | |
Write-Output $Items[$WimIdx] | |
#endregion | |
#region Preparing VHD | |
If (!$VhdFile) { | |
$VhdFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Items[$WimIdx] + ".vhdx") | |
}else{ | |
$VhdFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($VhdFile) | |
} | |
if (Test-Path $VhdFile) { | |
Write-Error "Vhd file '$VhdFile' allready exists." -ErrorAction Continue | |
Dismount-DiskImage -ImagePath $WinIso | Out-Null | |
return | |
} | |
$Disk = New-VHD $VhdFile $VhdSize | |
Mount-DiskImage -ImagePath $VhdFile | |
$DiskNumber = (Get-DiskImage -ImagePath $VhdFile | Get-Disk).Number | |
#make sure that selected disk is not first (probably main) disk | |
if ($DiskNumber -eq 0) { | |
Write-Error "Unexpected disk number" -ErrorAction Continue | |
Dismount-DiskImage -ImagePath $VhdFile | Out-Null -ErrorAction Continue | |
Dismount-DiskImage -ImagePath $WinIso | Out-Null -ErrorAction Continue | |
return | |
} | |
#disk confirmation, just in case | |
$opt = $host.UI.PromptForChoice("Please confirm mounted disk ImagePath above" , "" , [System.Management.Automation.Host.ChoiceDescription[]] @("&Continue", "&Quit"), 1) | |
if ($opt -eq 1) { | |
Dismount-DiskImage -ImagePath $VhdFile | Out-Null -ErrorAction Continue | |
Dismount-DiskImage -ImagePath $WinIso | Out-Null -ErrorAction Continue | |
return | |
} | |
#partition and format new vhd | |
Write-Host "Partitioning and formating vhd" | |
@" | |
select disk $DiskNumber | |
convert gpt | |
select partition 1 | |
delete partition override | |
create partition primary size=300 | |
format quick fs=ntfs | |
create partition efi size=100 | |
format quick fs=fat32 | |
assign letter="$EfiLetter" | |
create partition msr size=128 | |
create partition primary | |
format quick fs=ntfs | |
assign letter="$VirtualWinLetter" | |
exit | |
"@ | diskpart | Out-Null | |
#endregion | |
#region Installation | |
#apply image using dism | |
Write-Host "Installing windows" | |
Invoke-Expression "dism /apply-image /imagefile:`"$WimFile`" /index:$WimIdx /applydir:$($VirtualWinLetter):\" | |
Write-Host "" | |
Invoke-Expression "$($VirtualWinLetter):\windows\system32\bcdboot $($VirtualWinLetter):\windows /f uefi /s $($EfiLetter):" | |
Write-Host "" | |
Invoke-Expression "bcdedit /store $($EfiLetter):\EFI\Microsoft\Boot\BCD" | |
Write-Host "" | |
if($UnattendFile) { | |
Write-Host "Copying unattended.xml" | |
New-Item -ItemType "directory" -Path "$($VirtualWinLetter):\Windows\Panther\" | Out-Null | |
Copy-Item $UnattendFile "$($VirtualWinLetter):\Windows\Panther\unattend.xml" | Out-Null | |
} | |
#endregion | |
#region Cleanup | |
Write-Host "`r`nCleaning up" | |
@" | |
select disk $disknumber | |
select partition 2 | |
remove letter="$EfiLetter" | |
select partition 4 | |
remove letter="$VirtualWinLetter" | |
exit | |
"@ | diskpart | Out-Null | |
Dismount-DiskImage -ImagePath $VhdFile | Out-Null | |
Dismount-DiskImage -ImagePath $WinIso | Out-Null | |
#endregion | |
Write-Host "`r`nDone" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment