Инвентаризация железа и ПО с помощью скриптов (PowerShell + база данных)

Проблема

В любой организации, где есть хотя бы несколько компьютеров и серверов, администратор сталкивается с необходимостью вести учёт оборудования и установленного программного обеспечения. Без систематической инвентаризации невозможно:

  • Планировать замену устаревшего железа.
  • Контролировать соответствие лицензионного ПО.
  • Быстро находить компьютеры с определёнными характеристиками (например, с 8 ГБ ОЗУ для обновления).
  • Анализировать распространение вредоносного ПО или нежелательных приложений.

Ручной сбор данных (обход рабочих мест, опрос пользователей, просмотр в Active Directory) это долго, ненадёжно и не даёт актуальной картины в реальном времени. Нужно автоматизировать инвентаризацию: собирать данные централизованно, хранить в структурированном виде и иметь возможность формировать отчёты.

Решение

Используем PowerShell для сбора информации о железе и ПО на компьютерах Windows. Данные будем сохранять в базу данных SQL Server (или SQL Server Express) для последующего анализа и отчётности. Для сбора информации с удалённых компьютеров используем WMI/CIM (WinRM) или агентный подход через запланированные задачи и Invoke-Command. В этой статье мы создадим PowerShell-скрипт, который собирает:

  • Основные характеристики железа: производитель, модель, серийный номер, процессор, оперативная память, диски (модель, объём, тип), сетевые адаптеры (MAC, IP).
  • Данные об ОС: версия, редакция, последняя перезагрузка, установленные обновления (опционально).
  • Установленное программное обеспечение: название, версия, издатель, дата установки (из реестра и WMI).

Скрипт будет выполняться на каждом компьютере (например, через групповую политику или планировщик задач) и отправлять данные на центральный сервер, где они будут записываться в базу данных.

Основные источники: Документация Microsoft по WMIPowerShell CIM cmdletsSQL Server PowerShell модуль.

Пошаговая инструкция

1. Подготовка базы данных

На сервере (или на любом доступном SQL-сервере) создадим базу данных InventoryDB и таблицы для хранения данных.

1.1. Создание базы данных и таблиц

Подключитесь к SQL Server (например, через SSMS или sqlcmd) и выполните:

CREATE DATABASE InventoryDB;
GO

USE InventoryDB;
GO

-- Таблица для компьютеров (общая информация)
CREATE TABLE Computers (
    ComputerID INT IDENTITY(1,1) PRIMARY KEY,
    ComputerName NVARCHAR(50) NOT NULL,
    Manufacturer NVARCHAR(100),
    Model NVARCHAR(100),
    SerialNumber NVARCHAR(100),
    LastSeen DATETIME DEFAULT GETDATE(),
    CONSTRAINT UQ_ComputerName UNIQUE (ComputerName)
);

-- Таблица для процессоров
CREATE TABLE Processors (
    ProcessorID INT IDENTITY(1,1) PRIMARY KEY,
    ComputerID INT NOT NULL,
    Name NVARCHAR(200),
    NumberOfCores INT,
    MaxClockSpeed DECIMAL(10,2),
    FOREIGN KEY (ComputerID) REFERENCES Computers(ComputerID) ON DELETE CASCADE
);

-- Таблица для оперативной памяти
CREATE TABLE MemoryModules (
    MemoryID INT IDENTITY(1,1) PRIMARY KEY,
    ComputerID INT NOT NULL,
    CapacityGB INT,
    Speed INT,
    Manufacturer NVARCHAR(100),
    FOREIGN KEY (ComputerID) REFERENCES Computers(ComputerID) ON DELETE CASCADE
);

-- Таблица для дисков
CREATE TABLE Disks (
    DiskID INT IDENTITY(1,1) PRIMARY KEY,
    ComputerID INT NOT NULL,
    Model NVARCHAR(200),
    SizeGB INT,
    MediaType NVARCHAR(50),  -- SSD/HDD (если определяется)
    FOREIGN KEY (ComputerID) REFERENCES Computers(ComputerID) ON DELETE CASCADE
);

-- Таблица для сетевых адаптеров
CREATE TABLE NetworkAdapters (
    AdapterID INT IDENTITY(1,1) PRIMARY KEY,
    ComputerID INT NOT NULL,
    Name NVARCHAR(200),
    MACAddress NVARCHAR(20),
    IPAddress NVARCHAR(50),
    DHCPEnabled BIT,
    FOREIGN KEY (ComputerID) REFERENCES Computers(ComputerID) ON DELETE CASCADE
);

-- Таблица для операционных систем
CREATE TABLE OperatingSystems (
    OSID INT IDENTITY(1,1) PRIMARY KEY,
    ComputerID INT NOT NULL,
    Caption NVARCHAR(200),
    Version NVARCHAR(50),
    InstallDate DATETIME,
    LastBootUpTime DATETIME,
    FOREIGN KEY (ComputerID) REFERENCES Computers(ComputerID) ON DELETE CASCADE
);

-- Таблица для установленного ПО
CREATE TABLE InstalledSoftware (
    SoftwareID INT IDENTITY(1,1) PRIMARY KEY,
    ComputerID INT NOT NULL,
    Name NVARCHAR(255),
    Version NVARCHAR(100),
    Publisher NVARCHAR(255),
    InstallDate NVARCHAR(20),
    FOREIGN KEY (ComputerID) REFERENCES Computers(ComputerID) ON DELETE CASCADE
);

-- Индексы для производительности
CREATE INDEX IX_Computers_ComputerName ON Computers(ComputerName);
CREATE INDEX IX_InstalledSoftware_ComputerID ON InstalledSoftware(ComputerID);

1.2. Создание учётной записи для записи данных

В SQL Server создайте пользователя (например, inventory_user) с правами на вставку, обновление и выборку данных:

CREATE LOGIN inventory_user WITH PASSWORD = 'StrongPassword123';
USE InventoryDB;
CREATE USER inventory_user FOR LOGIN inventory_user;
GRANT INSERT, UPDATE, SELECT ON Computers TO inventory_user;
GRANT INSERT, UPDATE, SELECT ON Processors TO inventory_user;
-- аналогично для всех таблиц

2. PowerShell-скрипт для сбора данных

Скрипт будет собирать информацию с локального компьютера и отправлять её в базу данных. Он предназначен для выполнения на каждом управляемом компьютере (с правами администратора). Используем модуль SqlServer для работы с базой.

2.1. Установка модуля SqlServer (на каждом компьютере)

powershell

Install-Module -Name SqlServer -Force -AllowClobber

2.2. Создание скрипта Inventory.ps1

powershell

<#
.SYNOPSIS
    Сбор инвентаризационных данных компьютера и запись в SQL Server.
.DESCRIPTION
    Собирает информацию о железе, ОС и ПО, отправляет в центральную БД.
#>

param(
    [string]$SQLServer = "SRV-DB",
    [string]$Database = "InventoryDB",
    [string]$SQLUser = "inventory_user",
    [string]$SQLPassword = "StrongPassword123"
)

# Функция для выполнения SQL-запроса с параметрами
function Execute-SQL {
    param(
        [string]$Query,
        [hashtable]$Parameters
    )
    $connString = "Server=$SQLServer;Database=$Database;User Id=$SQLUser;Password=$SQLPassword;"
    try {
        $conn = New-Object System.Data.SqlClient.SqlConnection($connString)
        $conn.Open()
        $cmd = $conn.CreateCommand()
        $cmd.CommandText = $Query
        foreach ($key in $Parameters.Keys) {
            $cmd.Parameters.AddWithValue($key, $Parameters[$key]) | Out-Null
        }
        $cmd.ExecuteNonQuery() | Out-Null
    }
    catch {
        Write-Error "SQL Error: $_"
    }
    finally {
        if ($conn) { $conn.Close() }
    }
}

