This script will clone a SysPrep'd VHD or VHDX to a new Hyper-V VM. It's designed to be shell (explorer) integrated so you can just right click on a VHD/VHDX file in explorer and then select the "Create VM" option as below:

This then gives the user interface constructed on the fly by PowerShell:

All we have to do at this point if we are happy with the defaults is to enter the new virtual machine name and either click the "Create" button or hit Alt C or enter. Note that the VHD/VHDX destination folder is pre-populated with my personal default so I don't have to type in or use the folder browser - this is done via the registry value we'll see later and we can set other defaults in there too if we wish. The name of the new VHD/VHDX file will be the machine name that you have entered above or with a "- <number>" suffix if you create more than one VM at a time. If the destination VHD/VHDX file already exists then an error will be shown unless the "-force" option is specified which will cause the VHD/VHDX to be overwritten so use it wisely.

Depending on your storage, the file copy for a non-linked clone may take a while but you'll find a PowerShell window behind the GUI with some rudiments of a progress indicator in there. You will also get a message box upon completion unless you specify the -quiet switch.

cloned ok

Also, if you don't logon as an administrator or run as an administrator but with UAC enabled (what I like to call the "sensible" option when running as an administrator (which I avoid as much as I can anyway)), the script will detect this and run itself again, but elevated, so you should expect to see a UAC elevation prompt. Without it elevated, it will probably fail due to lack of permission.

I've tested it just on Server 2012R2 English with PowerShell 4.0 and Windows 10 with 5.0. It won't work on Server 2008/2008 R2 as they don't have PowerShell support for Hyper-V (out of the box anyway).

It's quick to use to as it has been designed to be used via the keyboard so you can tab through the fields and hit "enter" to start the creation process at any point. There are also keyboard accelerators - activated by hitting the "Alt" key. Hitting the escape key will quit immediately.

Note that it won't work with AVHD/AVHDX files or snapshots as the disk cloning is done completely outside of Hyper-V - it just uses the Hyper-V PowerShell cmdlets to check if a VM of the name you entered exists already and if not to create a new VM, set the generation and number of processors and start it if requested.

To integrate it into explorer, simply run with the -install option, giving it an argument of the name of the right click menu item you want such as "Clone to VM". Specify any other options you want to be used as defaults when the menu option is picked, e.g.

& 'C:\Scripts\Clone VHD.ps1' -Install "Clone to VM" -Generation 2 -Memory 1024 -Folder c:\Hyper-V -LinkedClone -Start -Hide

The script can also be run without the GUI in a PowerShell session, e.g.:

& 'c:\scripts\Clone VHD.ps1' -SourceVHD "c:\folder\source.vhdx" -Name "My new VM" -Switch "External virtual switch" -Start -LinkedClone -Folder "d:\hyper-v\hyper-v hard drives"

where you need to change paths and virtual switches to match your system. Full help is available for it via Get-Help.

If you use linked clones, esnure that the parent disk does not change or get deleted otherwise your clones will fail.

