# Copyright (c) 2026, R. W. Rodolico # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # windows_identity.ps1 # Machine identity (Spec 1.3): DMI uuid/serial, effective uuid/serial, virtualization. # Sensor contract: unix/sensors/Documentation/README.md; keys: Documentation/Spec.md 7.6 # Must stay Windows PowerShell 5.1 compatible and pure ASCII. # # Version 1.0.0 20261018 Claude # Initial version (bug 211, stage W2) Import-Module ([IO.Path]::Combine($PSScriptRoot, '..', 'lib', 'SysinfoSensor.psm1')) Test-SysinfoWindows function Get-CimValue { param([string]$ClassName, [string]$Property) $instance = Get-CimInstance -ClassName $ClassName -ErrorAction Stop | Select-Object -First 1 if ($null -eq $instance) { return '' } $p = $instance.PSObject.Properties[$Property] if ($null -eq $p -or $null -eq $p.Value) { return '' } return ([string]$p.Value).Trim() } Invoke-SysinfoSensor { if ($null -eq (Get-Command Get-CimInstance -ErrorAction SilentlyContinue)) { Exit-SysinfoSensor -Code $SYSINFO_EXIT_NOT_APPLICABLE -Message 'Get-CimInstance not available' } $uuidRaw = Get-CimValue 'Win32_ComputerSystemProduct' 'UUID' $serial = Get-CimValue 'Win32_BIOS' 'SerialNumber' $boardSerial = Get-CimValue 'Win32_BaseBoard' 'SerialNumber' $manufacturer = Get-CimValue 'Win32_ComputerSystem' 'Manufacturer' $product = Get-CimValue 'Win32_ComputerSystem' 'Model' $machineId = '' $base = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64) try { $key = $base.OpenSubKey('SOFTWARE\Microsoft\Cryptography') if ($null -ne $key) { try { $machineId = [string]$key.GetValue('MachineGuid', '') } finally { $key.Close() } } } finally { $base.Close() } $uuid = ConvertTo-NormalisedUUID $uuidRaw $uuidStatus = Get-SysinfoUUIDStatus $uuidRaw $serialStatus = Get-SysinfoSerialStatus $serial # virtualization, inferred from manufacturer/product $text = "$manufacturer $product" $virtualization = 'none' if ($text -match 'QEMU|KVM') { $virtualization = 'kvm' } elseif ($text -match 'Microsoft') { if ($product -match 'Virtual Machine') { $virtualization = 'hyperv' } } elseif ($text -match 'VMware') { $virtualization = 'vmware' } elseif ($text -match 'Xen') { $virtualization = 'xen' } elseif ($text -match 'VirtualBox|innotek') { $virtualization = 'virtualbox' } $configuredUUID = [string]$env:SYSINFO_CONFIGURED_UUID $configuredSerial = [string]$env:SYSINFO_CONFIGURED_SERIAL $serverUUID = [string]$env:SYSINFO_SERVERINFO_UUID $serverSerial = [string]$env:SYSINFO_SERVERINFO_SERIAL # effective UUID: first valid non-placeholder of server.info, DMI, config $effUUID = '' $effUUIDSource = '' foreach ($candidate in @(@($serverUUID, 'server_info'), @($uuidRaw, 'dmi'), @($configuredUUID, 'config'))) { if (-not (Test-PlaceholderUUID $candidate[0])) { $effUUID = ConvertTo-NormalisedUUID $candidate[0] $effUUIDSource = $candidate[1] break } } # effective serial: first non-placeholder of server.info, DMI, config; else uuid on a virtual host $effSerial = '' $effSerialSource = '' foreach ($candidate in @(@($serverSerial, 'server_info'), @($serial, 'dmi'), @($configuredSerial, 'config'))) { if (-not (Test-PlaceholderSerial $candidate[0])) { $effSerial = ([string]$candidate[0]).Trim() $effSerialSource = $candidate[1] break } } if ($effSerial -eq '' -and $virtualization -ne 'none' -and $effUUID -ne '') { $effSerial = $effUUID $effSerialSource = 'uuid' } Write-SysinfoFact -Path @('identity', 'system_uuid_raw') -Value $uuidRaw Write-SysinfoFact -Path @('identity', 'system_uuid') -Value $uuid Write-SysinfoFact -Path @('identity', 'system_uuid_status') -Value $uuidStatus Write-SysinfoFact -Path @('identity', 'system_serial') -Value $serial Write-SysinfoFact -Path @('identity', 'system_serial_status') -Value $serialStatus Write-SysinfoFact -Path @('identity', 'board_serial') -Value $boardSerial Write-SysinfoFact -Path @('identity', 'system_manufacturer') -Value $manufacturer Write-SysinfoFact -Path @('identity', 'system_product') -Value $product Write-SysinfoFact -Path @('identity', 'machine_id') -Value $machineId Write-SysinfoFact -Path @('identity', 'virtualization') -Value $virtualization Write-SysinfoFact -Path @('identity', 'configured_uuid') -Value $configuredUUID Write-SysinfoFact -Path @('identity', 'configured_serial') -Value $configuredSerial Write-SysinfoFact -Path @('identity', 'server_info_uuid') -Value $serverUUID Write-SysinfoFact -Path @('identity', 'server_info_serial') -Value $serverSerial Write-SysinfoFact -Path @('identity', 'uuid') -Value $effUUID Write-SysinfoFact -Path @('identity', 'uuid_source') -Value $effUUIDSource Write-SysinfoFact -Path @('identity', 'serial') -Value $effSerial Write-SysinfoFact -Path @('identity', 'serial_source') -Value $effSerialSource }