@@ -5,16 +5,18 @@ package device
55import (
66 "context"
77 "fmt"
8+ "strings"
89
910 "github.com/step-security/dev-machine-guard/internal/executor"
1011 "golang.org/x/sys/windows"
1112 "golang.org/x/sys/windows/registry"
1213)
1314
14- // getSerialNumberWindows reads the BIOS serial number from the Windows registry.
15- // Falls back to the machine GUID if the serial is empty or a placeholder.
16- func getSerialNumberWindows (_ context.Context , _ executor.Executor ) string {
17- // Try BIOS registry key
15+ // getSerialNumberWindows reads the BIOS serial number.
16+ // Tries native registry first, then PowerShell WMI (for VMs where the registry
17+ // key may not exist), then falls back to MachineGuid.
18+ func getSerialNumberWindows (ctx context.Context , exec executor.Executor ) string {
19+ // Try BIOS registry key (fastest, no subprocess)
1820 k , err := registry .OpenKey (registry .LOCAL_MACHINE , `HARDWARE\DESCRIPTION\System\BIOS` , registry .QUERY_VALUE )
1921 if err == nil {
2022 serial , _ , err := k .GetStringValue ("SystemSerialNumber" )
@@ -24,7 +26,18 @@ func getSerialNumberWindows(_ context.Context, _ executor.Executor) string {
2426 }
2527 }
2628
27- // Fallback: MachineGuid (unique per install, always present)
29+ // Fallback: PowerShell WMI query (works on EC2 and other VMs where
30+ // the registry key doesn't exist but WMI exposes the serial)
31+ stdout , _ , _ , err := exec .Run (ctx , "powershell" , "-NoProfile" , "-Command" ,
32+ "(Get-CimInstance -ClassName Win32_BIOS).SerialNumber" )
33+ if err == nil {
34+ s := strings .TrimSpace (stdout )
35+ if s != "" {
36+ return s
37+ }
38+ }
39+
40+ // Last resort: MachineGuid (unique per install, always present)
2841 k , err = registry .OpenKey (registry .LOCAL_MACHINE , `SOFTWARE\Microsoft\Cryptography` , registry .QUERY_VALUE )
2942 if err == nil {
3043 guid , _ , err := k .GetStringValue ("MachineGuid" )
0 commit comments