Раніше я написав про скрипт який завершує процес користувача на термінальному сервері з підвищення встановленого ліміту з оперативної пам’яті. А зараз надійшло завдання зробити таке але за процесором.
function Get-CPUPercent2 {
$lang = ([CultureInfo]::InstalleduICulture).Name
if ($lang -match "ru-") {
$cpucounter = '\Процессор(_Total)\% загруженности процессора'
} else {
$cpucounter = '\processor(_Total)\% processor time'
}
# Отримати дані щодо процесів з використанням Get-Counter
$processes = (Get-Counter $cpucounter -ErrorAction SilentlyContinue).CounterSamples |
Sort-Object CookedValue -Descending
# Сформувати об'єкт із даними про процеси
$processes | Select-Object InstanceName, @{Name='% CPU'; Expression={[math]::Round(($_.CookedValue / ($processes.CookedValue)[0] * 100), 2)}}
}
$hostName = [System.Net.Dns]::GetHostName()
# Отримати поточну дату
$currentDate = (Get-Date).ToString("yyyyMMdd")
$logFilePath = "C:\temp\logfile_$currentDate.txt"
# Отримати CPU відсотків для кожного процесу
$cpuData = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process |
Where-Object { $_.Name -notmatch '^(Idle|System|Total)$' } |
Select-Object -Property IDProcess, Name, @{Name='PercentProcessorTime'; Expression={[math]::Round($_.PercentProcessorTime / (Get-WmiObject Win32_ComputerSystem).NumberOfLogicalProcessors, 2)}} |
Sort-Object -Property PercentProcessorTime -Descending
# Встановити ліміт навантаження
$cpuLimit = 60
# Визначити список процесів, що виключаються
$excludedProcesses = @('Idle', 'explorer', 'dwm', 'umfd', 'svchost', 'winlogon', 'services', 'lsass', 'csrss', 'wininit', 'sqlceip', 'sqlservr', 'System', 'Taskmgr', '_Total')
# Фільтрувати процеси, які перевищують ліміт і не перебувають у списку винятків
$highCpuProcesses = $cpuData | Where-Object {
$_.PercentProcessorTime -gt $cpuLimit -and
-not ($excludedProcesses -contains $_.Name)
}
# Вивести результат у консоль
if ($highCpuProcesses) {
Write-Host "Процеси, що перевищують ліміт за CPU ($cpuLimit%):"
$highCpuProcesses | ForEach-Object {
# Прибрати суфікс #<число> з імені процесу
$processName = $_.Name -replace '#\d+$', ''
Write-Host "Процес: $processName | ID: $($_.IDProcess) | Завантаження CPU:$($highCpuProcesses.PercentProcessorTime)%"
# Отримати інформацію про власника процесу
$ownerInfo = Get-WmiObject Win32_Process -Filter "ProcessId = $($_.IDProcess)"
$userName = $ownerInfo.GetOwner().User
$domain = $ownerInfo.GetOwner().Domain
# Виключити системні облікові записи
if ($userName -ne "SYSTEM" -and $userName -ne "LOCAL SERVICE" -and $userName -ne "NETWORK SERVICE" -and $domain -ne "NT AUTHORITY" -and $userName -ne "") {
$message = "Хост: $hostName`nКористувач: $userName`nПроцес: $processName`nСпоживання CPU: $($highCpuProcesses.PercentProcessorTime)%`nПроцес $processName буде завершено."
$logMessage = "$(Get-Date) - $message"
Add-Content -Path $logFilePath -Value $logMessage -Encoding UTF8
$userMessage = "Ваші $userName дії, у додатку----- $($processName) ----приводять до навантаження на сервер. Цей процес може бути завершений."
Start-Process -FilePath 'msg' -ArgumentList "$userName $userMessage"
try {
# Закрити процес за хвилину
Start-Sleep -Seconds 60
# Повторна перевірка: чи існує процес і чи перевищує він ліміт CPU
$process = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process -Filter "IDProcess = $($_.IDProcess)"
if ($process -and $process.PercentProcessorTime -gt $cpuLimit) {
# Завершити процес
Stop-Process -Id $_.IDProcess -Force -ErrorAction Stop
Write-Host "Процес $processName (ID: $($_.IDProcess)) успішно завершено."
$logMessage = "$(Get-Date) - Процес $processName (ID: $($_.IDProcess)) успішно завершено."
Add-Content -Path $logFilePath -Value $logMessage -Encoding UTF8
} else {
Write-Host "Процес $processName (ID: $($_.IDProcess)) більше не перевищує ліміт по CPU або вже завершено."
$logMessage = "$(Get-Date) - Процес $processName (ID: $($_.IDProcess)) більше не перевищує ліміт по CPU або вже завершено."
Add-Content -Path $logFilePath -Value $logMessage -Encoding UTF8
}
} catch {
Write-Host "Не вдалося завершити процес $processName (ID: $($_.IDProcess)). Помилка: $_"
$logMessage = "$(Get-Date) - Не вдалося завершити процес $processName (ID: $($_.IDProcess)). Помилка: $_"
Add-Content -Path $logFilePath -Value $logMessage -Encoding UTF8
}
} else {
Write-Host "Процес $processName (ID: $($_.IDProcess)) належить до системного облікового запису і не буде завершено."
}
}
} else {
Write-Host "Немає процесів, що перевищують ліміт CPU ($cpuLimit%)."
}
Змінити встановлений ліміт можна у змінній $cpuLimit. Також можна додати у винятки процеси у змінній $excludedProcesses.
Запуск скрипту виконаємо через планувальник Windows. Створимо нове завдання:

А ось тригер ми створимо за подією. Але спочатку налаштуємо оповіщення лічильників PerfMon.
Відкриваємо “Управління комп’ютером” – “Продуктивність” – “Групи збирачів даних” – “Особливі” та створимо групу збирачів даних.


А тепер створимо Alert для моніторингу завантаження CPU.




Тепер встановимо межу завантаження 60%, при перевищенні якої спрацьовуватиме алерт, інтервал опитування лічильника за замовчуванням буде 15 секунд.

Ще потрібно натиснути на Алерт і вибрати “DataCollector01”. Перейти на вкладку “Alert Action” та встановити галочку.

Далі потрібно запустити групу збору інформації та алерт.

Коли спрацює алерт, у журналі (в консолі Event Viewer у розділі Applications and Services Logs\Microsoft\Windows\Diagnosis-PLA\Operational) з’явиться запис з кодом події 2031:

Тепер повернемося до Планувальника та встановимо тригер за подією на вкладці “Тригери”:

На вкладці “Дії” створюємо нову дію:

У полі “Додати аргументи” встановимо: -NoProfile -WindowsStyle hidden -ExecutionPolicy Bypass -File C:\temp\cpu_monitor.ps1 та збережемо завдання.
Тепер при завантаженні процесора більше встановленого ліміту спрацює тригер, запустить скрипт і видасть повідомлення користувачеві перед закриттям процесу:

Також сам лог файл:

Додав:
- Рядок “ if ($userName -ne “SYSTEM” -and $userName -ne “LOCAL SERVICE” -and $userName -ne “NETWORK SERVICE” -and $domain -ne “NT AUTHORITY” -and $userName -ne “”) { “ – виключаємо облікові записи.
- $processName = $_.Name -replace ‘#\d+$’, ” – прибираємо номер після назви процесу.
- $cpucounter = ‘\Процессор(_Total)\% загруженности процессора’ – тепер точніше лічить відсоток завантаженості процесора певним процесом.
- Також додав повторну перевірку чи існує процес і чи перевищує він ліміт CPU та запис у лог файл на кожному етапі.
