Проблема
В любой организации, где есть хотя бы несколько компьютеров и серверов, администратор сталкивается с необходимостью вести учёт оборудования и установленного программного обеспечения. Без систематической инвентаризации невозможно:
- Планировать замену устаревшего железа.
- Контролировать соответствие лицензионного ПО.
- Быстро находить компьютеры с определёнными характеристиками (например, с 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) и выполните:
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. Групповая политика (запланированное задание)
- Создайте групповую политику для компьютеров.
- Перейдите в раздел 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 позволяет собирать актуальные данные о каждом компьютере, хранить их в структурированной базе данных и использовать для планирования апгрейдов, лицензионного контроля и анализа. Полученная система легко масштабируется: при добавлении новых компьютеров они автоматически попадают в инвентаризацию через групповую политику или запланированные задачи. Для расширения функциональности можно добавить сбор данных о мониторах, принтерах, установленных обновлениях, а также настроить автоматическую генерацию отчётов и оповещений (например, о нехватке места на дисках).







