public/New-LablyBaseVHD.ps1

Function New-LablyBaseVHD {

    <#
     
    .SYNOPSIS
 
    Creates a new Base VHD file for use in creating new VMs.
 
    .DESCRIPTION
 
    This function is used to creates a new Base VHD file for use in creating new VMs. It is not tied to any specific lab, any lab can use any of the Base VHDs. Once the VHD is created, the Register-LablyBaseVHD should be used to add it to the image registry.
 
    .PARAMETER ISO
     
    Full or relative path to the ISO that has the Operating System installer on it.
 
    .PARAMETER VHD
 
    Full or relative path to where the Base VHD should be created.
 
    .PARAMETER VHDSizeInBytes
 
    Optional size of the Base VHD. As the VHD expands to fit the content and doesn't use the full space unless necessary, there is unlikely to be a value in setting this parameter. Defaults to 127GB.
 
    .PARAMETER Index
 
    Index number of the Operating System to create the Base VHD for. Default to 1. If you're unsure of the Index Number, use Get-LablyISODetails to identify it.
 
    .PARAMETER Force
 
    Switch that defines that the VHD should be overwritten if the file exists.
 
    .INPUTS
 
    None. You cannot pipe objects to New-LablyBaseVHD.
 
    .OUTPUTS
 
    None. The function will either complete successfully or throw an error.
     
    .EXAMPLE
 
    New-LablyBaseVHD -ISO C:\ISOs\Windows10-Enterprise.iso -Index 4 -VHD C:\VHDRepo\Win10Ent.vhdx
 
    #>


    [CmdLetBinding(DefaultParameterSetName='OSByIndex')]
    Param(
        [Parameter(Mandatory=$True,Position=0)]
        [String]$ISO,

        [Parameter(Mandatory=$True,Position=1)]
        [String]$VHD,

        [Parameter(Mandatory=$False)]
        [Int64]$VHDSizeInBytes = 127GB,

        [Parameter(Mandatory=$False)]
        [Int]$Index = 1,

        [Parameter(Mandatory=$False)]
        [Switch]$Force
    )

    ValidateModuleRun -RequiresAdministrator

    # Credit: Help with this function from: https://github.com/greyhamwoohoo/new-vm-from-iso

    $gptTypeEFI = "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}"
    $gptTypeMSR = "{e3c9e316-0b5c-4db8-817d-f92df00215ae}"

    Write-Host "Creating new VHDx from $ISO Index $Index"

    If($(Test-Path $VHD) -and $($Force -eq $False)) {
        Throw "File $VHD Already Exists. Use -Force to Overwrite."
    }

    Try {
        Write-Verbose "Mounting $ISO"
        $mnt = Mount-DiskImage -ImagePath $ISO 
        $mntVolume = $($mnt | Get-Volume).DriveLetter
    } Catch {
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Throw "Could not mount ISO. $($_.Exception.Message)"
    }
    
    Write-Verbose "Mounted $ISO to $mntVolume"

    $WIM = "$mntVolume`:\Sources\Install.wim"

    Write-Verbose "Looking for $WIM"
    If(-Not(Test-Path $WIM)) {
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Throw "Unable to find \Source\Install.wim in ISO $ISO"
    }

    Try {
        $DiskImages = Get-WindowsImage -ImagePath $WIM
        $ImageIndexes = $DiskImages | Select-Object -ExpandProperty ImageIndex
        If($Index -notin $ImageIndexes) {
            Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
            Throw "Image Index #$Index is not in the range of valid indexes in WIM."    
        }
    } Catch {
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Throw "Unable get Windows Images from $WIM. $($_.Exception.Message)"
    }

    If(Test-Path $VHD) {
        Try {
            Remove-Item $VHD -Force
        } Catch {
            Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
            Throw "Unable to overwrite $VHD. $($_.Exception.Message)"
        }
    }

    Try {
        Write-Verbose "Creating $VHD"
        $DiskFile = New-VHD -Path $VHD -SizeBytes $VHDSizeInBytes -Dynamic -ErrorAction Stop
    } Catch {
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Throw "Unable to Create VHD. $($_.Exception.Message)"
    }

    Try {
        Write-Verbose "Mounting $VHD"
        $vhdMount = Mount-Vhd -Path $DiskFile.Path -Passthru -ErrorAction Stop
    } Catch {
        Dismount-Vhd -Path $VHD -ErrorAction SilentlyContinue
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Remove-Item $VHD -ErrorAction SilentlyContinue
        Throw "Unable to Mount VHD $($_.Exception.Message)"
    }

    Try {
        Write-Verbose "Getting Disk of $VHD"
        $vhdDisk = $vhdMount | Get-Disk
    } Catch {
        Dismount-Vhd -Path $VHD -ErrorAction SilentlyContinue
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Remove-Item $VHD -ErrorAction SilentlyContinue
        Throw "Unable to Get Disk from VHD $($_.Exception.Message)"
    }

    Try {
        Write-Verbose "Initializing VHD (Disk Number $($vhdDisk.DiskNumber))"
        Initialize-Disk -Number $vhdDisk.DiskNumber -PartitionStyle GPT -Passthru -ErrorAction Stop | Out-Null
    } Catch {
        Dismount-Vhd -Path $VHD -ErrorAction SilentlyContinue
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Remove-Item $VHD -ErrorAction SilentlyContinue
        Throw "Unable to Initialize VHD. $($_.Exception.Message)"
    }

    Try {
        Write-Verbose "Creating EFI Partition on $VHD"
        $diskPartSystem = New-Partition -DiskNumber $vhdDisk.DiskNumber -Size 100MB -GptType $gptTypeEFI -AssignDriveLetter -ErrorAction Stop

        Write-Verbose "Creating Microsoft Reserved Partition on $VHD"
        New-Partition -DiskNumber $vhdDisk.DiskNumber -Size 128MB -GptType $gptTypeMSR -ErrorAction Stop | Out-Null

        Write-Verbose "Creating Primary Partitions on $VHD"
        $diskPartPrimary = New-Partition -DiskNumber $vhdDisk.DiskNumber -UseMaximumSize -AssignDriveLetter -ErrorAction Stop
    } Catch {
        Dismount-Vhd -Path $VHD -ErrorAction SilentlyContinue
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Remove-Item $VHD -ErrorAction SilentlyContinue
        Throw "Unable to Create Partition. $($_.Exception.Message)"
    }

    Try {
        Write-Verbose "Formatting System Partition $($diskPartSystem.DriveLetter):"
        Format-Volume -DriveLetter $($DiskPartSystem.DriveLetter) -FileSystem FAT32 -NewFileSystemLabel "System" -ErrorAction Stop | Out-Null
        Write-Verbose "Formatting Primary Partition $($diskPartPrimary.DriveLetter):"
        Format-Volume -DriveLetter $($diskPartPrimary.DriveLetter) -FileSystem NTFS -NewFileSystemLabel "OSDisk" -ErrorAction Stop | Out-Null
    } Catch {
        Dismount-Vhd -Path $VHD -ErrorAction SilentlyContinue
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Remove-Item $VHD -ErrorAction SilentlyContinue
        Throw "Unable to Format Volume. $($_.Exception.Message)"
    }

    Try {
        Write-Host "Expanding Index $index of $WIM to $($diskPartPrimary.DriveLetter):"
        Expand-Windowsimage -ImagePath $WIM -ApplyPath "$($diskPartPrimary.DriveLetter):\" -Index $index -ErrorAction Stop | Out-Null
    } Catch {
        Dismount-Vhd -Path $VHD -ErrorAction SilentlyContinue
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Remove-Item $VHD -ErrorAction SilentlyContinue
        Throw "Unable to Expand WIM. $($_.Exception.Message)"
    }

    Try {
        Write-Verbose "Making Disk Bootable with OS Drive $($diskPartPrimary.DriveLetter): and System Drive $($diskPartSystem.DriveLetter):"
        Start-Process bcdboot.exe -ArgumentList @("$($diskPartPrimary.DriveLetter):\Windows", "/s $($diskPartSystem.DriveLetter):", "/f UEFI") -WorkingDirectory "$($diskPartPrimary.DriveLetter):\Windows\System32" -WindowStyle Hidden -Wait
    } Catch {
        Dismount-Vhd -Path $VHD -ErrorAction SilentlyContinue
        Dismount-DiskImage -ImagePath $ISO -ErrorAction SilentlyContinue | Out-Null
        Remove-Item $VHD -ErrorAction SilentlyContinue
        Throw "Unable to Make Disk Bootable. $($_.Exception.Message)"
    }

    Write-Host "Cleaning up..."

    Try {
        Dismount-Vhd -Path $VHD -ErrorAction Stop
    } Catch {
        Write-Warning "Could not dismount $VHD, you'll need to manually dismount or reboot before using. $($_.Exception.Message)"
    }

    Try {
        Dismount-DiskImage -ImagePath $ISO -ErrorAction Stop | Out-Null
    } Catch {
        Write-Warning "Unable to dismount ISO image. This will automatically dismount at reboot or you can try and do so manually."
    }

    Write-Host "Awesome! You've got yourself a new base image at the following path. You can use this to build VMs!" -ForegroundColor Green
    Write-Host "You should register this Base VHD with Lably using: " -NoNewline
    Write-Host "Register-LablyBaseVHD -VHD $VHD" -ForegroundColor Gray

}