Powershell скрипт для завершення програми на термінальному сервері при перевищенні встановленого ліміту CPU(Змінив та доповнив)

Раніше я написав про скрипт який завершує процес користувача на термінальному сервері з підвищення встановленого ліміту з оперативної пам’яті. А зараз надійшло завдання зробити таке але за процесором.

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 та збережемо завдання.

Тепер при завантаженні процесора більше встановленого ліміту спрацює тригер, запустить скрипт і видасть повідомлення користувачеві перед закриттям процесу:

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

Додав:

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

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *