Проблема
В любой организации, где есть хотя бы несколько компьютеров и серверов, администратор сталкивается с необходимостью вести учёт оборудования и установленного программного обеспечения. Без систематической инвентаризации невозможно планировать замену устаревшего железа, контролировать соответствие лицензионного ПО, быстро находить компьютеры с определёнными характеристиками (например, с 8 ГБ ОЗУ для обновления), анализировать распространение вредоносного ПО или нежелательных приложений.
Ручной сбор данных (обход рабочих мест, опрос пользователей, просмотр в Active Directory) это долго, ненадёжно и не даёт актуальной картины в реальном времени. Нужно автоматизировать инвентаризацию. Собирать данные централизованно, хранить в структурированном виде и иметь возможность формировать отчёты.
Решение
Используем PowerShell для сбора информации о железе и ПО на компьютерах Windows. Данные будем сохранять в базу данных SQL Server (или SQL Server Express) для последующего анализа и отчётности. Для сбора информации с удалённых компьютеров используем WMI и CIM (WinRM) или агентный подход через запланированные задачи и Invoke-Command.
В этой статье мы создадим PowerShell скрипт, который собирает следующие данные.
Основные характеристики железа. Производитель, модель, серийный номер, процессор, оперативная память, диски (модель, объём, тип), сетевые адаптеры (MAC, IP).
Данные об ОС. Версия, редакция, последняя перезагрузка, установленные обновления (опционально).
Установленное программное обеспечение. Название, версия, издатель, дата установки (из реестра и WMI).
Скрипт будет выполняться на каждом компьютере (например, через групповую политику или планировщик задач) и отправлять данные на центральный сервер, где они будут записываться в базу данных.
Основные источники: документация Microsoft по WMI, PowerShell CIM cmdlets, SQL Server PowerShell модуль.
Пошаговая инструкция
1. Подготовка базы данных
На сервере (или на любом доступном SQL сервере) создадим базу данных InventoryDB и таблицы для хранения данных.
1.1. Создание базы данных и таблиц
Подключитесь к SQL Server (например, через SSMS или sqlcmd) и выполните следующий скрипт.
sql
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) с правами на вставку, обновление и выборку данных.
sql
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. Групповая политика (запланированное задание)
Создайте групповую политику для компьютеров. Перейдите в раздел Computer Configuration → Preferences → Control Panel Settings → Scheduled Tasks. Создайте новую задачу. Запуск: powershell.exe -ExecutionPolicy Bypass -File "\\server\share\Inventory.ps1" с периодичностью (например, еженедельно). Разрешите выполнение скрипта из сетевой папки (политика выполнения может быть настроена через 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 позволяет собирать актуальные данные о каждом компьютере, хранить их в структурированной базе данных и использовать для планирования апгрейдов, лицензионного контроля и анализа. Полученная система легко масштабируется. При добавлении новых компьютеров они автоматически попадают в инвентаризацию через групповую политику или запланированные задачи. Для расширения функциональности можно добавить сбор данных о мониторах, принтерах, установленных обновлениях, а также настроить автоматическую генерацию отчётов и оповещений (например, о нехватке места на дисках).