+function Get-SetupVirtualMachine {
+ # The hypervisor this Windows is running under as a guest, by name, or $null
+ # on physical hardware.
+ #
+ # Read from the SMBIOS strings the firmware reports - Win32_ComputerSystem's
+ # Manufacturer and Model - which every common hypervisor fills in with its own
+ # name. NOT from HypervisorPresent: that is true on a PHYSICAL box too as soon
+ # as Hyper-V, WSL2, Windows Sandbox or virtualization-based security is on,
+ # which is most Windows 11 machines - the host OS then runs in the root
+ # partition, above a hypervisor, without being anyone's guest.
+ #
+ # A lookup that fails says "physical": this only ever takes steps away, and a
+ # step wrongly offered on a VM is one you can switch off in the plan.
+ $cs = Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue
+ if (-not $cs) { return $null }
+ $smbios = "$($cs.Manufacturer) | $($cs.Model)"
+ # Hyper-V's pair is matched whole: Microsoft is also the manufacturer of
+ # every Surface, and "Virtual Machine" is the model only in a guest. Azure
+ # guests report the same pair.
+ $known = [ordered]@{
+ 'innotek|VirtualBox' = 'VirtualBox'
+ 'VMware' = 'VMware'
+ '^Microsoft Corporation \| Virtual Machine$' = 'Hyper-V'
+ 'QEMU|KVM|Bochs' = 'QEMU/KVM'
+ 'Parallels' = 'Parallels'
+ '\bXen\b|HVM domU' = 'Xen'
+ 'Amazon EC2' = 'Amazon EC2'
+ 'Google Compute Engine' = 'Google Compute Engine'
+ }
+ foreach ($pattern in $known.Keys) {
+ if ($smbios -match $pattern) { return $known[$pattern] }
+ }
+ $null
+}
+