Skip to content

Instantly share code, notes, and snippets.

@milolav
Created January 17, 2020 16:25
Show Gist options
  • Save milolav/3e296ed6a9f8a6c431a8553060f7514b to your computer and use it in GitHub Desktop.
Save milolav/3e296ed6a9f8a6c431a8553060f7514b to your computer and use it in GitHub Desktop.
# 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