Собираем информацию о компьютерах или WMI ВСЕМОГУЩИЙ!!!
Всем привет из Пензы.
Добра Вам Ребята:)
Сегодня будем использовать Powershell 2.0 и WMI для сбора характеристик компьютера. Почему Powershell 2.0 ведь на момент написания уже существует версия 7.4.0? Да все просто, половина компьютеров под моим управлением все еще на windows 7 и сценарий на версии 2.0 будет работать и для windows 10-11. Поэтому жертвуем функционалом, дабы всё везде работало. Ну и конечно обновить на windows 7 powershell до версии посвежее не предлагать, лень:).
Статья будет писаться не за раз, постепенно буду дополнять, в конце весь результат выгрузим в txt файлы, это тоже не спроста будет продолжение.
Начнем.
Для написания и тестирования я использую PowerShell ISE.
Узнаем имя компьютера и результат поместим в переменную $ComputerName:
$ComputerName = $env:ComputerName
Сразу договоримся что результат всех команд будет помещаться в переменную, это нужно для дальнейшего использования.
Узнаем имя пользователя:
$UserName = $env:UserName
Вытаскиваем нужный IP-адрес:
$ip = Get-WmiObject Win32_NetworkAdapterConfiguration |
Where {$_.IPAddress -and $_.DefaultIPGateway -and $_.DHCPEnabled -like "False"} | select -ExpandProperty IPAddress |
% { $_ -replace "f.*" , "" }
IP адреса есть прописанные руками, есть которые раздаются сервером, есть ipv4 версии, есть ipv6 версии, в конце концов может быть несколько адресов на интерфейсе, на всех не угодишь, да и не надо, у меня статические адреса прописанные руками, один ipv4 адрес на интерфейс, поэтому сценарий выше ищет интерфейс с адресом, у которого прописан шлюз по умолчанию и DHCP выключен, также удаляем найденный ipv6 адрес с помощью { $_ -replace "f.*" , "" }, на выходе получится статический ipv4 адрес у которого на интерфейсе прописан шлюз, а это значит предположительно рабочий адрес.
Определим процессор:
$Name_CPU = Get-WmiObject Win32_Processor | Where-Object { $_.DeviceID -eq 'CPU0'} |
Select-Object -ExpandProperty Name | % {$_ -replace '\s+',' ' }
Вывод будет примерно такой:
Intel(R) Core(TM) i7-2700K CPU @ 3.50GHz
{$_ -replace '\s+',' ' } - удаляет все пробелы во всем выводе и заменяет на один пробел. Это также пригодится дальше.
Узнаем сокет процессора:
$SocketDesignation = Get-WmiObject Win32_Processor | Where-Object { $_.DeviceID -eq 'CPU0'} |
Select-Object -ExpandProperty SocketDesignation
if ($SocketDesignation -eq $Name_CPU)
{
$SocketDesignation = $SocketDesignation -replace 'Intel\(R\) Core\(TM\)|CPU.*' -replace '^\s+|\s+$'
}
Все просто, сокетов может быть не один, т.к. в сети есть сервера и виртуальные машины и чтобы не выводить один и тот же сокет несколько раз, то определяем сокет по CPU0.
Если этот сценарий выполнить на одном компьютере то все хорошо, если его выполнить на 150 компьютерах, то на некоторых из них (у меня таких 4 компьютера), сокет определится как имя процессора, например такое: Intel(R) Core(TM) i3-2100 CPU @ 3.10GHz, мне не нравится такое длинное имя сокета, т.к. в дальнейшем имя будет использоваться, поэтому делаем сравнение сокета с именем процессора и если они равны, то урезаем его до короткого, результат будет таким: i3-2100.
Узнаем количество ядер процессора:
$NumberOfCores = Get-WmiObject Win32_Processor | Select-Object -ExpandProperty NumberOfCores
if($NumberOfCores.Count -gt 1)
{
$NumberOfCores = $NumberOfCores | Measure-Object -Sum | Select-Object -ExpandProperty Sum
}
Виртуальной машине выделено 8 ядер, но WMI может ошибочно определить систему с двумя сокетами и вместо 8 ядер рисует 4 и 4 ядра, поэтому дополнительно проверяем и выводим сумму двух значений, т.е. 8 ядер, это нас устроит, сколько ядер выделили на vm такое значение и получили на выходе.
По части некорректного определения сокетов и ядер, это свойственно для Windows Server на ESXi, но мы это уже поправили выше.
Определяем количество логических ядер:
$NumberOfLogicalProcessors = Get-WmiObject Win32_Processor | Select-Object -ExpandProperty NumberOfLogicalProcessors
if($NumberOfLogicalProcessors.Count -gt 1)
{
$NumberOfLogicalProcessors = $NumberOfLogicalProcessors | Measure-Object -Sum | Select-Object -ExpandProperty Sum
}
С логическими ядрами такая же история как и с физическими.
Узнаем частоту процессора:
$MaxClockSpeed = Get-WmiObject Win32_Processor | Where-Object { $_.DeviceID -eq 'CPU0'} | Select-Object -ExpandProperty MaxClockSpeed
Частоту определяем у CPU0, иначе на некоторых машинах получим несколько значений.
Узнаем общий объем ОЗУ:
$TotalMemory = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty TotalPhysicalMemory
$TotalPhysicalMemory = [math]::Ceiling($TotalMemory /1024 /1024 /1024)
Значение хранится в байтах, поэтому округляем до Гбайт.
Определяем производителя ОЗУ:
$PhysicalMemoryManuf = Get-WmiObject Win32_PhysicalMemory | Where-Object { $_.Manufacturer -ne $null } |
Select-Object -ExpandProperty Manufacturer | % {$_ -replace '\(PDP Systems\)|Virtual RAM' -replace 'Manufacturer?', 'Manuf' -replace '(^\s+|\s+$)' }
$PhysicalMemoryManuf = foreach ($PhysMemManuf in $PhysicalMemoryManuf)
{
Write "($PhysMemManuf)"
}
Поскольку планок может быть не одна, то результат заключаем в скобки. Для наглядности мне удобно видеть результат именно в скобках.
Так же обращаю внимание на редактирование вывода:
{$_ -replace '\(PDP Systems\)|Virtual RAM' -replace 'Manufacturer?', 'Manuf' -replace '(^\s+|\s+$)' } - это делается для вывода короткого значения, виртуальные машины выводят длинные названия, нам это не требуется.
Узнать модель ОЗУ:
$PhysicalMemoryPartN = Get-WmiObject Win32_PhysicalMemory | Where-Object { $_.PartNumber -ne $null } |
Select-Object -ExpandProperty PartNumber | % {$_ -replace '(^\s+|\s+$)',''}
$PhysicalMemoryPartN = foreach ($PhysMemPartN in $PhysicalMemoryPartN)
{
Write "($PhysMemPartN)"
}
Здесь схема таже как и с производителем, результат для наглядности заключаем в скобки.
Отдельно хотелось бы сказать про форматирования вывода с помощью replace:
{$_ -replace '\s+',' '} # Все пробелы заменить на один пробел. Результат: (Kingston KHX1600C10D3/8G )
{$_ -replace '\s+',''} # Удалить все пробелы. Результат: (KingstonKHX1600C10D3/8G)
{$_ -replace '(^\s+|\s+$)'} # Удалить все пробелы в начале и в конце строки. Результат: (Kingston KHX1600C10D3/8G)
{$_ -replace '(^\s+|\s+$)','' -replace '\s+',' ' } # Удалить все пробелы в начале и в конце и заменить все пробелы в середине на 1 пробел. Результат: (Kingston KHX1600C10D3/8G)
Определим объем памяти каждого модуля:
$CapacityMemory = Get-WmiObject Win32_PhysicalMemory | Select-Object -ExpandProperty Capacity
$CapacityMemory = foreach ($CapacMem in $CapacityMemory)
{
$CapacityMemory = [math]::Ceiling($CapacMem /1024 /1024 /1024)
Write "($CapacityMemory GB)"
}
Результат округляем и помещаем в скобки.
Узнать частоту ОЗУ:
$Speed = Get-WmiObject Win32_PhysicalMemory | Select-Object -ExpandProperty Speed
# Ниже конструкция которая перебирает все значения и выводит последнее значение частоты
#$Speed =
foreach ($sp in $Speed)
{
$Speed = Write "($sp MHz)"
}
Определяем версию ОС:
$OSVersion = Get-WmiObject Win32_OperatingSystem | Select-Object -ExpandProperty Version
Узнать объем физических дисков:
$dks = get-WmiObject Win32_DiskDrive | Where-Object { $_.InterfaceType -ne 'USB' } | Select-Object -ExpandProperty Size
$dks = foreach ($dk in $dks)
{
$dks = [math]::Floor($dk /1000 /1000 /1000)
Write "($dks GB)"
}
Нас не интересует объем USB-накопителей которые используют пользователи, поэтому исключаем USB.
###################
Определяем букву логических дисков:
[object[]]$DeviceID = Get-WmiObject Win32_LogicalDisk | Where-Object {$_.Size -ne $null -and $_.DriveType -eq '3'} |
Select-Object -ExpandProperty DeviceID
Фильтрация идёт по типу "3" в который не входят usb и сетевые диски. Также я дополнительно указал что тип данных это массив значений, это пригодится для сопоставления буквы диска с объемом.
Определяем объем свободного места на логическом диске:
$FreeSpace1 = Get-WmiObject Win32_LogicalDisk | Where-Object {$_.Size -ne $null -and $_.DriveType -eq '3'} |
Select-Object -ExpandProperty FreeSpace
Далее мы производим округление результата $FreeSpace1:
[object[]]$FreeSpace1 = foreach ($FSpace1 in $FreeSpace1)
{
$FreeSpace1 = [math]::Round($FSpace1 /1024 /1024 /1024, 1)
Write "$FreeSpace1 GB, "
}
И сопоставляем букву диска со значение свободного места:
$c = 0
$DevFreeSp = ForEach ($DeviceIDaa in $DeviceID)
{
$DevFreeSp = $DeviceID[$c]+" "+$FreeSpace1[$c]
Write "$DevFreeSp"
$c=$c+1
}
###################
Узнаем производителя диска:
$Disks = get-WmiObject Win32_DiskDrive | Where-Object {$_.InterfaceType -ne 'USB'} | Select-Object -ExpandProperty Model |
% { $_ -replace 'SCSI Disk Device','' -replace 'ATA Device','' -replace '(\s+$)','' }
$Disks = foreach ($disk in $Disks)
{
Write "($disk)"
}
Ниже пример очень интересный:)
Узнать администраторов на компьютере, т.е. определяем админов на компе:
$admins = Get-WmiObject Win32_GroupUser |
Where {$_.GroupComponent -like "*Name=`"Администраторы`"" -and $_.PartComponent -notlike "*Name=""Администратор""" } | select -ExpandProperty PartComponent |
% { $_ -replace "\\\\.*Name=" , "" -replace """" , "" -replace '.*Localadmin|.*Администраторы домена', '' } | where { $_ -ne "" }
$admins = foreach ($admin in $admins)
{
Write "($admin)"
}
Узнать производителя материнской платы:
$Manufacturer = Get-WmiObject Win32_BaseBoard | Select-Object -ExpandProperty Manufacturer | % {$_ -replace 'INC.|Co., Ltd.' -replace '\s+$'}
Узнать модель материнской платы:
$Product = Get-WmiObject Win32_BaseBoard | Select-Object -ExpandProperty Product
Определяем имя ОС:
$ProductName = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" |
Select-Object -ExpandProperty ProductName
Здесь название операционной системы вытаскиваем из реестра.
Так же это можно сделать через WMI:
$OS = Get-WmiObject win32_OperatingSystem | select -ExpandProperty Caption
Узнаем к чему относится наша windows к серверной ОС или к клиентской ОС:
$SerWorkHash = @{
"1" = "Workstation";
"2" = "Server";
"3" = "Server";
}
$SerWork = Get-WmiObject win32_OperatingSystem | select -ExpandProperty ProductType | Out-String -Stream
$SerOrWork = $SerWorkHash.$SerWork
Определяем видеокарту:
$Video = Get-WmiObject win32_videocontroller | Where {$_.Name -notlike "*DameWare*"} | Select-Object -ExpandProperty Name | % {$_ -replace '\(Майкрософт\)|\(низкое разрешение\)' -replace '(^\s+|\s+$)','' }
$Video = foreach ($Vid in $Video)
{
Write "($Vid)"
}
В системе может быть несколько видеокарт, одна встроенная в ядро процессора, другая дискретная или может быть только одна.
Узнаем производителя и модель монитора:
Ниже скрипт был взят из ресурса github, я лишь его немного изменил под себя, т.к. мне не требуются серийные номера и т.д.
Обращайте внимание что некоторые строки я просто закомментировал знаком # или <# #>.
$ManufacturerHash = @{
"FPM" = "Advantech";
"SKY" = "Skyworth";
"AUS" = "Asus";
"SAC" = "SunWind";
"AAC" = "AcerView";
"ACR" = "Acer";
"AOC" = "AOC";
"AUO" = "Asus";
"BNQ" = "BenQ";
"CMO" = "Acer";
"CPQ" = "Compaq";
"DEL" = "Dell";
"FUJ" = "Fujitsu";
"FUS" = "Fujitsu-Siemens";
"GSM" = "LG Electronics";
"HEI" = "Hyundai";
"HIT" = "Hyundai";
"HTC" = "Hitachi/Nissei";
"HWP" = "HP";
"IBM" = "IBM";
"ICL" = "Fujitsu ICL";
"IVM" = "Iiyama";
"LEN" = "Lenovo";
"LGD" = "Asus";
"LPL" = "Fujitsu";
"MEI" = "Panasonic";
"MEL" = "Mitsubishi Electronics";
"MS_" = "Panasonic";
"NEC" = "NEC";
"NOK" = "Nokia Data";
"NVD" = "Fujitsu";
"OPT" = "Optoma";
"PHL" = "Philips";
"SAN" = "Samsung";
"SAM" = "Samsung";
"SNY" = "Sony";
"SEC" = "Hewlett-Packard";
"TOS" = "Toshiba";
"TSB" = "Toshiba";
"VSC" = "ViewSonic";
"UNK" = "Unknown";
"_YV" = "Fujitsu";
}
#Creates an empty array to hold the data
$Monitor_Array_Mod = @()
$Monitor_Array_Manuf = @()
#Takes each computer specified and runs the following code:
#ForEach ($Computer in $ComputerName) {
#Grabs the Monitor objects from WMI
$Monitors = Get-WmiObject -Namespace "root\WMI" -Class "WMIMonitorID" -ComputerName $env:ComputerName -ErrorAction SilentlyContinue
#Takes each monitor object found and runs the following code:
ForEach ($Monitor in $Monitors) {
#Grabs respective data and converts it from ASCII encoding and removes any trailing ASCII null values
If ([System.Text.Encoding]::ASCII.GetString($Monitor.UserFriendlyName) -ne $null) {
$Mon_Model = ([System.Text.Encoding]::ASCII.GetString($Monitor.UserFriendlyName)).Replace("$([char]0x0000)","")
} else {
$Mon_Model = $null
}
#$Mon_Serial_Number = ([System.Text.Encoding]::ASCII.GetString($Monitor.SerialNumberID)).Replace("$([char]0x0000)","")
#$Mon_Attached_Computer = ($Monitor.PSComputerName).Replace("$([char]0x0000)","")
$Mon_Manufacturer = ([System.Text.Encoding]::ASCII.GetString($Monitor.ManufacturerName)).Replace("$([char]0x0000)","")
#Filters out "non monitors". Place any of your own filters here. These two are all-in-one computers with built in displays. I don't need the info from these.
If ($Mon_Model -like "*800 AIO*" -or $Mon_Model -like "*8300 AiO*") {Break}
#Sets a friendly name based on the hash table above. If no entry found sets it to the original 3 character code
$Mon_Manufacturer_Friendly = $ManufacturerHash.$Mon_Manufacturer
If ($Mon_Manufacturer_Friendly -eq $null) {
$Mon_Manufacturer_Friendly = $Mon_Manufacturer
}
<#
#Creates a custom monitor object and fills it with 4 NoteProperty members and the respective data
$Monitor_Obj = [PSCustomObject]@{
Manufacturer = $Mon_Manufacturer_Friendly
Model = $Mon_Model
SerialNumber = $Mon_Serial_Number
AttachedComputer = $Mon_Attached_Computer
}
#>
$Mon_Model = write "($Mon_Model)"
$Mon_Manufacturer_Friendly = write "($Mon_Manufacturer_Friendly)"
#Appends the object to the array
$Monitor_Array_Mod += $Mon_Model #= write "($Mon_Model)"
$Monitor_Array_Manuf += $Mon_Manufacturer_Friendly #= write "($Mon_Manufacturer_Friendly)"
#$Monitor_Array += $Monitor_Obj
} #End ForEach Monitor
#Outputs the Array
$MonModel = $Monitor_Array_Mod
$MonManuf = $Monitor_Array_Manuf
# $Monitor_Array
#} #End ForEach Computer
Ну и вишенка на торте:)
Определим принтеры подключенные через USB:
$Printers = Get-WmiObject Win32_Printer -namespace "root\CIMV2" |
Where-Object { $_.PortName -like "USB*" -and $_.Local -like "True" -and $_.WorkOffline -like "False" } |
Select-Object -ExpandProperty Name
If ($Printers -ne $null)
{
$Printers = Write "$env:COMPUTERNAME; $Printers" |
Out-File "\\server\Computer_Info\Printers\$env:COMPUTERNAME.txt" -Encoding default -Append
}
Причем тут вишенка спросите вы? Тут одновременно всё просто и всё сложно. Принтер можно выловить только когда он включен, принтеров много и каждый включает их как вздумается, поэтому вылавливаем активный, локальный USB-принтер и отправляем значение в текстовый файл, с возможностью дозаписывать, т.е. в файле при каждой отработке сценария будет появляться запись если принтер доступен и не будет появляться если он выключен, в течении некоторого времени, допустим неделя, скрипт выловит большинство USB-принтеров.
Все это подготовка к следующему этапу, внесение результатов в таблицу Excel.
И последнее.
Записываем значение всех переменных в одну переменную $ALL:
$ALL = Write "$ComputerName; $UserName; $ip; $SerOrWork; $ProductName; $OSVersion; $Manufacturer; $Product; $Name_CPU; $SocketDesignation; $NumberOfCores; $NumberOfLogicalProcessors; $MaxClockSpeed; $PhysicalMemoryManuf; $PhysicalMemoryPartN; $TotalPhysicalMemory; $CapacityMemory; $Speed; $dks; $DevFreeSp; $Disks; $MonManuf; $MonModel; $Video; $admins"
Далее значение переменной $ALL отправляем в текстовый файл:
$ALL | Out-File \\server\Computer_Info\Computers\$env:COMPUTERNAME.txt
Далее нас ждет внесение результатов в таблицу.
Вышеописанные сценарии и всё что мы проделали это для выполнения на локальном компьютере. Тогда возникает вопрос, как определить характеристики всех компьютеров в сети?
А все просто, т.к. мы работаем в домене, то создаем задачу в групповых политиках и распространяем её на все компьютеры.
Так же чтобы при выполнении сценария у пользователя не мелькало окно Powershell лучше сделать запуск скрипта vbs в котором указан путь на запуск сценария ps1.
Всем хорошего дня, если есть вопросы пишите:)
Комментарии
Отправить комментарий