agent/managedInstances/fingerprint/hardwareInfo_windows.go (173 lines of code) (raw):
// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//go:build windows
// +build windows
// package fingerprint contains functions that helps identify an instance
// hardwareInfo contains platform specific way of fetching the hardware hash
package fingerprint
import (
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/gob"
"fmt"
"path/filepath"
"time"
"github.com/aws/amazon-ssm-agent/agent/appconfig"
"github.com/aws/amazon-ssm-agent/agent/log"
"github.com/aws/amazon-ssm-agent/agent/log/ssmlog"
"github.com/aws/amazon-ssm-agent/agent/platform"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
)
type WMIInterface string
const (
hardwareID = "uuid"
wmiServiceName = "Winmgmt"
serviceRetryInterval = 15 // Seconds
serviceRetry = 5
wmic WMIInterface = "WMIC"
wql WMIInterface = "WQL"
)
func waitForService(log log.T, service *mgr.Service) error {
var err error
var status svc.Status
for attempt := 1; attempt <= serviceRetry; attempt++ {
status, err = service.Query()
if err == nil && status.State == svc.Running {
return nil
}
if err != nil {
log.Debugf("Attempt %d: Failed to get WMI service status: %v", attempt, err)
} else {
log.Debugf("Attempt %d: WMI not running - Current status: %v", attempt, status.State)
}
time.Sleep(serviceRetryInterval * time.Second)
}
return fmt.Errorf("Failed to wait for WMI to get into Running status")
}
var wmicCommand = filepath.Join(appconfig.EnvWinDir, "System32", "wbem", "wmic.exe")
var currentHwHash = func() (map[string]string, error) {
log := ssmlog.SSMLogger(true)
hardwareHash := make(map[string]string)
// Wait for WMI Service
winManager, err := mgr.Connect()
log.Debug("Waiting for WMI Service to be ready.....")
if err != nil {
log.Warnf("Failed to connect to WMI: '%v'", err)
return hardwareHash, err
}
// Open WMI Service
var wmiService *mgr.Service
wmiService, err = winManager.OpenService(wmiServiceName)
if err != nil {
log.Warnf("Failed to open wmi service: '%v'", err)
return hardwareHash, err
}
// Wait for WMI Service to start
if err = waitForService(log, wmiService); err != nil {
log.Warn("WMI Service cannot be query for hardware hash.")
return hardwareHash, err
}
log.Debug("WMI Service is ready to be queried....")
wmiInterface := getWMIInterface(log)
hardwareHash[hardwareID], _ = csproductUuid(log, wmiInterface)
hardwareHash["processor-hash"], _ = processorInfoHash(log, wmiInterface)
hardwareHash["memory-hash"], _ = memoryInfoHash(log, wmiInterface)
hardwareHash["bios-hash"], _ = biosInfoHash(log, wmiInterface)
hardwareHash["system-hash"], _ = systemInfoHash(log, wmiInterface)
hardwareHash["hostname-info"], _ = hostnameInfo()
hardwareHash[ipAddressID], _ = primaryIpInfo()
hardwareHash["macaddr-info"], _ = macAddrInfo()
hardwareHash["disk-info"], _ = diskInfoHash(log, wmiInterface)
return hardwareHash, nil
}
// getWMIInterface returns WMI interface which should be used to retrieve hardware info data
func getWMIInterface(logger log.T) (wmiInterface WMIInterface) {
windows2025OrLater, err := platform.IsPlatformWindowsServer2025OrLater(logger)
// if we fail to determine Windows version, default to WMIC
if err != nil {
logger.Warnf("Failed to determine Windows version: %v, returning WMIC as WMI interface", err)
return wmic
}
// if it is Windows 2025 or later use WQL, otherwise use WMIC
if windows2025OrLater {
logger.Debugf("Detected Windows 2025 version or later, returning WQL as WMI interface...")
return wql
} else {
logger.Debugf("Detected version prior to Windows 2025, returning WMIC as WMI interface...")
return wmic
}
}
func csproductUuid(logger log.T, wmiInterface WMIInterface) (encodedData string, err error) {
var uuid string
switch wmiInterface {
case wmic:
encodedData, uuid, err = commandOutputHash(wmicCommand, "csproduct", "get", "UUID")
case wql:
var csProductData platform.Win32_ComputerSystemProduct
encodedData, csProductData, err = getWMIObject[platform.Win32_ComputerSystemProduct](logger)
uuid = csProductData.UUID
default:
logger.Warnf("Unknown WMI interface: %v", wmiInterface)
}
logger.Tracef("Current UUID value: /%v/", uuid)
return
}
func processorInfoHash(logger log.T, wmiInterface WMIInterface) (encodedData string, err error) {
switch wmiInterface {
case wmic:
encodedData, _, err = commandOutputHash(wmicCommand, "cpu", "list", "brief")
case wql:
encodedData, _, err = getWMIObject[platform.Win32_Processor](logger)
default:
logger.Warnf("Unknown WMI interface: %v", wmiInterface)
}
return
}
func memoryInfoHash(logger log.T, wmiInterface WMIInterface) (encodedData string, err error) {
switch wmiInterface {
case wmic:
encodedData, _, err = commandOutputHash(wmicCommand, "memorychip", "list", "brief")
case wql:
encodedData, _, err = getWMIObject[platform.Win32_PhysicalMemory](logger)
default:
logger.Warnf("Unknown WMI interface: %v", wmiInterface)
}
return
}
func biosInfoHash(logger log.T, wmiInterface WMIInterface) (encodedData string, err error) {
switch wmiInterface {
case wmic:
encodedData, _, err = commandOutputHash(wmicCommand, "bios", "list", "brief")
case wql:
encodedData, _, err = getWMIObject[platform.Win32_BIOS](logger)
default:
logger.Warnf("Unknown WMI interface: %v", wmiInterface)
}
return
}
func systemInfoHash(logger log.T, wmiInterface WMIInterface) (encodedData string, err error) {
switch wmiInterface {
case wmic:
encodedData, _, err = commandOutputHash(wmicCommand, "computersystem", "list", "brief")
case wql:
encodedData, _, err = getWMIObject[platform.Win32_ComputerSystem](logger)
default:
logger.Warnf("Unknown WMI interface: %v", wmiInterface)
}
return
}
func diskInfoHash(logger log.T, wmiInterface WMIInterface) (encodedData string, err error) {
switch wmiInterface {
case wmic:
encodedData, _, err = commandOutputHash(wmicCommand, "diskdrive", "list", "brief")
case wql:
encodedData, _, err = getWMIObject[platform.Win32_DiskDrive](logger)
default:
logger.Warnf("Unknown WMI interface: %v", wmiInterface)
}
return
}
func getWMIObject[T interface{}](logger log.T) (encodedWmiObject string, wmiObject T, err error) {
if wmiObject, err = platform.GetSingleWMIObject[T](""); err != nil {
logger.Errorf("Failed to fetch WMI object: %v", err)
} else {
var b bytes.Buffer
if err = gob.NewEncoder(&b).Encode(wmiObject); err != nil {
logger.Errorf("Failed to encode WMI object: %v", err)
} else {
sum := md5.Sum(b.Bytes())
encodedWmiObject = base64.StdEncoding.EncodeToString(sum[:])
}
}
return
}