Скрипт збору серійних номерів техніки

Знімає серійний номер комп'ютера, розпізнає заглушки виробника («System Serial Number», «To Be Filled By O.E.M.») і підставляє замість них номер материнської плати, додає UUID — і збирає всі підключені монітори з їхніми серійниками, які зазвичай доводиться шукати на задній панелі. Нічого не змінює, прав адміністратора не потребує, дописує по рядку на пристрій у спільний CSV.

Скрипт

Перевірено на Windows 11 Pro, збірка 26200, з правами звичайного користувача.

Get-SerialNumbers.ps1
# Серійні номери робочого місця: системний блок або ноутбук ПЛЮС усі
# підключені монітори. Нічого не змінює, прав адміністратора не потребує.
# Дописує по рядку на кожен пристрій у спільний CSV.
#
#   .\Get-SerialNumbers.ps1
#   .\Get-SerialNumbers.ps1 -CsvPath "\SERVER\inv\seriini-nomery.csv"
#
# Заглушки виробника («System Serial Number», «To Be Filled By O.E.M.»)
# скрипт розпізнає й підставляє серійний номер материнської плати.

param(
    [string] $CsvPath
)

$ErrorActionPreference = 'SilentlyContinue'

# Значення, які материнські плати віддають замість справжнього номера
$PLACEHOLDERS = @(
    'System Serial Number', 'To Be Filled By O.E.M.', 'To be filled by O.E.M.',
    'Default string', 'None', 'Not Applicable', 'Not Specified', 'O.E.M.', '0'
)

function Test-Placeholder([string] $value) {
    if ([string]::IsNullOrWhiteSpace($value)) { return $true }
    foreach ($p in $PLACEHOLDERS) {
        if ($value.Trim() -eq $p) { return $true }
    }
    # Номер із самих нулів або однакових символів теж нічого не означає
    return ($value.Trim() -match '^(.)\1*$')
}

$bios  = Get-CimInstance Win32_BIOS
$csp   = Get-CimInstance Win32_ComputerSystemProduct
$cs    = Get-CimInstance Win32_ComputerSystem
$board = Get-CimInstance Win32_BaseBoard

$serial = $bios.SerialNumber
$source = 'BIOS'
if (Test-Placeholder $serial) {
    # На зібраних вручну машинах справжній номер лишається тільки в платі
    if (-not (Test-Placeholder $board.SerialNumber)) {
        $serial = $board.SerialNumber
        $source = 'материнська плата'
    } else {
        $serial = ''
        $source = 'немає — потрібен інвентарний номер'
    }
}

$model = ($cs.Manufacturer + ' ' + $cs.Model).Trim()
if ($cs.Model -match 'System Product Name|To Be Filled|Default string') {
    $model = ('складання: ' + $board.Manufacturer + ' ' + $board.Product).Trim()
}

$rows = @()
$rows += [pscustomobject]@{
    Компютер      = $env:COMPUTERNAME
    Тип           = if ($cs.PCSystemType -eq 2) { 'Ноутбук' } else { 'Системний блок' }
    Виробник      = $cs.Manufacturer
    Модель        = $model
    СерійнийНомер = $serial
    ДжерелоНомера = $source
    UUID          = $csp.UUID
    Дата          = (Get-Date -Format 'yyyy-MM-dd HH:mm')
}

# --- Монітори -----------------------------------------------------------
# EDID приходить масивами кодів символів, тому їх треба зібрати в рядок.
function ConvertTo-EdidString($codes) {
    if (-not $codes) { return '' }
    (-join ($codes | Where-Object { $_ -gt 0 } | ForEach-Object { [char]$_ })).Trim()
}

$monitors = @(Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorID)
foreach ($m in $monitors) {
    $rows += [pscustomobject]@{
        Компютер      = $env:COMPUTERNAME
        Тип           = 'Монітор'
        Виробник      = ConvertTo-EdidString $m.ManufacturerName
        Модель        = ConvertTo-EdidString $m.UserFriendlyName
        СерійнийНомер = ConvertTo-EdidString $m.SerialNumberID
        ДжерелоНомера = "EDID, рік випуску $($m.YearOfManufacture)"
        UUID          = ''
        Дата          = (Get-Date -Format 'yyyy-MM-dd HH:mm')
    }
}

$rows | Format-Table -AutoSize Тип, Виробник, Модель, СерійнийНомер, ДжерелоНомера

if ($monitors.Count -eq 0) {
    Write-Host ''
    Write-Host 'Моніторів не знайдено. Так буває, якщо монітор під`єднано через' -ForegroundColor Yellow
    Write-Host 'перемикач або док-станцію, які не передають EDID, або якщо він вимкнений.' -ForegroundColor Yellow
}
if ($source -like 'немає*') {
    Write-Host ''
    Write-Host 'Виробник не записав серійний номер: у BIOS стоїть заглушка.' -ForegroundColor Yellow
    Write-Host 'Ідентифікуйте машину за інвентарним номером, а в облік запишіть UUID.' -ForegroundColor Yellow
}

if ($CsvPath) {
    $dir = Split-Path $CsvPath -Parent
    if ($dir -and -not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
    $exists = Test-Path $CsvPath
    # UTF8 з BOM — інакше Excel покаже кирилицю абракадаброю
    $rows | Export-Csv -Path $CsvPath -NoTypeInformation -Encoding UTF8 -Append:$exists
    Write-Host ''
    Write-Host "Записано рядків: $($rows.Count) у $CsvPath" -ForegroundColor Green
}
Зберігайте файл у кодуванні UTF-8 з BOM. Інакше PowerShell 5.1 прочитає кирилицю в коментарях як ANSI і не запустить скрипт.

Як запустити

СценарійКоманда
Одна машина, вивід на екран .\Get-SerialNumbers.ps1
Із записом у спільний CSV .\Get-SerialNumbers.ps1 -CsvPath "\\SERVER\inv\seriini-nomery.csv"
Через групову політику Скрипт входу з тим самим -CsvPath: парк опише себе сам за тиждень
Віддалено на список машин Invoke-Command -ComputerName PC1,PC2 -FilePath .\Get-SerialNumbers.ps1
Віддалений запуск потребує ввімкненого віддаленого керування PowerShell. У домені його зазвичай вмикають політикою; поза доменом простіше піти шляхом скрипта входу.

Що варто знати про результат

Заглушка замість номера — не поломка. На зібраних вручну машинах виробник плати лишає в BIOS текст на кшталт System Serial Number. Скрипт це розпізнає й бере номер материнської плати, а в колонці «ДжерелоНомера» пише, звідки значення. Якщо справжнього немає ніде — ідентифікуйте машину за інвентарним номером.
Монітор може не потрапити в список. EDID приходить лише від увімкненого й безпосередньо під'єднаного монітора. Через частину перемикачів і док-станцій дані не передаються, а дешеві моделі часом пишуть порожній серійний номер або однаковий для всієї партії.
Виробник монітора — трилітерний код. AUS — ASUS, DEL — Dell, SAM — Samsung. Це стандартний ідентифікатор із EDID, а не помилка.