I'd like to get an id unique to a computer with Python on Windows and Linux. It could be the CPU ID, the motherboard serial, ... or anything else.
I looked at several modules (pycpuid, psi, ...) without luck.
Any idea on how to do that?
I'd like to get an id unique to a computer with Python on Windows and Linux. It could be the CPU ID, the motherboard serial, ... or anything else.
I looked at several modules (pycpuid, psi, ...) without luck.
Any idea on how to do that?
How about using the MAC address as unique id?
The discussion here Obtain MAC Address from Devices using Python shows how to obtain the MAC address
There seems to be no direct "python" way of doing this. On modern PC hardware, there usually is an UUID stored in the BIOS - on Linux there is a command line utility dmidecode
that can read this; example from my desktop:
System Information
Manufacturer: Dell Inc.
Product Name: OptiPlex 755
Version: Not Specified
Serial Number: 5Y8YF3J
UUID: 44454C4C-5900-1038-8059-B5C04F46334A
Wake-up Type: Power Switch
SKU Number: Not Specified
Family: Not Specified
The problem with MAC addresses is that usually you can easily change them programmatically (at least if you run the OS in a VM)
On Windows, you can use this C API
machineid
package introduced in @ezekg's answer below. –
Palatine Funny! But uuid.getnode return the same value as dmidecode.exe.
subprocess.Popen('dmidecode.exe -s system-uuid'.split())
00000000-0000-0000-0000-001FD088565A
import uuid
uuid.UUID(int=uuid.getnode())
UUID('00000000-0000-0000-0000-001fd088565a')
uuid.getnode()
Good enough for my purpose –
Slaveholder for Windows you need DmiDecode for Windows (link) :
subprocess.Popen('dmidecode.exe -s system-uuid'.split())
for Linux (non root):
subprocess.Popen('hal-get-property --udi /org/freedesktop/Hal/devices/computer --key system.hardware.uuid'.split())
For python3.6 and windows must be used decode
>>> import subprocess
... current_machine_id = subprocess.check_output('wmic csproduct get uuid').decode().split('\n')[1].strip()
... print(current_machine_id)
Or if you don't want to use subprocess, (It's slow) use ctypes. This is for Linux non root.
import ctypes
from ctypes.util import find_library
from ctypes import Structure
class DBusError(Structure):
_fields_ = [("name", ctypes.c_char_p),
("message", ctypes.c_char_p),
("dummy1", ctypes.c_int),
("dummy2", ctypes.c_int),
("dummy3", ctypes.c_int),
("dummy4", ctypes.c_int),
("dummy5", ctypes.c_int),
("padding1", ctypes.c_void_p),]
class HardwareUuid(object):
def __init__(self, dbus_error=DBusError):
self._hal = ctypes.cdll.LoadLibrary(find_library('hal'))
self._ctx = self._hal.libhal_ctx_new()
self._dbus_error = dbus_error()
self._hal.dbus_error_init(ctypes.byref(self._dbus_error))
self._conn = self._hal.dbus_bus_get(ctypes.c_int(1),
ctypes.byref(self._dbus_error))
self._hal.libhal_ctx_set_dbus_connection(self._ctx, self._conn)
self._uuid_ = None
def __call__(self):
return self._uuid
@property
def _uuid(self):
if not self._uuid_:
udi = ctypes.c_char_p("/org/freedesktop/Hal/devices/computer")
key = ctypes.c_char_p("system.hardware.uuid")
self._hal.libhal_device_get_property_string.restype = \
ctypes.c_char_p
self._uuid_ = self._hal.libhal_device_get_property_string(
self._ctx, udi, key, self._dbus_error)
return self._uuid_
You can use this like:
get_uuid = HardwareUuid()
print get_uuid()
AttributeError: function 'libhal_ctx_new' not found
help! –
Farrica undefined symbol: libhal_ctx_new
–
Mauritamauritania Invoke one of these in the shell or through a pipe in Python to get the hardware serial number of Apple machines running OS X >= 10.5:
/usr/sbin/system_profiler SPHardwareDataType | fgrep 'Serial' | awk '{print $NF}'
or
ioreg -l | awk '/IOPlatformSerialNumber/ { print $4 }' | sed s/\"//g
BTW: MAC addresses are not a good idea: there can be >1 network cards in a machine, and MAC addresses can be spoofed.
This should work on windows:
import subprocess
current_machine_id = subprocess.check_output('wmic csproduct get uuid').split('\n')[1].strip()
I don't think there is a reliable, cross platform, way to do this. I know of one network device that changes its MAC address as a form of hardware error reporting, and there are a million other ways this could fail.
The only reliable solution is for your application to assign a unique key to each machine. Yes it can be spoofed, but you don't have to worry about it completely breaking. If you are worried about spoofing you can apply some sort of heuristic (like a change in mac address) to try and determine if the key has been moved.
UPDATE: You can use bacterial fingerprinting.
I'd recommend using the machine's native GUID, assigned by the operating system during installation. Since this question comes up relatively often, I wrote a small, cross-platform PyPI package that queries a machine's native GUID called machineid.
Essentially, it looks like this, but with some Windows-specific WMI registry queries for an even more accurate ID. The package also has support for anonymizing the GUID via HMAC-SHA256.
import subprocess
import sys
def run(cmd):
try:
return subprocess.run(cmd, shell=True, capture_output=True, check=True, encoding="utf-8") \
.stdout \
.strip()
except:
return None
def guid():
if sys.platform == 'darwin':
return run(
"ioreg -d2 -c IOPlatformExpertDevice | awk -F\\\" '/IOPlatformUUID/{print $(NF-1)}'",
)
if sys.platform == 'win32' or sys.platform == 'cygwin' or sys.platform == 'msys':
return run('wmic csproduct get uuid').split('\n')[2] \
.strip()
if sys.platform.startswith('linux'):
return run('cat /var/lib/dbus/machine-id') or \
run('cat /etc/machine-id')
if sys.platform.startswith('openbsd') or sys.platform.startswith('freebsd'):
return run('cat /etc/hostid') or \
run('kenv -q smbios.system.uuid')
(Open to feedback and PRs!)
How about using the MAC address as unique id?
The discussion here Obtain MAC Address from Devices using Python shows how to obtain the MAC address
I believe HDD ID is usually more unique than UUID:
serials = subprocess.check_output('wmic diskdrive get Name, SerialNumber').decode().split('\n')[1:]
for serial in serials:
if 'DRIVE0' in serial:
return serial.split('DRIVE0')[-1].strip()
For improved uniqueness, you can combine both UUID and HDD ID:
def get_uuid() -> str:
return subprocess.check_output('wmic csproduct get uuid').decode().split('\n')[1].strip()
def get_hdd_id() -> str:
serials = subprocess.check_output('wmic diskdrive get Name,
SerialNumber').decode().split('\n')[1:]
for serial in serials:
if 'DRIVE0' in serial:
return serial.split('DRIVE0')[-1].strip()
unique_id = get_uuid() + '-' + get_hdd_id()
2019 Answer (for Windows):
from typing import Optional
import re
import subprocess
import uuid
def get_windows_uuid() -> Optional[uuid.UUID]:
try:
# Ask Windows for the device's permanent UUID. Throws if command missing/fails.
txt = subprocess.check_output("wmic csproduct get uuid").decode()
# Attempt to extract the UUID from the command's result.
match = re.search(r"\bUUID\b[\s\r\n]+([^\s\r\n]+)", txt)
if match is not None:
txt = match.group(1)
if txt is not None:
# Remove the surrounding whitespace (newlines, space, etc)
# and useless dashes etc, by only keeping hex (0-9 A-F) chars.
txt = re.sub(r"[^0-9A-Fa-f]+", "", txt)
# Ensure we have exactly 32 characters (16 bytes).
if len(txt) == 32:
return uuid.UUID(txt)
except:
pass # Silence subprocess exception.
return None
print(get_windows_uuid())
Uses Windows API to get the computer's permanent UUID, then processes the string to ensure it's a valid UUID, and lastly returns a Python object (https://docs.python.org/3/library/uuid.html) which gives you convenient ways to use the data (such as 128-bit integer, hex string, etc).
Good luck!
PS: The subprocess call could probably be replaced with ctypes directly calling Windows kernel/DLLs for the Win32_ComputerSystemProduct API (which is what wmic
uses internally). But then you have to be very careful and ensure that you call it properly on all systems. For my purposes this wmic
-based function is safer and is all I need. It does strong validation and produces correct results. And if the wmic output is wrong or if the command is missing, our function returns None
to let you handle that any way you want (such as generating a random UUID and saving it in your app's config file instead).
(Get-CimInstance -Class Win32_ComputerSystemProduct).UUID
is identical to wmic csproduct get uuid
. It is THE "official" Windows UUID. Here's the Microsoft docs, learn.microsoft.com/en-us/windows/win32/cimwin32prov/… which says that "This value comes from the UUID member of the System Information structure in the SMBIOS information". Which means that the UUID from wmic csproduct get uuid
is read from the motherboard. If it's the same on all systems maybe you use virtual machines that have a fake SMBIOS? –
Essam 03000200-0400-0500-0006-000700080009
. It may not even be substantial, but if you search that result you'll see that some systems will spit the exact same result, even though the MB manufacturer may be at fault. Quote: I've probably seen more SMBIOS UUIDs than you've had hot dinners, and I can tell you that the UUID is not always unique and not always constant.
, ergo, unreliable. –
Fouquiertinville uuid
lib), and save that UUID to config. But that fails if the user copies/moves their installation and gives their per-installation UUID to another computer too. So I would combine the per-installation UUID with the motherboard UUID, and regenerate the per-installation UUID if the motherboard UUID doesn't match anymore. –
Essam from uuid import getnode as get_mac mac = get_mac()
. The MAC address is also not a great approach due to it being easily manipulated, but in this situation I don't much care for that, I'm trying to determine uniqueness in an environment where users don't change MACs, and it is also good enough. Not sure what issue may arise in systems with VPN interfaces or multiple physical interfaces... yeah, there will always be issues. –
Fouquiertinville This is a quick and easy solution for Windows:
import subprocess
computer_id = subprocess.check_output("wmic bios get serialnumber").decode().split()[1]
print(computer_id)
© 2022 - 2024 — McMap. All rights reserved.
uuid.UUID(int=uuid.getnode())
was good enough for me – Slaveholder