private/Tests/Test-IsVM.ps1

function Test-IsVM {
    <#
    .SYNOPSIS
        Returns $true when the operating system is running inside any hypervisor
        (Hyper-V guest, VMware, VirtualBox, or QEMU/KVM).
 
    .DESCRIPTION
        Queries Win32_ComputerSystem and Win32_BIOS via CIM to detect the most
        common hypervisor platforms. Returns $false on bare-metal or when CIM
        data is unavailable.
 
        Used to prevent enabling Hyper-V when the host OS is itself a virtual
        machine (nested virtualisation is not universally supported).
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Hypervisors detected:
          - Microsoft Hyper-V guest (Manufacturer: 'Microsoft Corporation', Model: 'Virtual Machine')
          - VMware (Manufacturer: 'VMware*')
          - Oracle VirtualBox (Manufacturer: 'innotek GmbH' | Model: 'VirtualBox')
          - QEMU / KVM (Manufacturer: 'QEMU*' | BIOS: 'BOCHS' | BIOS: 'QEMU')
          - BIOS version strings ('VRTUAL', 'VMWARE', 'VBOX')
 
    Dependencies: None
    #>

    [OutputType([bool])]
    param ()

    $cs   = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction SilentlyContinue
    $bios = Get-CimInstance -ClassName Win32_BIOS           -ErrorAction SilentlyContinue

    if (-not $cs) { return $false }

    # Hyper-V guest
    if ($cs.Manufacturer -eq 'Microsoft Corporation' -and $cs.Model -eq 'Virtual Machine') {
        return $true
    }

    # VMware
    if ($cs.Manufacturer -like 'VMware*') {
        return $true
    }

    # Oracle VirtualBox
    if ($cs.Manufacturer -eq 'innotek GmbH' -or $cs.Model -eq 'VirtualBox') {
        return $true
    }

    # QEMU / KVM
    if ($cs.Manufacturer -like 'QEMU*') {
        return $true
    }

    # BIOS version strings (covers edge cases and some cloud VMs)
    if ($bios) {
        $biosVersion = [string]$bios.SMBIOSBIOSVersion
        foreach ($marker in @('VRTUAL', 'VMWARE', 'VBOX', 'BOCHS', 'QEMU')) {
            if ($biosVersion -like "*$marker*") {
                return $true
            }
        }
    }

    return $false
}