PowerShell
Edit|Remove
Function CreateNew-VM 
{ 
    Param 
    ( 
        [string]$sourceVHD, 
        [string]$VMName , 
        [int]$Memory , 
        [int]$Processors , 
        [int]$numberOfVMs  , 
        [int]$generation , 
        [string]$vSwitch , 
        [string]$notes , 
        [string]$Folder , 
        [switch]$start , 
        [switch]$force , 
        [switch]$linkedClone , 
        [switch]$quiet 
    ) 
     
    Write-Verbose "Creating `"$VMName`" from `"$sourceVHD`" with $Processors CPUs and $Memory memory, switch $vSwitch in `"$Folder`" linkedClone $linkedClone start $start force $force number $numberOfVMs generation $generation" 
    [int]$status = 0 
    [void]$error.Clear() 
 
    ## Figure out what type of disk it is so we keep the same type 
    $extension = [System.IO.Path]::GetExtension( $sourceVHD ).ToLower() 
 
    if$extension -ne ".vhd" -and $extension -ne ".vhdx" ) 
    { 
        if! $quiet ) 
        { 
            [void][System.Windows.Forms.MessageBox]::Show("Unknown virtual disk format `"$extension`"" , 'Disk Cloning Error' , [System.Windows.Forms.MessageBoxButtons]::OK , [System.Windows.Forms.MessageBoxIcon]::Error ) 
        } 
        return 87 
    } 
 
    for$counter = 1 ; $counter -le $numberOfVMs ; $counter++ ) 
    { 
        $thisVMName = $VMName 
        if$numberOfVMs -gt 1 ) 
        { 
            $thisVMName +" - $($counter)" 
        } 
        $thisDisk = "$Folder\$thisVMName$extension" 
        $activity = "Creating VM #$counter/$numberOfVMs" 
 
        if( Get-VM -Name $thisVMName -EA SilentlyContinue ) 
        { 
            if! $quiet ) 
            { 
                [void][System.Windows.Forms.MessageBox]::Show("VM `"$thisVMName`" already exists" , 'Disk Cloning Error' , [System.Windows.Forms.MessageBoxButtons]::OK , [System.Windows.Forms.MessageBoxIcon]::Error ) 
            } 
            return 80 
        } 
        elseif( ( Test-Path -Path $thisDisk ) -And -Not $force ) 
        { 
            if! $quiet ) 
            { 
                [void][System.Windows.Forms.MessageBox]::Show("Disk `"$thisDisk`" already exists in destination"'Disk Cloning Error' , [System.Windows.Forms.MessageBoxButtons]::OK , [System.Windows.Forms.MessageBoxIcon]::Error ) 
            } 
            return 80 
        } 
        else 
        { 
            Write-Progress -id 1 -Activity $activity -Status "Copying disk" -PercentComplete 50 
            try 
            { 
                if$linkedClone ) 
                { 
                    $null = New-VHD -Path $thisDisk -ParentPath $sourceVHD -Differencing -EA Stop 
                } 
                else 
                { 
                    Copy-Item $sourceVHD $thisDisk -EA Stop 
                    ## May be read-only if source was so flick it off 
                    Set-ItemProperty -Path $thisDisk -Name IsReadOnly -Value $false 
                } 
            } 
            catch 
            { 
                $status = $? 
                if! $quiet ) 
                { 
                     [void][System.Windows.Forms.MessageBox]::Show("Failed to copy `"$sourceVHD`" to `"$thisDisk`"`n$($error[0].Exception.Message)"'Disk Cloning Error' , [System.Windows.Forms.MessageBoxButtons]::OK , [System.Windows.Forms.MessageBoxIcon]::Error ) 
                } 
                return $status 
            } 
 
            Write-Progress -id 1 -Activity $activity -Status 'Creating VM'   -PercentComplete 75 
            ## Now create the VM 
            $switchargs = @{} 
 
            if! [String]::IsNullOrEmpty($vSwitch-And $vSwitch -ne '<None>' ) 
            { 
                $switchargs = @{ 'switch' = "$vswitch" } 
            } 
            if( ( $vm = New-VM -Name $thisVMName -VHDPath $thisDisk -MemoryStartupBytes ($memory * 1024 * 1024) -Generation $generation @switchargs)) 
            { 
                if$Processors -gt 1 ) 
                { 
                    Write-Progress -id 1 -Activity $activity -Status 'Adding processors' -PercentComplete 85 
                    if! ( $vm | Set-VM -ProcessorCount $Processors ) -And ( (Get-VM -Name $thisVMName).ProcessorCount -ne $Processors )) 
                    { 
                        $status = $? 
                        $failed = $true 
                        if! $quiet ) 
                        { 
                            [void][System.Windows.Forms.MessageBox]::Show("Failed to set processors to $Processors`n$($error[0].Exception.Message)"'Disk Cloning Error' , [System.Windows.Forms.MessageBoxButtons]::OK , [System.Windows.Forms.MessageBoxIcon]::Error ) 
                        } 
                    } 
                } 
                if! [string]::IsNullOrEmpty( $notes ) ) 
                { 
                    Write-Progress -id 1 -Activity $activity -Status 'Adding notes' -PercentComplete 90 
                    if! ( $vm | Set-VM -Notes $notes ) -And ( (Get-VM -Name $thisVMName).Notes -ne $notes )) 
                    { 
                        $status = $? 
                        $failed = $true 
                        if! $quiet ) 
                        { 
                            [void][System.Windows.Forms.MessageBox]::Show("Failed to set notes to `"$notes`"`n$($error[0].Exception.Message)"'Disk Cloning Error' , [System.Windows.Forms.MessageBoxButtons]::OK , [System.Windows.Forms.MessageBoxIcon]::Error ) 
                        } 
                    } 
                } 
                if! $failed ) 
                { 
                    if$start ) 
                    { 
                        Write-Progress -id 1 -Activity $activity -Status 'Starting VM' -PercentComplete 95 
                        if! ( Start-VM -Name $thisVMName ) -And ( (Get-VM -Name $thisVMName).State -eq 'Off' ) ) 
                        { 
                            $status = $? 
                            if! $quiet ) 
                            { 
                              [void][System.Windows.Forms.MessageBox]::Show("Failed to start `"$thisVMName`"`n$($error[0].Exception.Message)"'Disk Cloning Error' , [System.Windows.Forms.MessageBoxButtons]::OK , [System.Windows.Forms.MessageBoxIcon]::Error ) 
                            } 
                        } 
                    } 
                    Write-Progress -id 1 -Activity $activity -Status 'VM created' -PercentComplete 100 
                    if! $quiet ) 
                    { 
                        [void][System.Windows.Forms.MessageBox]::Show("Disk $counter/$numberOfVMs cloned to VM OK""Disk Cloning" , [System.Windows.Forms.MessageBoxButtons]::OK , [System.Windows.Forms.MessageBoxIcon]::Information ) 
                    } 
                } 
            } 
            else 
            { 
                $status = $? 
                if! $quiet ) 
                { 
                     [void][System.Windows.Forms.MessageBox]::Show("Failed to create `"$thisVMName`"`n$($error[0].Exception.Message)"'Disk Cloning Error' , [System.Windows.Forms.MessageBoxButtons]::OK , [System.Windows.Forms.MessageBoxIcon]::Error ) 
                } 
            } 
        } 
    } 
    ## Need to return status so caller can take action if required 
    return $status 
}