# 1. Получаем имя компьютера
$computerName = $env:COMPUTERNAME

# 2. Проверяем, есть ли компьютер в таблице Computers; если нет – добавляем
$checkComputer = @"
IF NOT EXISTS (SELECT 1 FROM Computers WHERE ComputerName = @ComputerName)
    INSERT INTO Computers (ComputerName) VALUES (@ComputerName)
"@
Execute-SQL -Query $checkComputer -Parameters @{ "@ComputerName" = $computerName }

# 3. Получаем ComputerID
$getIDQuery = "SELECT ComputerID FROM Computers WHERE ComputerName = @ComputerName"
$connString = "Server=$SQLServer;Database=$Database;User Id=$SQLUser;Password=$SQLPassword;"
$conn = New-Object System.Data.SqlClient.SqlConnection($connString)
$conn.Open()
$cmd = $conn.CreateCommand()
$cmd.CommandText = $getIDQuery
$cmd.Parameters.AddWithValue("@ComputerName", $computerName) | Out-Null
$reader = $cmd.ExecuteReader()
$computerID = $null
if ($reader.Read()) { $computerID = $reader["ComputerID"] }
$reader.Close()
$conn.Close()

if (-not $computerID) {
    Write-Error "Не удалось получить ComputerID"
    exit 1
}

# 4. Сбор и запись информации о компьютере (производитель, модель, серийник)
$cs = Get-CimInstance -ClassName Win32_ComputerSystem
$manufacturer = $cs.Manufacturer
$model = $cs.Model
$serial = (Get-CimInstance -ClassName Win32_BIOS).SerialNumber

$updateComputerQuery = @"
UPDATE Computers SET Manufacturer = @Manufacturer, Model = @Model, SerialNumber = @SerialNumber, LastSeen = GETDATE()
WHERE ComputerID = @ComputerID
"@
Execute-SQL -Query $updateComputerQuery -Parameters @{
    "@ComputerID" = $computerID
    "@Manufacturer" = $manufacturer
    "@Model" = $model
    "@SerialNumber" = $serial
}

# 5. Процессоры
$processors = Get-CimInstance -ClassName Win32_Processor
foreach ($proc in $processors) {
    $name = $proc.Name
    $cores = $proc.NumberOfCores
    $maxSpeed = $proc.MaxClockSpeed / 1000  # в ГГц
    $insertProcQuery = @"
INSERT INTO Processors (ComputerID, Name, NumberOfCores, MaxClockSpeed)
VALUES (@ComputerID, @Name, @Cores, @Speed)
"@
    Execute-SQL -Query $insertProcQuery -Parameters @{
        "@ComputerID" = $computerID
        "@Name" = $name
        "@Cores" = $cores
        "@Speed" = $maxSpeed
    }
}

# 6. Оперативная память
$memory = Get-CimInstance -ClassName Win32_PhysicalMemory
foreach ($mem in $memory) {
    $capacityGB = $mem.Capacity / 1GB
    $speed = $mem.Speed
    $manufacturerMem = $mem.Manufacturer
    $insertMemQuery = @"
INSERT INTO MemoryModules (ComputerID, CapacityGB, Speed, Manufacturer)
VALUES (@ComputerID, @Capacity, @Speed, @Manufacturer)
"@
    Execute-SQL -Query $insertMemQuery -Parameters @{
        "@ComputerID" = $computerID
        "@Capacity" = $capacityGB
        "@Speed" = $speed
        "@Manufacturer" = $manufacturerMem
    }
}

