Agent
...
Service v1
Hypervisor Discovery
3 min
description attempts to determine if a devices "virtualization type" 1 = physical machine (no virtualization detected) 2 = virtual machine guest (based on system/bios strings) 3 = physical host with hypervisor enabled (e g , vbs, wsl2, but hyper v role not detected) 4 = physical host with hyper v configured (hyper v role/service 'vmms' detected) 5 = physical host with other virtualization software detected (e g , vmware workstation/player, virtualbox services/processes/install found) frequency run daily or on manual full pull powershell \#requires modules cimcmdlets, microsoft powershell management, microsoft powershell utility <# synopsis determines the virtualization status of the machine (physical, guest, hyper v host, other host) description this script checks various system properties using wmi/cim, services, processes, and registry entries to classify the machine into one of five categories 1 = physical machine (no virtualization detected) 2 = virtual machine guest (based on system/bios strings) 3 = physical host with hypervisor enabled (e g , vbs, wsl2, but hyper v role not detected) 4 = physical host with hyper v configured (hyper v role/service 'vmms' detected) 5 = physical host with other virtualization software detected (e g , vmware workstation/player, virtualbox services/processes/install found) the script outputs a single integer representing the detected status notes requires powershell v3+, administrator privileges to query wmi/services/registry comprehensively run context designed to run locally on the target machine detection methods uses hypervisorpresent flag, hyper v 'vmms' service check, guest vm string indicators, and an exhaustive check for common type 2 hypervisors (vmware workstation/player, virtualbox, parallels) via services, processes, installed software registry keys, and default folders \#> \# configuration \# set $verbosepreference = "continue" to see detailed step by step logging \# $verbosepreference = "silentlycontinue" # default \# function definitions \# define a function to check for common type 2 hypervisor indicators function test othervirtualizationsoftware { <# synopsis checks for indicators of common type 2 virtualization software installation or activity description performs a reasonably exhaustive check for vmware workstation/player, oracle virtualbox, and parallels by looking for associated services, processes, registry entries (installed software), and default installation folders returns $true if any indicator is found, $false otherwise notes this function checks multiple sources and may take slightly longer to execute than simple checks it uses erroraction silentlycontinue extensively, as missing items are expected \#> write verbose "starting check for other virtualization software " \# 1 check running services \# write verbose "checking known services " $vmwareservicenames = @( "vmauthdservice", # vmware authorization service "vmnetdhcp", # vmware dhcp service "vmusbarbservice", # vmware usb arbitration service "vstor2 ws ", # vmware storage driver service (use wildcard with name, requires psv6+ or filter) \# for broader compatibility, check specific known versions or rely on display name / other methods "vmware hostd" # sometimes present ) $vmwaredisplaynamespatterns = @( "vmware nat service", "vmware workstation server" \# "vstor2 ws" # check display name prefix too if needed ) $vboxservicenames = @( "vboxservice", # oracle vm virtualbox service "vboxdrv" # virtualbox driver service (older?) ) $parallelsservicenames = @( "parallels service", # parallels service "parallels client support", # parallels tools related? "prl disp service", # parallels display service "prl hostd" # parallels host service ) \# check by specific service name foreach ($servicename in ($vmwareservicenames + $vboxservicenames + $parallelsservicenames)) { \# handle potential wildcard for vstor2 if on psv6+ (otherwise it might fail) \# simpler approach check without wildcard first, rely on other methods if missed if ($servicename ne "vstor2 ws " and ($null ne (get service name $servicename erroraction silentlycontinue))) { write verbose "found service by name $servicename" return $true } \# add specific checks for known vstor2 versions if needed, e g \# if ($null ne (get service name "vstor2 ws60" erroraction silentlycontinue)) { return $true } } \# check by specific display name pattern foreach ($pattern in $vmwaredisplaynamespatterns) { if ($null ne (get service displayname $pattern erroraction silentlycontinue)) { write verbose "found service by display name $pattern" return $true } } \# 2 check running processes \# write verbose "checking known processes " $processnames = @( "vmware", # main vmware workstation/player ui process "vmplayer", # vmware player ui process "vmware authd", # vmware auth service process "vmware tray", # vmware tray icon "virtualbox", # main virtualbox ui/manager process "vboxsvc", # virtualbox service process "vboxmanage", # virtualbox command line tool (if running) "prl client app", # parallels client "prl disp service" # parallels display service process ) \# use try/catch as get process throws terminating error if any name isn't found when list is passed try { if ($null ne (get process name $processnames erroraction stop)) { write verbose "found running process from list $($processnames join ', ')" return $true } } catch { \# expected error if none of the processes are running write verbose "no target processes found running (or error checking $($ exception message)) " } \# 3 check installed software (registry uninstall keys) \# write verbose "checking installed software via registry " $uninstallpaths = @( 'hklm \software\microsoft\windows\currentversion\uninstall', 'hklm \software\wow6432node\microsoft\windows\currentversion\uninstall' # for 32 bit apps on 64 bit os ) $softwarekeywords = @( "vmware workstation", "vmware player", "vmware vix", # often installed alongside workstation "vmware ovf tool", # often installed alongside workstation "oracle vm virtualbox", \#"virtualbox guest additions", # unlikely on host, commented out "parallels tools", # might be installed on windows even if not hosting "parallels desktop" ) foreach ($path in $uninstallpaths) { \# check if path exists before querying if (test path $path) { \# get subkey names, then get displayname property from each subkey $subkeys = get childitem path $path erroraction silentlycontinue if ($subkeys) { foreach ($key in $subkeys) { $appname = $key getvalue("displayname") $apppublisher = $key getvalue("publisher") # optional could check publisher too if ($appname) { # ensure appname is not null/empty foreach ($keyword in $softwarekeywords) { \# use match for case insensitive substring match if ($appname match \[regex] escape($keyword)) { write verbose "found installed software matching keyword '$keyword' $appname" return $true } } } } } } else { write verbose "registry path not found $path" } } \# 4 check default installation folders \# write verbose "checking default installation folders " \# use get folderpath if available (psv6+), otherwise fallback to environment variables $programfiles = $env\ programfiles $programfilesx86 = $env\ programfilesx86 $folderstocheck = @( "$programfiles\oracle\virtualbox", "$programfiles\vmware\vmware workstation", "$programfiles\vmware\vmware player", "$programfiles\parallels\parallels desktop", \# also check x86 folders if they exist and are different "$programfilesx86\oracle\virtualbox", "$programfilesx86\vmware\vmware workstation", "$programfilesx86\vmware\vmware player", "$programfilesx86\parallels\parallels desktop" ) | where object {$ } | get unique # filter out nulls and get unique paths foreach ($folder in $folderstocheck) { if (test path path $folder pathtype container erroraction silentlycontinue) { write verbose "found default installation folder $folder" return $true } } \# if none of the above checks found anything write verbose "no indicators of other virtualization software found " return $false } \# function to check for virtualization indicators in system/bios strings (primarily for guest detection) function get virtualizationindicator { param( \[parameter(mandatory = $true)] $computerobject, \[parameter(mandatory = $true)] $biosobject ) write verbose "checking system/bios strings for vm guest indicators " \# specific check for hyper v guests if ($computerobject manufacturer match "microsoft corporation" and $computerobject model eq "virtual machine") { write verbose "detected microsoft virtual machine indicator " return $true } \# general keywords $virtkeywords = @("virtual", "vmware", "virtualbox", "hyper v", "kvm", "xen", "parallels", "qemu") foreach ($kw in $virtkeywords) { \# use match for case insensitive substring matching if ( ($computerobject model match $kw) or ($computerobject manufacturer match $kw) or ($biosobject manufacturer match $kw) or \# join biosversion array elements (if it's an array) before matching (($biosobject biosversion join " ") match $kw) ) { write verbose "found vm indicator keyword '$kw' in system/bios strings " return $true } } write verbose "no vm guest indicators found in system/bios strings " return $false } \# main script logic \# output codes definition (for reference) \# 1 = physical machine (no virtualization detected) \# 2 = virtual machine guest (based on system/bios strings) \# 3 = physical host with hypervisor enabled (e g , vbs, wsl2, but hyper v role not detected) \# 4 = physical host with hyper v configured (hyper v role/service 'vmms' detected) \# 5 = physical host with other virtualization software detected (e g , vmware workstation/player, virtualbox services/processes/install found) write verbose "gathering system information via wmi/cim " $computer = get ciminstance win32 computersystem erroraction silentlycontinue $bios = get ciminstance win32 bios erroraction silentlycontinue $cpu = get ciminstance win32 processor erroraction silentlycontinue \# handle potential wmi query failures if ( not $computer or not $bios or not $cpu) { write error "failed to retrieve essential wmi data (win32 computersystem, win32 bios, win32 processor) cannot determine status " exit 1 # exit with non zero status } write verbose "successfully gathered basic wmi data " \# convert bios release date (if available) from wmi datetime format $biosreleasedate = "n/a" if ($bios releasedate) { try { $biosreleasedate = \[system management managementdatetimeconverter] todatetime($bios releasedate) write verbose "converted bios release date $biosreleasedate" } catch { $biosreleasedate = $bios releasedate # keep original if conversion fails write warning "failed to convert bios release date '$($bios releasedate)' " } } else { write verbose "bios release date not available " } \# determine overall virtualization status $resultint = 0 # default/error state $isvirtualindicator = get virtualizationindicator computerobject $computer biosobject $bios $isothervirtsoftwaredetected = $false # initialize flag write verbose "checking hypervisorpresent flag " if ($computer hypervisorpresent) { \# state 3 or 4 windows hypervisor platform is running write verbose "hypervisorpresent is true checking for hyper v 'vmms' service " $hypervservice = get service name vmms erroraction silentlycontinue if ($null ne $hypervservice) { write verbose "'vmms' service found " $resultint = 4 # physical host with hyper v configured } else { write verbose "'vmms' service not found " $resultint = 3 # physical host with hypervisor enabled (whpx, vbs, wsl2 etc , but no vmms) } } elseif ($isvirtualindicator) { \# state 2 not a host based on hypervisorpresent, but looks like a guest vm based on strings write verbose "hypervisorpresent is false detected as vm guest based on strings " $resultint = 2 # virtual machine guest } else { \# state 1 or 5 hypervisorpresent is false, and doesn't look like a guest vm write verbose "hypervisorpresent is false and not detected as vm guest checking for other virtualization software " \# check for other virtualization software (vmware workstation/player, virtualbox, parallels) $isothervirtsoftwaredetected = test othervirtualizationsoftware if ($isothervirtsoftwaredetected) { \# found indicators of vmware workstation/player or virtualbox likely installed/active write verbose "detected other virtualization software " $resultint = 5 # physical host with other virtualization software detected } else { \# no hypervisorpresent, not a vm guest, no other virt software detected write verbose "no other virtualization software detected " $resultint = 1 # physical machine (no virtualization detected) } } write verbose "final determined status code $resultint" \# optionally, create a result object with details for logging or audit $detailedresult = \[pscustomobject]@{ computermanufacturer = $computer manufacturer computermodel = $computer model systemname = $computer name hypervisorpresent = $computer hypervisorpresent systemtype = $computer systemtype biosmanufacturer = $bios manufacturer biosversion = ($bios biosversion join " ") # handle potential array biosreleasedate = $biosreleasedate cpuvirtualizationflag = $cpu virtualizationfirmwareenabled # check if cpu supports vt x/amd v detectedasvmguest = $isvirtualindicator hypervservicefound = if ($computer hypervisorpresent and $resultint eq 4) {$true} else {$false} othervirtsoftwaredetected = $isothervirtsoftwaredetected # based on exhaustive checks virtualizationstatus = $resultint # the final classification code } \# output \# output the final integer result only write output $resultint \# for debugging or more detailed logging, output the detailed object (comment out if not needed) \# $verbosepreference = "continue" # set this at the top or use verbose switch \# write host " detailed results " \# $detailedresult | format list \# write host " "