# 7. Диски (физические диски через Win32_DiskDrive)
$disks = Get-CimInstance -ClassName Win32_DiskDrive
foreach ($disk in $disks) {
    $modelDisk = $disk.Model
    $sizeGB = $disk.Size / 1GB
    $mediaType = $null
    # Попытка определить тип диска (SSD/HDD) через Win32_PhysicalMedia или по модели
    if ($disk.MediaType -like "*SSD*") { $mediaType = "SSD" }
    elseif ($disk.MediaType -like "*HDD*") { $mediaType = "HDD" }
    else { $mediaType = "Unknown" }
    $insertDiskQuery = @"
INSERT INTO Disks (ComputerID, Model, SizeGB, MediaType)
VALUES (@ComputerID, @Model, @Size, @MediaType)
"@
    Execute-SQL -Query $insertDiskQuery -Parameters @{
        "@ComputerID" = $computerID
        "@Model" = $modelDisk
        "@Size" = $sizeGB
        "@MediaType" = $mediaType
    }
}

# 8. Сетевые адаптеры (только физические с IP)
$adapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled -eq $true }
foreach ($adapter in $adapters) {
    $name = $adapter.Description
    $mac = $adapter.MACAddress
    $ip = $adapter.IPAddress[0]  # берём первый IP
    $dhcp = $adapter.DHCPEnabled
    $insertAdapterQuery = @"
INSERT INTO NetworkAdapters (ComputerID, Name, MACAddress, IPAddress, DHCPEnabled)
VALUES (@ComputerID, @Name, @MAC, @IP, @DHCP)
"@
    Execute-SQL -Query $insertAdapterQuery -Parameters @{
        "@ComputerID" = $computerID
        "@Name" = $name
        "@MAC" = $mac
        "@IP" = $ip
        "@DHCP" = $dhcp
    }
}

# 9. Операционная система
$os = Get-CimInstance -ClassName Win32_OperatingSystem
$caption = $os.Caption
$version = $os.Version
$installDate = $os.InstallDate
$lastBoot = $os.LastBootUpTime

$insertOSQuery = @"
INSERT INTO OperatingSystems (ComputerID, Caption, Version, InstallDate, LastBootUpTime)
VALUES (@ComputerID, @Caption, @Version, @InstallDate, @LastBoot)
"@
Execute-SQL -Query $insertOSQuery -Parameters @{
    "@ComputerID" = $computerID
    "@Caption" = $caption
    "@Version" = $version
    "@InstallDate" = $installDate
    "@LastBoot" = $lastBoot
}

# 10. Установленное ПО (из реестра)
$softwareKeys = @(
    "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
    "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
)
$software = foreach ($key in $softwareKeys) {
    Get-ItemProperty $key -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -and $_.DisplayName -notlike "*Update for*" }
}
foreach ($app in $software) {
    $name = $app.DisplayName
    $version = $app.DisplayVersion
    $publisher = $app.Publisher
    $installDate = $app.InstallDate  # обычно в формате YYYYMMDD
    if ($name) {
        $insertSoftwareQuery = @"
INSERT INTO InstalledSoftware (ComputerID, Name, Version, Publisher, InstallDate)
VALUES (@ComputerID, @Name, @Version, @Publisher, @InstallDate)
"@
        Execute-SQL -Query $insertSoftwareQuery -Parameters @{
            "@ComputerID" = $computerID
            "@Name" = $name
            "@Version" = $version
            "@Publisher" = $publisher
            "@InstallDate" = $installDate
        }
    }
}

Write-Output "Инвентаризация для $computerName завершена."

3. Развёртывание скрипта на компьютерах

Есть несколько способов выполнить скрипт на всех компьютерах:

3.1. Групповая политика (запланированное задание)

  1. Создайте групповую политику для компьютеров.
  2. Перейдите в раздел Computer Configuration → Preferences → Control Panel Settings → Scheduled Tasks.
  3. Создайте новую задачу: запуск powershell.exe -ExecutionPolicy Bypass -File "\\server\share\Inventory.ps1" с периодичностью (например, еженедельно).
  4. Разрешите выполнение скрипта из сетевой папки (политика выполнения может быть настроена через GPO).

3.2. Запуск удалённо с центрального сервера

На сервере можно выполнить скрипт, который перебирает компьютеры в Active Directory и запускает Invoke-Command:

powershell

$computers = Get-ADComputer -Filter * | Select-Object -ExpandProperty Name
foreach ($comp in $computers) {
    Invoke-Command -ComputerName $comp -FilePath "\\server\share\Inventory.ps1" -Credential (Get-Credential)
}

3.3. Агент через планировщик задач на каждом компьютере

Можно вручную или через групповую политику создать локальную задачу, которая копирует скрипт локально и запускает его. Это снижает зависимость от сетевой папки.

4. Анализ данных и отчёты

После того как данные собраны, можно строить отчёты с помощью SQL-запросов или использовать инструменты вроде Power BI.

Примеры запросов:

  • Список компьютеров с дисками менее 100 ГБ:

sql

SELECT c.ComputerName, d.Model, d.SizeGB
FROM Computers c
JOIN Disks d ON c.ComputerID = d.ComputerID
WHERE d.SizeGB < 100
ORDER BY c.ComputerName;
  • Количество установленных копий определённого ПО:

sql

SELECT s.Name, s.Version, COUNT(*) AS Count
FROM InstalledSoftware s
GROUP BY s.Name, s.Version
ORDER BY Count DESC;
  • Компьютеры, не обновлявшиеся более 30 дней (по LastSeen):

sql

SELECT ComputerName, LastSeen
FROM Computers
WHERE LastSeen < DATEADD(day, -30, GETDATE());
  • Средний объём ОЗУ по моделям:

sql

SELECT c.Model, AVG(m.CapacityGB) AS AvgRAM
FROM Computers c
JOIN MemoryModules m ON c.ComputerID = m.ComputerID
GROUP BY c.Model;

5. Оптимизация и безопасность

  • Учётные данные: в скрипте пароль SQL записан в открытом виде. Для production используйте Windows Authentication (Integrated Security) и запускайте задачу от имени учётной записи, имеющей доступ к SQL.
  • Сжатие и шифрование: при передаче данных по сети можно использовать WinRM с HTTPS (порт 5986) или VPN.
  • Логирование: добавьте в скрипт запись логов в файл для отладки.
  • Дедупликация ПО: некоторые приложения могут дублироваться в реестре. Добавьте проверку на уникальность при вставке.

Устранение распространённых проблем

ПроблемаВероятная причинаРешение
Ошибка подключения к SQLБрандмауэр блокирует порт 1433, неверные учётные данныеОткрыть порт в Windows Firewall на SQL-сервере. Использовать Integrated Security.
Скрипт не выполняется из-за политики ExecutionPolicyПолитика PowerShell ограничивает выполнениеЗадать Set-ExecutionPolicy Bypass -Scope Process перед запуском или настроить политику через GPO.
Не собираются данные о дисках (Win32_DiskDrive)Недостаточно правЗапускать скрипт с правами администратора (или через SYSTEM).
Дублирование записей в таблицахНет очистки старых данных перед вставкойДобавить удаление старых записей для текущего компьютера перед вставкой новых (например, DELETE FROM Processors WHERE ComputerID = @ComputerID).
Долгое выполнение при большом количестве ПОСбор из реестра может быть медленнымОграничить выборку только определёнными категориями ПО, добавить индексы в базу данных.

Итог

Мы создали систему автоматической инвентаризации железа и программного обеспечения на основе PowerShell и SQL Server. Инвентаризация компьютеров powershell позволяет собирать актуальные данные о каждом компьютере, хранить их в структурированной базе данных и использовать для планирования апгрейдов, лицензионного контроля и анализа. Полученная система легко масштабируется: при добавлении новых компьютеров они автоматически попадают в инвентаризацию через групповую политику или запланированные задачи. Для расширения функциональности можно добавить сбор данных о мониторах, принтерах, установленных обновлениях, а также настроить автоматическую генерацию отчётов и оповещений (например, о нехватке места на дисках).

Menu