"Быстрый
<br />
PowerShell — это не язык программирования для избранных. Это твоя вторая смена на дежурстве. Пять групп задач закрывают 80% ежедневной рутины сетевого инженера: диагностика портов, поиск заблокированных файлов, управление ICMP, контроль дискового пространства и анализ Event Log. Все <a href="https://it-apteka.com/rezervnoe-kopirovanie-mikrotik-routeros-7-v-telegram-avtomatizacija-za-20-minut/" title="Резервное копирование MikroTik RouterOS 7 в Telegram: рабочий скрипт и разбор ошибок" target="_blank" rel="noopener" data-wpil-monitor-id="2190">скрипты ниже — рабочие</a>, проверены в продакшне, копируй и используй.<br />
<h1>PowerShell-скрипты на каждый день для сетевого инженера</h1>
<p>Садимся, завариваем кофе погуще. Ежедневная рутина сетевого инженера — это не красивые дашборды и IaC-пайплайны. Это война с мелочами: «что слушает 5000-й порт?», «кто сожрал весь диск C:?» и «почему пинг не ходит в 3 ночи?».</p>
<p>Вместо того чтобы сто раз делать одно и то же руками — напишем арсенал быстрых и метких скриптов. Не монстров на 500 строк, а инструменты для сиюминутной диагностики. PowerShell Core (7.x) — наш выбор, но всё работает и на классической 5.1.</p>
<p>Статья выросла из исходной версии. Добавил новые скрипты: сканер доступности хостов, <a class="wpil_keyword_link" title="Мониторинг" href="https://it-apteka.com/category/monitoring/" target="_blank" rel="noopener" data-wpil-keyword-link="linked" data-wpil-monitor-id="2124">мониторинг</a> служб Windows, анализ топ-пожирателей диска, проверка DNS, сводный отчёт о системе. Плюс troubleshooting, FAQ и раздел про профиль PowerShell.</p>
<h2>Что нужно перед стартом</h2>
<table style="border-collapse: collapse; width: 100%;" border="1" cellspacing="0" cellpadding="8">
<thead style="background: #f0f4ff;">
<tr>
<th>Компонент</th>
<th>Минимум</th>
<th>Рекомендую</th>
<th>Зачем</th>
</tr>
</thead>
<tbody>
<tr>
<td>PowerShell</td>
<td>5.1 (встроен в Win 10+)</td>
<td>7.x (Core)</td>
<td>Кросс-платформа, нет устаревших командлетов</td>
</tr>
<tr>
<td>Права</td>
<td>Обычный пользователь</td>
<td>Локальный администратор</td>
<td>Firewall, WMI, Event Log требуют прав</td>
</tr>
<tr>
<td>ОС</td>
<td><a title="windows server" href="https://it-apteka.com/tag/windows-server/" target="_blank" rel="noopener" data-wpil-monitor-id="2128">Windows 10 / Server</a> 2016</td>
<td><a title="Автомонтирование сетевых дисков: от костылей Windows 7 до изысков Server 2022" href="https://it-apteka.com/avtomontirovanie-setevyh-diskov-ot-kostylej-windows-7-do-izyskov-server-2022/" target="_blank" rel="noopener" data-wpil-monitor-id="2129">Windows 11 / Server</a> 2022</td>
<td>Командлеты Net* появились в Server 2012 R2</td>
</tr>
<tr>
<td>Execution Policy</td>
<td>RemoteSigned</td>
<td>RemoteSigned</td>
<td>Без этого <a title="PowerShell скрипты в Windows: как создать, запустить и автоматизировать выполнение" href="https://it-apteka.com/powershell-skripty-v-windows-kak-sozdat-zapustit-i-avtomatizirovat-vypolnenie/" target="_blank" rel="noopener" data-wpil-monitor-id="2130">скрипты не запустятся</a></td>
</tr>
</tbody>
</table>
<p>Проверь политику выполнения скриптов:</p>
<pre><code class="language-powershell">
Get-ExecutionPolicy
# Если Restricted - исправь:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
</code></pre>
<p>На момент публикации актуальна версия PowerShell 7.4. Перед установкой проверь свежие релизы на <a href="https://github.com/PowerShell/PowerShell/releases" target="_blank" rel="nofollow noopener">github.com/PowerShell/PowerShell</a>.</p>
<h2>Архитектура: как это всё работает вместе</h2>
<pre class="mermaid">%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
A["$PROFILE (профиль PS)"] --> B["Get-OpenPorts"]
A --> C["Get-LockedFile"]
A --> D["Enable-ICMPEcho"]
A --> E["Get-DiskSpaceReport"]
A --> F["Get-RecentEvents"]
A --> G["Test-HostsAlive"]
A --> H["Get-ServiceStatus"]
A --> I["Get-TopDiskConsumers"]
A --> J["Get-DNSCheck"]
A --> K["Get-SystemSummary"]
B --> L["Get-NetTCPConnection / netstat"]
C --> M["System.IO.File / WMI"]
D --> N["NetFirewallRule"]
E --> O["Get-PSDrive / Get-WmiObject"]
F --> P["Get-WinEvent"]
G --> Q["Test-Connection"]
H --> R["Get-Service"]
I --> S["Get-ChildItem (рекурсия)"]
J --> T["Resolve-DnsName"]
K --> U["CIM / WMI / Net*"]
style A fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style L fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style M fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style N fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style O fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style P fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style Q fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style R fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style S fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style T fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style U fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
</pre>
<p>Все функции живут в профиле PowerShell. Один раз прописал — всегда доступны в любом терминале.</p>
<h2>1. Просмотр открытых портов: кто здесь слушает без спросу</h2>
<p>Команда <code>netstat -ano</code> — старый друг, но её вывод просится в структурированный объект. Чтобы не пялиться в столбики текста, сделаем красиво и с пользой. <code>Get-NetTCPConnection</code> даёт нам PowerShell-объект, а значит — фильтрацию, сортировку и цвет.</p>
<p>Скрипт подсвечивает системные порты ниже 1024 красным. Это не случайно — именно там чаще всего обнаруживается что-то незапланированное.</p>
<pre><code class="language-powershell">
function Get-OpenPorts {
[CmdletBinding()]
param(
[Parameter(Position = 0)]
[string]$FilterByState = 'LISTENING'
)
Write-Host "Ищем открытые порты (состояние: '$FilterByState')..." -ForegroundColor Cyan
Write-Host "PID Протокол Локальный адрес Внешний адрес Состояние Процесс" -ForegroundColor Yellow
Write-Host "--- -------- ------------- ------------ -------- -------" -ForegroundColor Yellow
try {
$networkData = Get-NetTCPConnection -ErrorAction Stop | Where-Object State -eq $FilterByState
}
catch {
Write-Warning "Get-NetTCPConnection не сработал. Пробую netstat."
$netstatResult = netstat -ano -p TCP | Select-String -Pattern '\s+(LISTENING|ESTABLISHED)\s+'
foreach ($line in $netstatResult) {
$parts = $line -split '\s+'
Write-Host ($parts[4..$($parts.Length)] -join ' ') -ForegroundColor Gray
}
return
}
foreach ($conn in $networkData) {
$proc = Get-Process -Id $conn.OwningProcess -ErrorAction SilentlyContinue
$procName = if ($proc) { $proc.ProcessName } else { "N/A" }
$color = if ($conn.LocalPort -lt 1024) { 'Red' } else { 'Green' }
Write-Host ("{0,-5} {1,-8} {2,-22} {3,-20} {4,-10} {5}" -f
$conn.OwningProcess,
'TCP',
"$($conn.LocalAddress):$($conn.LocalPort)",
"$($conn.RemoteAddress):$($conn.RemotePort)",
$conn.State,
$procName) -ForegroundColor $color
}
}
</code></pre>
<p>Запусти так:</p>
<pre><code class="language-powershell">
# Все слушающие порты
Get-OpenPorts
# Только установленные соединения
Get-OpenPorts -FilterByState 'ESTABLISHED'
# Посмотреть конкретный порт
Get-OpenPorts | Where-Object { $_ -match ':8080' }
</code></pre>
<p>Результат: список портов с именем процесса, PID и состоянием. Системные порты — красным.</p>
<h2>2. Поиск заблокированного файла: кто прилепился к файлу как репей</h2>
<p>Блокировка файла — классика жанра. Особенно весело когда лог-файл не ротируется, а база не бэкапится. Встроенные средства <a class="wpil_keyword_link" href="https://it-apteka.com/category/windows-server/" target="_blank" rel="noopener" title="Windows Server" data-wpil-keyword-link="linked" data-wpil-monitor-id="2136">Windows</a> работают через WMI-запрос к командной строке процесса. Это не идеально, но в большинстве случаев достаточно.</p>
<p>Если хочешь гарантированный результат — скачай <a href="https://learn.microsoft.com/ru-ru/sysinternals/downloads/handle" target="_blank" rel="noopener">handle.exe от Sysinternals</a>. Но сначала попробуй встроенное.</p>
<pre><code class="language-powershell">
function Get-LockedFile {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]$Path
)
process {
Write-Host "Пробуем выяснить, кто держит файл '$Path'..." -ForegroundColor Cyan
try {
$fileStream = [System.IO.File]::Open($Path, 'Open', 'Read', 'None')
$fileStream.Close()
Write-Host "Файл свободен." -ForegroundColor Green
}
catch [System.IO.IOException] {
Write-Host "Файл заблокирован! Ищем виновника..." -ForegroundColor Red
$lockingProcess = Get-CimInstance -ClassName Win32_Process |
Where-Object { $_.CommandLine -like "*$Path*" } |
Select-Object ProcessId, Name, CommandLine
if ($lockingProcess) {
$lockingProcess | Format-Table -AutoSize
Write-Host "Принудительно закрыть: Stop-Process -Id $($lockingProcess.ProcessId) -Force" -ForegroundColor Yellow
}
else {
Write-Host "WMI не нашёл процесс. Скачай handle.exe от Sysinternals для точного ответа." -ForegroundColor Yellow
Write-Host "handle.exe `"$Path`"" -ForegroundColor Gray
}
}
}
}
</code></pre>
<pre><code class="language-powershell">
Get-LockedFile -Path "C:\logs\app.log"
</code></pre>
<h2>3. Включаем ICMP: возвращаем пинг в строй</h2>
<p>«Сервер не пингуется!» — эта фраза будит в 3 ночи. Часто дело в фаерволе Windows, который по умолчанию блокирует входящий ICMP. Скрипт проверяет существующее правило и либо создаёт новое, либо включает отключённое.</p>
"Запускать
<br />
Изменение правил брандмауэра требует прав администратора. Запускай PowerShell через "Запуск от имени администратора" или в сессии с UAC-повышением.<br />
<pre><code class="language-powershell">
function Enable-ICMPEcho {
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[Parameter(Position=0)]
[string]$RuleName = "Allow ICMPv4 Echo Request"
)
Write-Host "Включаем ICMP echo для этого хоста..." -ForegroundColor Cyan
$existingRule = Get-NetFirewallRule -DisplayName $RuleName -ErrorAction SilentlyContinue
if ($existingRule) {
Write-Host "Правило уже есть. Включаем." -ForegroundColor Gray
Set-NetFirewallRule -DisplayName $RuleName -Enabled True
}
else {
Write-Host "Создаём правило: '$RuleName'" -ForegroundColor Gray
New-NetFirewallRule `
-DisplayName $RuleName `
-Description "Allow ICMP Echo Request" `
-Protocol ICMPv4 `
-IcmpType 8 `
-Enabled True `
-Profile Any `
-Action Allow
}
$checkRule = Get-NetFirewallRule -DisplayName $RuleName | Select-Object DisplayName, Enabled, Action
Write-Host "Результат:" -ForegroundColor Green
$checkRule | Format-Table -AutoSize
Write-Host "Готово. Проверь пинг с другой машины." -ForegroundColor Green
}
</code></pre>
<pre><code class="language-powershell">
Enable-ICMPEcho
# Откатить:
# Set-NetFirewallRule -DisplayName "Allow ICMPv4 Echo Request" -Enabled False
</code></pre>
<h2>4. Дисковое пространство: диск C: снова на голодном пайке</h2>
<p>Классика с визуализацией. Не просто сухие цифры — прогресс-бар прямо в консоли и предупреждение при критическом заполнении.</p>
<p>Формула в скрипте считает процент заполнения, а не процент свободного места. Не перепутай при анализе результата.</p>
<pre><code class="language-powershell">
function Get-DiskSpaceReport {
[CmdletBinding()]
param(
[Parameter(Position=0)]
[string]$DriveLetter = "C",
[int]$WarnThresholdPct = 20,
[int]$CritThresholdPct = 10
)
Write-Host "Анализируем диск $DriveLetter`:\" -ForegroundColor Cyan
$disk = Get-PSDrive -Name $DriveLetter -ErrorAction SilentlyContinue
if (-not $disk) {
Write-Error "Диск $DriveLetter не найден."
return
}
$totalGB = [math]::Round(($disk.Used + $disk.Free) / 1GB, 2)
$freeGB = [math]::Round($disk.Free / 1GB, 2)
$usedGB = [math]::Round($disk.Used / 1GB, 2)
$freePct = [math]::Round(($disk.Free / ($disk.Used + $disk.Free)) * 100, 1)
$color = if ($freePct -lt $CritThresholdPct) { 'Red' }
elseif ($freePct -lt $WarnThresholdPct) { 'Yellow' }
else { 'Green' }
$filledBlocks = [math]::Round((100 - $freePct) / 5)
$bar = '[' + ('#' * $filledBlocks) + ('-' * (20 - $filledBlocks)) + ']'
Write-Host "Диск: $($disk.Root)" -ForegroundColor White
Write-Host "Всего: $totalGB GB" -ForegroundColor Gray
Write-Host "Занято: $usedGB GB" -ForegroundColor Gray
Write-Host "Свободно: $freeGB GB ($freePct%)" -ForegroundColor $color
Write-Host "Заполнение: $bar" -ForegroundColor $color
if ($freePct -lt $CritThresholdPct) {
Write-Host ""
Write-Host "КРИТИЧНО: свободного места меньше $CritThresholdPct%!" -ForegroundColor Red -BackgroundColor Black
Write-Host "Запусти Get-TopDiskConsumers чтобы найти виновника." -ForegroundColor Yellow
}
elseif ($freePct -lt $WarnThresholdPct) {
Write-Host "Предупреждение: свободного места меньше $WarnThresholdPct%." -ForegroundColor Yellow
}
}
</code></pre>
<pre><code class="language-powershell">
Get-DiskSpaceReport
Get-DiskSpaceReport -DriveLetter D -WarnThresholdPct 30
</code></pre>
<h2>5. Event Log: что там натворила система</h2>
<p>Листать Event Log руками — как искать иголку в стоге сена. Скрипт фильтрует только ошибки и предупреждения за нужный период. Достаточно чтобы понять, почему разбудили в 3 ночи.</p>
<pre><code class="language-powershell">
function Get-RecentEvents {
[CmdletBinding()]
param(
[int]$HoursBack = 24,
[ValidateSet('System', 'Application', 'Security')]
[string]$LogName = 'System',
[int]$MaxEvents = 50
)
Write-Host "Журнал '$LogName', последние $HoursBack часов..." -ForegroundColor Cyan
$startTime = (Get-Date).AddHours(-$HoursBack)
$events = Get-WinEvent -LogName $LogName -MaxEvents $MaxEvents -ErrorAction SilentlyContinue |
Where-Object { $_.TimeCreated -ge $startTime -and ($_.Level -eq 2 -or $_.Level -eq 3) } |
Select-Object TimeCreated, LevelDisplayName, ProviderName, Id, Message |
Sort-Object TimeCreated -Descending
if (-not $events) {
Write-Host "Ничего серьёзного за этот период. Хорошая ночь." -ForegroundColor Green
return
}
Write-Host "Найдено событий: $($events.Count)" -ForegroundColor Yellow
foreach ($event in $events) {
$levelColor = if ($event.LevelDisplayName -eq 'Error') { 'Red' } else { 'Yellow' }
Write-Host "$($event.TimeCreated) [$($event.LevelDisplayName)] $($event.ProviderName) | ID: $($event.Id)" -ForegroundColor $levelColor
$msg = $event.Message -replace '\r?\n', ' '
if ($msg.Length -gt 200) { $msg = $msg.Substring(0, 200) + "..." }
Write-Host " $msg" -ForegroundColor Gray
Write-Host ("-" * 60) -ForegroundColor DarkGray
}
Write-Host "Детали: eventvwr.msc -> фильтр по ID" -ForegroundColor Cyan
}
</code></pre>
<pre><code class="language-powershell">
Get-RecentEvents -HoursBack 2 -LogName Application
Get-RecentEvents -HoursBack 48 -LogName System -MaxEvents 100
</code></pre>
<h2>6. Сканер доступности хостов: пинг по списку</h2>
<p>Новый <a class="wpil_keyword_link" title="Скрипты" href="https://it-apteka.com/category/scripts/" target="_blank" rel="noopener" data-wpil-keyword-link="linked" data-wpil-monitor-id="2127">скрипт</a>. Берёшь список IP или имён хостов, скрипт параллельно проверяет каждый и выдаёт таблицу: живой / мёртвый, время отклика. Незаменим когда нужно быстро проверить сегмент <a class="wpil_keyword_link" title="Сети" href="https://it-apteka.com/category/networks/" target="_blank" rel="noopener" data-wpil-keyword-link="linked" data-wpil-monitor-id="2125">сети</a> или список серверов из инвентаря.</p>
<p>Параллельность через <code>ForEach-Object -Parallel</code> работает только в PowerShell 7+. На 5.1 скрипт автоматически переключается на обычный цикл.</p>
<pre><code class="language-powershell">
function Test-HostsAlive {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string[]]$Hosts,
[int]$TimeoutMs = 1000,
[int]$Count = 2
)
Write-Host "Проверяем $($Hosts.Count) хостов..." -ForegroundColor Cyan
$results = @()
$psVersion = $PSVersionTable.PSVersion.Major
if ($psVersion -ge 7) {
$results = $Hosts | ForEach-Object -Parallel {
$pingResult = Test-Connection -ComputerName $_ -Count $using:Count -TimeoutSeconds ($using:TimeoutMs / 1000) -ErrorAction SilentlyContinue
[PSCustomObject]@{
Host = $_
Status = if ($pingResult) { "ALIVE" } else { "DEAD" }
AvgMs = if ($pingResult) { [math]::Round(($pingResult.Latency | Measure-Object -Average).Average, 1) } else { "-" }
Packets = if ($pingResult) { $pingResult.Count } else { 0 }
}
} -ThrottleLimit 20
}
else {
foreach ($h in $Hosts) {
$pingResult = Test-Connection -ComputerName $h -Count $Count -ErrorAction SilentlyContinue
$results += [PSCustomObject]@{
Host = $h
Status = if ($pingResult) { "ALIVE" } else { "DEAD" }
AvgMs = if ($pingResult) { [math]::Round(($pingResult.ResponseTime | Measure-Object -Average).Average, 1) } else { "-" }
Packets = if ($pingResult) { $pingResult.Count } else { 0 }
}
}
}
$alive = ($results | Where-Object Status -eq "ALIVE").Count
$dead = ($results | Where-Object Status -eq "DEAD").Count
$results | Sort-Object Status, Host | Format-Table -AutoSize | Out-String | ForEach-Object {
$color = if ($_ -match "DEAD") { 'Red' } else { 'White' }
Write-Host $_ -ForegroundColor $color
}
Write-Host "Итого: $alive живых, $dead недоступных из $($Hosts.Count)" -ForegroundColor $(if ($dead -gt 0) { 'Yellow' } else { 'Green' })
}
</code></pre>
<pre><code class="language-powershell">
# Проверить несколько хостов
Test-HostsAlive -Hosts "192.168.1.1", "192.168.1.2", "8.8.8.8", "google.com"
# Из файла со списком
Test-HostsAlive -Hosts (Get-Content "servers.txt")
# Просканировать подсеть /24 быстро
$subnet = "192.168.1"
Test-HostsAlive -Hosts (1..254 | ForEach-Object { "$subnet.$_" }) -TimeoutMs 500
</code></pre>
<h2>7. Мониторинг служб Windows: кто упал пока ты не смотрел</h2>
<p>Классическая задача: пришёл утром, а что-то не работает. Скрипт показывает все остановленные службы с типом запуска «Автоматически» — именно они должны работать. Если служба остановлена и должна стартовать — это повод для расследования.</p>
<pre><code class="language-powershell">
function Get-ServiceStatus {
[CmdletBinding()]
param(
[string[]]$ServiceNames,
[switch]$ShowOnlyStopped,
[switch]$AutoStartOnly
)
Write-Host "Проверяем службы Windows..." -ForegroundColor Cyan
$services = if ($ServiceNames) {
$ServiceNames | ForEach-Object { Get-Service -Name $_ -ErrorAction SilentlyContinue }
}
else {
Get-Service
}
if ($AutoStartOnly) {
$services = $services | Where-Object {
(Get-WmiObject -Class Win32_Service -Filter "Name='$($_.Name)'" -ErrorAction SilentlyContinue).StartMode -eq 'Auto'
}
}
if ($ShowOnlyStopped) {
$services = $services | Where-Object { $_.Status -ne 'Running' }
}
if (-not $services) {
Write-Host "Все службы работают нормально." -ForegroundColor Green
return
}
Write-Host ("{0,-40} {1,-12} {2}" -f "Имя службы", "Статус", "Отображаемое имя") -ForegroundColor Yellow
foreach ($svc in $services | Sort-Object Status, DisplayName) {
$color = switch ($svc.Status) {
'Running' { 'Green' }
'Stopped' { 'Red' }
default { 'Yellow' }
}
Write-Host ("{0,-40} {1,-12} {2}" -f $svc.Name, $svc.Status, $svc.DisplayName) -ForegroundColor $color
}
$stopped = ($services | Where-Object { $_.Status -ne 'Running' }).Count
if ($stopped -gt 0) {
Write-Host "`nОстановлено служб: $stopped" -ForegroundColor Red
Write-Host "Перезапустить: Start-Service -Name 'имя_службы'" -ForegroundColor Yellow
}
}
</code></pre>
<pre><code class="language-powershell">
# Все службы с автозапуском которые не работают
Get-ServiceStatus -AutoStartOnly -ShowOnlyStopped
# Проверить конкретные службы
Get-ServiceStatus -ServiceNames "Spooler", "wuauserv", "BITS"
# Все службы
Get-ServiceStatus
</code></pre>
<h2>8. Топ пожирателей диска: найди кто съел всё место</h2>
<p>Диск заполнен, нужно найти виновника. Скрипт рекурсивно обходит папку и возвращает топ-N самых жирных файлов или папок. Незаменим когда 200 гигабайт «куда-то ушли» и ты не знаешь куда.</p>
<p>Осторожно с рекурсией на системных папках — это может занять несколько минут на медленных дисках.</p>
<pre><code class="language-powershell">
function Get-TopDiskConsumers {
[CmdletBinding()]
param(
[string]$Path = "C:\",
[int]$Top = 20,
[switch]$ByFolder,
[string]$Extension
)
Write-Host "Ищем топ-$Top пожирателей пространства в $Path ..." -ForegroundColor Cyan
Write-Host "Это может занять минуту. Потерпи." -ForegroundColor Gray
if ($ByFolder) {
$items = Get-ChildItem -Path $Path -Directory -ErrorAction SilentlyContinue | ForEach-Object {
$size = (Get-ChildItem -Path $_.FullName -Recurse -File -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
[PSCustomObject]@{
Path = $_.FullName
SizeGB = [math]::Round($size / 1GB, 3)
SizeMB = [math]::Round($size / 1MB, 1)
}
} | Sort-Object SizeGB -Descending | Select-Object -First $Top
}
else {
$filter = if ($Extension) { "*.$Extension" } else { "*" }
$items = Get-ChildItem -Path $Path -Recurse -File -Filter $filter -ErrorAction SilentlyContinue |
Sort-Object Length -Descending |
Select-Object -First $Top |
ForEach-Object {
[PSCustomObject]@{
File = $_.FullName
SizeGB = [math]::Round($_.Length / 1GB, 3)
SizeMB = [math]::Round($_.Length / 1MB, 1)
Modified = $_.LastWriteTime
}
}
}
$items | Format-Table -AutoSize
$total = ($items | Measure-Object -Property SizeMB -Sum).Sum
Write-Host "Суммарно топ-$Top занимают: $([math]::Round($total / 1024, 2)) GB" -ForegroundColor Yellow
}
</code></pre>
<pre><code class="language-powershell">
# Топ-20 самых больших файлов на диске C:
Get-TopDiskConsumers -Path "C:\" -Top 20
# Топ папок
Get-TopDiskConsumers -Path "C:\" -ByFolder -Top 10
# Только .log файлы
Get-TopDiskConsumers -Path "C:\logs" -Extension "log" -Top 15
# Быстро найти .dmp файлы (дампы памяти)
Get-TopDiskConsumers -Path "C:\Windows" -Extension "dmp" -Top 5
</code></pre>
<h2>9. Проверка DNS: резолвится или нет</h2>
<p>Простой, но часто нужный инструмент. Проверяет резолвинг хостов через системный <a class="wpil_keyword_link" title="DNS" href="https://it-apteka.com/tag/dns/" target="_blank" rel="noopener" data-wpil-keyword-link="linked" data-wpil-monitor-id="2126">DNS</a>, дополнительный DNS и через публичный резолвер. Сразу видно где рвётся цепочка.</p>
<pre><code class="language-powershell">
function Get-DNSCheck {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string[]]$Hostnames,
[string[]]$DNSServers = @("8.8.8.8", "1.1.1.1")
)
Write-Host "Проверяем DNS-резолвинг..." -ForegroundColor Cyan
foreach ($hostname in $Hostnames) {
Write-Host "`n[$hostname]" -ForegroundColor White
# Системный DNS
try {
$sysResult = Resolve-DnsName -Name $hostname -ErrorAction Stop
$sysIPs = ($sysResult | Where-Object { $_.Type -in 'A','AAAA' }).IPAddress -join ', '
Write-Host " Системный DNS: $sysIPs" -ForegroundColor Green
}
catch {
Write-Host " Системный DNS: ОШИБКА - $($_.Exception.Message)" -ForegroundColor Red
}
# Альтернативные DNS-серверы
foreach ($dns in $DNSServers) {
try {
$altResult = Resolve-DnsName -Name $hostname -Server $dns -ErrorAction Stop
$altIPs = ($altResult | Where-Object { $_.Type -in 'A','AAAA' }).IPAddress -join ', '
Write-Host " $dns : $altIPs" -ForegroundColor Green
}
catch {
Write-Host " $dns : ОШИБКА" -ForegroundColor Red
}
}
}
}
</code></pre>
<pre><code class="language-powershell">
Get-DNSCheck -Hostnames "google.com", "internal.corp", "api.example.com"
Get-DNSCheck -Hostnames "mail.corp" -DNSServers "10.0.0.1", "8.8.8.8"
</code></pre>
<h2>10. Сводный отчёт о системе: всё сразу одной командой</h2>
<p>Иногда нужно быстро осмотреться на новом сервере или при старте диагностики. Этот скрипт собирает в одном месте: информацию о железе, сетевых интерфейсах, топ-процессах по памяти и CPU, и статус критических служб. Вроде fastfetch, но для инженера.</p>
<pre><code class="language-powershell">
function Get-SystemSummary {
[CmdletBinding()]
param(
[switch]$SkipNetwork,
[switch]$SkipProcesses
)
Write-Host "=== СИСТЕМНЫЙ ОТЧЁТ ===" -ForegroundColor Cyan
Write-Host "Дата: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Gray
# Базовая информация
$os = Get-CimInstance -ClassName Win32_OperatingSystem
$cpu = Get-CimInstance -ClassName Win32_Processor
$cs = Get-CimInstance -ClassName Win32_ComputerSystem
Write-Host "`n[Система]" -ForegroundColor Yellow
Write-Host " Хост: $($env:COMPUTERNAME)" -ForegroundColor White
Write-Host " ОС: $($os.Caption) $($os.OSArchitecture)" -ForegroundColor White
Write-Host " Аптайм: $(New-TimeSpan -Start $os.LastBootUpTime -End (Get-Date) | Select-Object -ExpandProperty ToString)" -ForegroundColor White
Write-Host " CPU: $($cpu.Name)" -ForegroundColor White
Write-Host " RAM всего: $([math]::Round($cs.TotalPhysicalMemory / 1GB, 1)) GB" -ForegroundColor White
$freeRam = [math]::Round($os.FreePhysicalMemory / 1MB, 1)
Write-Host " RAM свободно: $freeRam GB" -ForegroundColor $(if ($freeRam -lt 1) { 'Red' } else { 'Green' })
# Диски
Write-Host "`n[Диски]" -ForegroundColor Yellow
Get-PSDrive -PSProvider FileSystem | ForEach-Object {
if ($_.Used -gt 0 -or $_.Free -gt 0) {
$total = $_.Used + $_.Free
$freePct = [math]::Round(($_.Free / $total) * 100, 1)
$color = if ($freePct -lt 10) { 'Red' } elseif ($freePct -lt 20) { 'Yellow' } else { 'Green' }
Write-Host (" {0,-4} Всего: {1,6} GB Свободно: {2,6} GB ({3}%)" -f
"$($_.Name):\",
[math]::Round($total/1GB,1),
[math]::Round($_.Free/1GB,1),
$freePct) -ForegroundColor $color
}
}
# Сеть
if (-not $SkipNetwork) {
Write-Host "`n[Сеть]" -ForegroundColor Yellow
Get-NetIPAddress -AddressFamily IPv4 |
Where-Object { $_.IPAddress -ne '127.0.0.1' } |
Select-Object InterfaceAlias, IPAddress, PrefixLength |
ForEach-Object {
Write-Host " $($_.InterfaceAlias): $($_.IPAddress)/$($_.PrefixLength)" -ForegroundColor White
}
}
# Топ-процессов
if (-not $SkipProcesses) {
Write-Host "`n[Топ-7 процессов по памяти]" -ForegroundColor Yellow
Get-Process |
Sort-Object WorkingSet -Descending |
Select-Object -First 7 |
ForEach-Object {
Write-Host (" {0,-30} {1,6} MB" -f $_.ProcessName, [math]::Round($_.WorkingSet/1MB, 1)) -ForegroundColor Gray
}
}
Write-Host "`n=== КОНЕЦ ОТЧЁТА ===" -ForegroundColor Cyan
}
</code></pre>
<pre><code class="language-powershell">
# Полный отчёт
Get-SystemSummary
# Без сети и процессов - быстро
Get-SystemSummary -SkipNetwork -SkipProcesses
</code></pre>
<h2>Собираем всё в профиль PowerShell</h2>
<p>Смысл всего этого — иметь инструменты под рукой в любой момент, не искать скрипты по папкам. Профиль PowerShell загружается при каждом открытии терминала. Добавь туда все функции.</p>
<pre><code class="language-powershell">
# Открыть профиль для редактирования
notepad $PROFILE
# Если профиля нет - создать
if (-not (Test-Path $PROFILE)) {
New-Item -ItemType File -Path $PROFILE -Force
}
</code></pre>
<p>В файл профиля добавь dot-sourcing или сами функции. Рекомендую вынести в отдельный файл:</p>
<pre><code class="language-powershell">
# В $PROFILE добавь:
. "$HOME\Documents\PowerShell\MyFunctions.ps1"
</code></pre>
<p>Туда же — алиасы для быстрого вызова:</p>
<pre><code class="language-powershell">
Set-Alias -Name ports -Value Get-OpenPorts
Set-Alias -Name wholocksit -Value Get-LockedFile
Set-Alias -Name allowping -Value Enable-ICMPEcho
Set-Alias -Name diskcheck -Value Get-DiskSpaceReport
Set-Alias -Name eventcheck -Value Get-RecentEvents
Set-Alias -Name hostscan -Value Test-HostsAlive
Set-Alias -Name svccheck -Value Get-ServiceStatus
Set-Alias -Name bigfiles -Value Get-TopDiskConsumers
Set-Alias -Name dnscheck -Value Get-DNSCheck
Set-Alias -Name sysinfo -Value Get-SystemSummary
</code></pre>
<p>После редактирования профиль перечитывается:</p>
<pre><code class="language-powershell">
. $PROFILE
</code></pre>
<h2>Диагностика рабочего дня: типовой сценарий</h2>
<pre class="mermaid">%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
A["Утро. Что-то не работает."] --> B["sysinfo - общий осмотр"]
B --> C{Диск забит?}
C -->|Да| D["bigfiles - найти пожирателей"]
C -->|Нет| E{Сервис упал?}
E -->|Да| F["svccheck -AutoStartOnly -ShowOnlyStopped"]
E -->|Нет| G{Сеть не отвечает?}
G -->|Да| H["hostscan - проверить хосты"]
H --> I["dnscheck - проверить DNS"]
G -->|Нет| J{Ошибки в логах?}
J -->|Да| K["eventcheck -HoursBack 24"]
J -->|Нет| L["ports - проверить слушающие порты"]
L --> M["Нашёл? Разобрался."]
K --> M
I --> M
F --> M
D --> M
style A fill:#fef9c3,stroke:#eab308,stroke-width:2px,color:#713f12
style M fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#15803d
style D fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
style F fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
style H fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
style I fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
style K fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
style L fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
</pre>
<h2>Troubleshooting: когда что-то пошло не так</h2>
<table style="border-collapse: collapse; width: 100%;" border="1" cellspacing="0" cellpadding="8">
<thead style="background: #fff4f4;">
<tr>
<th>Ошибка</th>
<th>Причина</th>
<th>Решение</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>File cannot be loaded because running scripts is disabled</code></td>
<td>Execution Policy = Restricted</td>
<td><code>Set-ExecutionPolicy RemoteSigned -Scope CurrentUser</code></td>
</tr>
<tr>
<td><code>Access to the path is denied</code></td>
<td>Нет прав администратора</td>
<td>Перезапусти PowerShell «от имени администратора»</td>
</tr>
<tr>
<td><code>Get-NetTCPConnection: The term is not recognized</code></td>
<td><a title="Активация Windows 11 и Office через PowerShell: команды, скрипты, диагностика" href="https://it-apteka.com/aktivacija-windows-11-i-office-cherez-powershell-komandy-skripty-diagnostika/" target="_blank" rel="noopener" data-wpil-monitor-id="2131">PowerShell 2.0 или Windows</a> 7</td>
<td>Обновись до PS 5.1+. На старых системах используй netstat вручную.</td>
</tr>
<tr>
<td><code>Get-WinEvent: No events found</code></td>
<td>Журнал пуст или период слишком мал</td>
<td>Увеличь -HoursBack. Проверь, включён ли журнал: <code>Get-WinEvent -ListLog Application</code></td>
</tr>
<tr>
<td><code>ForEach-Object -Parallel: The parameter 'Parallel' not found</code></td>
<td>PowerShell 5.1 не поддерживает -Parallel</td>
<td>Скрипт автоматически fallback на обычный цикл. Обнови до PS 7 для скорости.</td>
</tr>
<tr>
<td><code>New-NetFirewallRule: Access denied</code></td>
<td>Нет прав на изменение фаервола</td>
<td>Обязательно запускай Enable-ICMPEcho от администратора</td>
</tr>
<tr>
<td><code>Resolve-DnsName: DNS name does not exist</code></td>
<td>Хост не резолвится через системный DNS</td>
<td>Проверь через альтернативный DNS: <code>-DNSServers "8.8.8.8"</code></td>
</tr>
<tr>
<td>Get-TopDiskConsumers завис</td>
<td>Рекурсия на C:\ по миллиону файлов</td>
<td>Указывай конкретную папку: <code>-Path "C:\Logs"</code></td>
</tr>
<tr>
<td>Профиль не загружается</td>
<td>Путь к файлу с пробелами, Execution Policy</td>
<td>Проверь <code>$PROFILE</code>, убедись что файл существует и нет ошибок синтаксиса</td>
</tr>
</tbody>
</table>
<h2>Безопасность: что нельзя забывать</h2>
"Принципы
<br />
PowerShell — мощный инструмент. Именно поэтому злоумышленники его любят не меньше, чем администраторы. Несколько правил которые не стоит игнорировать.<br />
<ul>
<li>Не храни учётные данные в скриптах открытым текстом. Используй <code>Get-Credential</code> или Windows Credential Manager.</li>
<li>Не давай всем скриптам ExecutionPolicy Unrestricted. RemoteSigned достаточно для большинства задач.</li>
<li>Enable-ICMPEcho открывает хост для пинга из любой сети (Profile Any). В продакшне уточни профиль: <code>-Profile Domain</code> или <code>-Profile Private</code>.</li>
<li>Логируй действия в продакшне. PowerShell умеет писать лог через <code>Start-Transcript</code>.</li>
<li>Скрипт <code>Stop-Process -Force</code> не спрашивает подтверждения. Проверь PID прежде чем убивать процесс.</li>
<li>Не запускай скрипты от администратора без необходимости. Принцип минимальных привилегий работает и в PowerShell.</li>
</ul>
<pre><code class="language-powershell">
# Логировать сессию в файл - полезно при работе с продакшн-серверами
Start-Transcript -Path "$HOME\ps_log_$(Get-Date -Format 'yyyyMMdd_HHmm').txt"
# ... работаешь ...
Stop-Transcript
</code></pre>
<h2>Профилактика: как не чинить то, что сломается завтра</h2>
<p>Скрипты написаны. Теперь сделай так, чтобы они работали на автопилоте, а не ждали когда ты вспомнишь запустить их вручную.</p>
<p>Создай задачу в планировщике для ежедневного отчёта о дисках и службах:</p>
<pre><code class="language-powershell">
# Создать задачу - запускать каждое утро в 08:00
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NonInteractive -File C:\Scripts\DailyCheck.ps1 -OutputPath C:\Reports\"
$trigger = New-ScheduledTaskTrigger -Daily -At "08:00"
$settings = New-ScheduledTaskSettingsSet -RunOnlyIfNetworkAvailable
Register-ScheduledTask -TaskName "DailyServerCheck" `
-Action $action -Trigger $trigger -Settings $settings `
-RunLevel Highest -Description "Ежедневная диагностика"
</code></pre>
<p>Файл DailyCheck.<a class="wpil_keyword_link" title="PowerShell" href="https://it-apteka.com/tag/powershell/" target="_blank" rel="noopener" data-wpil-keyword-link="linked" data-wpil-monitor-id="2123">ps1</a> — вызывает твои функции и пишет результат в файл или отправляет на почту:</p>
<pre><code class="language-powershell">
# DailyCheck.ps1
param([string]$OutputPath = "C:\Reports\")
. "$HOME\Documents\PowerShell\MyFunctions.ps1"
$reportFile = Join-Path $OutputPath "report_$(Get-Date -Format 'yyyyMMdd').txt"
Start-Transcript -Path $reportFile
Get-SystemSummary
Get-ServiceStatus -AutoStartOnly -ShowOnlyStopped
Get-DiskSpaceReport -DriveLetter C
Stop-Transcript
# Отправить письмо если есть проблемы (опционально)
# Send-MailMessage -To "admin@corp.ru" -Subject "Daily Check" -Body (Get-Content $reportFile -Raw) -SmtpServer "mail.corp.ru"
</code></pre>
<p>Одна строка в crontab без комментария убивает весь отдел. Комментируй задачи планировщика — твой будущий я скажет спасибо.</p>
<h2>Альтернативные решения</h2>
<table style="border-collapse: collapse; width: 100%;" border="1" cellspacing="0" cellpadding="8">
<thead style="background: #f0f8ff;">
<tr>
<th>Задача</th>
<th>PowerShell (эта статья)</th>
<th>Альтернативы</th>
</tr>
</thead>
<tbody>
<tr>
<td>Открытые порты</td>
<td>Get-OpenPorts</td>
<td>netstat, <a href="https://nmap.org" target="_blank" rel="noopener">nmap</a>, TCPView от Sysinternals</td>
</tr>
<tr>
<td>Блокировка файлов</td>
<td>Get-LockedFile</td>
<td><a href="https://learn.microsoft.com/ru-ru/sysinternals/downloads/handle" target="_blank" rel="noopener">handle.exe</a>, <a href="https://www.nirsoft.net/utils/opened_files_view.html" target="_blank" rel="noopener">OpenedFilesView</a></td>
</tr>
<tr>
<td>Дисковое пространство</td>
<td>Get-DiskSpaceReport + Get-TopDiskConsumers</td>
<td><a href="https://windirstat.net/" target="_blank" rel="noopener">WinDirStat</a>, <a href="https://www.diskanalyzer.com/" target="_blank" rel="noopener">TreeSize</a></td>
</tr>
<tr>
<td>Event Log</td>
<td>Get-RecentEvents</td>
<td>eventvwr.msc, <a href="https://www.nirsoft.net/utils/my_event_viewer.html" target="_blank" rel="noopener">MyEventViewer</a></td>
</tr>
<tr>
<td>Ping / сканирование</td>
<td>Test-HostsAlive</td>
<td>nmap, <a href="https://angryip.org/" target="_blank" rel="noopener">Angry IP Scanner</a></td>
</tr>
<tr>
<td>Полный мониторинг</td>
<td>Get-SystemSummary</td>
<td><a href="https://learn.microsoft.com/ru-ru/sysinternals/downloads/process-monitor" target="_blank" rel="noopener">Process Monitor</a>, <a href="https://www.zabbix.com/ru" target="_blank" rel="noopener">Zabbix</a></td>
</tr>
</tbody>
</table>
<p>PowerShell выигрывает там, где нужна автоматизация, интеграция в пайплайн и работа без GUI. Для разовой визуальной диагностики — сторонние утилиты удобнее.</p>
<h2>FAQ</h2>
<h3>Как запустить скрипт PowerShell если написано «запуск скриптов отключён»?</h3>
<p>Это Execution Policy. Открой PowerShell от администратора и выполни:</p>
<pre><code class="language-powershell">
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
</code></pre>
<p>RemoteSigned разрешает запускать локальные скрипты без подписи, но требует подпись для скачанных из интернета. Это разумный компромисс между безопасностью и удобством.</p>
<h3>Почему Get-OpenPorts не показывает UDP-порты?</h3>
<p>Командлет <code>Get-NetTCPConnection</code> работает только с TCP. Для UDP используй отдельный запрос:</p>
<pre><code class="language-powershell">
Get-NetUDPEndpoint | Select-Object LocalAddress, LocalPort, OwningProcess,
@{N='Process';E={(Get-Process -Id $_.OwningProcess -EA SilentlyContinue).ProcessName}} |
Sort-Object LocalPort | Format-Table -AutoSize
</code></pre>
<h3>Как проверить, что профиль PowerShell загружается корректно?</h3>
<pre><code class="language-powershell">
# Посмотреть путь к профилю
$PROFILE
# Проверить, что файл существует
Test-Path $PROFILE
# Проверить на синтаксические ошибки перед загрузкой
. $PROFILE
# Если ошибок нет - тихо выполнится. Ошибки появятся в консоли.
</code></pre>
<h3>Test-HostsAlive медленно работает на PS 5.1 — как ускорить?</h3>
<p>На PS 5.1 нет <code>-Parallel</code>, поэтому хосты проверяются последовательно. Варианта два: обновиться до PowerShell 7, или использовать Jobs:</p>
<pre><code class="language-powershell">
# Параллельная проверка через Jobs на PS 5.1
$jobs = $hosts | ForEach-Object {
$h = $_
Start-Job -ScriptBlock { Test-Connection -ComputerName $using:h -Count 1 -Quiet }
}
$jobs | Wait-Job | Receive-Job
$jobs | Remove-Job
</code></pre>
<h3>Можно ли эти скрипты запускать на удалённых серверах?</h3>
<p>Да. Используй <code>Invoke-Command</code>:</p>
<pre><code class="language-powershell">
# Запустить функцию на удалённом сервере
Invoke-Command -ComputerName "server01.corp" -ScriptBlock ${function:Get-DiskSpaceReport}
# Или передать функцию явно
$funcDef = ${function:Get-DiskSpaceReport}.ToString()
Invoke-Command -ComputerName "server01", "server02" -ScriptBlock {
param($funcDef)
Invoke-Expression "function Get-DiskSpaceReport { $funcDef }"
Get-DiskSpaceReport
} -ArgumentList $funcDef
</code></pre>
<p>WinRM должен быть включён на целевых серверах: <code>Enable-PSRemoting -Force</code>.</p>
<h3>Что делать если Event Log показывает тысячи ошибок?</h3>
<p>Сначала сгруппируй по источнику — это покажет системный паттерн, а не случайный шум:</p>
<pre><code class="language-powershell">
Get-WinEvent -LogName System -MaxEvents 1000 -ErrorAction SilentlyContinue |
Where-Object { $_.Level -eq 2 } |
Group-Object ProviderName |
Sort-Object Count -Descending |
Select-Object -First 10 Count, Name |
Format-Table -AutoSize
</code></pre>
<p>Если одна служба генерирует 90% ошибок — начинай расследование с неё, а не с разбора каждого события.</p>
<h2>Итог</h2>
<p>Десять функций — это не коллекция скриптов ради коллекции. Это рабочий набор для конкретных задач: диагностика, поиск виновника, контроль состояния системы. Добавь всё в профиль, потрать полчаса один раз — и экономишь часы каждую неделю.</p>
<p>PowerShell — инструмент для тех кто хочет понять что происходит, а не просто нажать кнопку «починить». Разбирайся в том что запускаешь. Тогда и в 3 ночи будет понятно куда смотреть.</p>
"Если
<br />
Пиши в комментарии — разберёмся. Укажи версию PowerShell, ОС и текст ошибки. Без этого диагностика превращается в гадание.<br />
Быстрый ответ
PowerShell — это не язык программирования для избранных. Это твоя вторая смена на дежурстве. Пять групп задач закрывают 80% ежедневной рутины сетевого инженера: диагностика портов, поиск заблокированных файлов, управление ICMP, контроль дискового пространства и анализ Event Log. Все
скрипты ниже — рабочие, проверены в продакшне, копируй и используй.
PowerShell-скрипты на каждый день для сетевого инженера
Садимся, завариваем кофе погуще. Ежедневная рутина сетевого инженера — это не красивые дашборды и IaC-пайплайны. Это война с мелочами: «что слушает 5000-й порт?», «кто сожрал весь диск C:?» и «почему пинг не ходит в 3 ночи?».
Вместо того чтобы сто раз делать одно и то же руками — напишем арсенал быстрых и метких скриптов. Не монстров на 500 строк, а инструменты для сиюминутной диагностики. PowerShell Core (7.x) — наш выбор, но всё работает и на классической 5.1.
Статья выросла из исходной версии. Добавил новые скрипты: сканер доступности хостов, мониторинг служб Windows, анализ топ-пожирателей диска, проверка DNS, сводный отчёт о системе. Плюс troubleshooting, FAQ и раздел про профиль PowerShell.
Что нужно перед стартом
| Компонент |
Минимум |
Рекомендую |
Зачем |
| PowerShell |
5.1 (встроен в Win 10+) |
7.x (Core) |
Кросс-платформа, нет устаревших командлетов |
| Права |
Обычный пользователь |
Локальный администратор |
Firewall, WMI, Event Log требуют прав |
| ОС |
Windows 10 / Server 2016 |
Windows 11 / Server 2022 |
Командлеты Net* появились в Server 2012 R2 |
| Execution Policy |
RemoteSigned |
RemoteSigned |
Без этого скрипты не запустятся |
Проверь политику выполнения скриптов:
Get-ExecutionPolicy
# Если Restricted - исправь:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
На момент публикации актуальна версия PowerShell 7.4. Перед установкой проверь свежие релизы на github.com/PowerShell/PowerShell.
Архитектура: как это всё работает вместе
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
A["$PROFILE (профиль PS)"] --> B["Get-OpenPorts"]
A --> C["Get-LockedFile"]
A --> D["Enable-ICMPEcho"]
A --> E["Get-DiskSpaceReport"]
A --> F["Get-RecentEvents"]
A --> G["Test-HostsAlive"]
A --> H["Get-ServiceStatus"]
A --> I["Get-TopDiskConsumers"]
A --> J["Get-DNSCheck"]
A --> K["Get-SystemSummary"]
B --> L["Get-NetTCPConnection / netstat"]
C --> M["System.IO.File / WMI"]
D --> N["NetFirewallRule"]
E --> O["Get-PSDrive / Get-WmiObject"]
F --> P["Get-WinEvent"]
G --> Q["Test-Connection"]
H --> R["Get-Service"]
I --> S["Get-ChildItem (рекурсия)"]
J --> T["Resolve-DnsName"]
K --> U["CIM / WMI / Net*"]
style A fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style L fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style M fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style N fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style O fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style P fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style Q fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style R fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style S fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style T fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
style U fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#15803d
Все функции живут в профиле PowerShell. Один раз прописал — всегда доступны в любом терминале.
1. Просмотр открытых портов: кто здесь слушает без спросу
Команда netstat -ano — старый друг, но её вывод просится в структурированный объект. Чтобы не пялиться в столбики текста, сделаем красиво и с пользой. Get-NetTCPConnection даёт нам PowerShell-объект, а значит — фильтрацию, сортировку и цвет.
Скрипт подсвечивает системные порты ниже 1024 красным. Это не случайно — именно там чаще всего обнаруживается что-то незапланированное.
function Get-OpenPorts {
[CmdletBinding()]
param(
[Parameter(Position = 0)]
[string]$FilterByState = 'LISTENING'
)
Write-Host "Ищем открытые порты (состояние: '$FilterByState')..." -ForegroundColor Cyan
Write-Host "PID Протокол Локальный адрес Внешний адрес Состояние Процесс" -ForegroundColor Yellow
Write-Host "--- -------- ------------- ------------ -------- -------" -ForegroundColor Yellow
try {
$networkData = Get-NetTCPConnection -ErrorAction Stop | Where-Object State -eq $FilterByState
}
catch {
Write-Warning "Get-NetTCPConnection не сработал. Пробую netstat."
$netstatResult = netstat -ano -p TCP | Select-String -Pattern '\s+(LISTENING|ESTABLISHED)\s+'
foreach ($line in $netstatResult) {
$parts = $line -split '\s+'
Write-Host ($parts[4..$($parts.Length)] -join ' ') -ForegroundColor Gray
}
return
}
foreach ($conn in $networkData) {
$proc = Get-Process -Id $conn.OwningProcess -ErrorAction SilentlyContinue
$procName = if ($proc) { $proc.ProcessName } else { "N/A" }
$color = if ($conn.LocalPort -lt 1024) { 'Red' } else { 'Green' }
Write-Host ("{0,-5} {1,-8} {2,-22} {3,-20} {4,-10} {5}" -f
$conn.OwningProcess,
'TCP',
"$($conn.LocalAddress):$($conn.LocalPort)",
"$($conn.RemoteAddress):$($conn.RemotePort)",
$conn.State,
$procName) -ForegroundColor $color
}
}
Запусти так:
# Все слушающие порты
Get-OpenPorts
# Только установленные соединения
Get-OpenPorts -FilterByState 'ESTABLISHED'
# Посмотреть конкретный порт
Get-OpenPorts | Where-Object { $_ -match ':8080' }
Результат: список портов с именем процесса, PID и состоянием. Системные порты — красным.
2. Поиск заблокированного файла: кто прилепился к файлу как репей
Блокировка файла — классика жанра. Особенно весело когда лог-файл не ротируется, а база не бэкапится. Встроенные средства Windows работают через WMI-запрос к командной строке процесса. Это не идеально, но в большинстве случаев достаточно.
Если хочешь гарантированный результат — скачай handle.exe от Sysinternals. Но сначала попробуй встроенное.
function Get-LockedFile {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]$Path
)
process {
Write-Host "Пробуем выяснить, кто держит файл '$Path'..." -ForegroundColor Cyan
try {
$fileStream = [System.IO.File]::Open($Path, 'Open', 'Read', 'None')
$fileStream.Close()
Write-Host "Файл свободен." -ForegroundColor Green
}
catch [System.IO.IOException] {
Write-Host "Файл заблокирован! Ищем виновника..." -ForegroundColor Red
$lockingProcess = Get-CimInstance -ClassName Win32_Process |
Where-Object { $_.CommandLine -like "*$Path*" } |
Select-Object ProcessId, Name, CommandLine
if ($lockingProcess) {
$lockingProcess | Format-Table -AutoSize
Write-Host "Принудительно закрыть: Stop-Process -Id $($lockingProcess.ProcessId) -Force" -ForegroundColor Yellow
}
else {
Write-Host "WMI не нашёл процесс. Скачай handle.exe от Sysinternals для точного ответа." -ForegroundColor Yellow
Write-Host "handle.exe `"$Path`"" -ForegroundColor Gray
}
}
}
}
Get-LockedFile -Path "C:\logs\app.log"
3. Включаем ICMP: возвращаем пинг в строй
«Сервер не пингуется!» — эта фраза будит в 3 ночи. Часто дело в фаерволе Windows, который по умолчанию блокирует входящий ICMP. Скрипт проверяет существующее правило и либо создаёт новое, либо включает отключённое.
Запускать от администратора
Изменение правил брандмауэра требует прав администратора. Запускай PowerShell через «Запуск от имени администратора» или в сессии с UAC-повышением.
function Enable-ICMPEcho {
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[Parameter(Position=0)]
[string]$RuleName = "Allow ICMPv4 Echo Request"
)
Write-Host "Включаем ICMP echo для этого хоста..." -ForegroundColor Cyan
$existingRule = Get-NetFirewallRule -DisplayName $RuleName -ErrorAction SilentlyContinue
if ($existingRule) {
Write-Host "Правило уже есть. Включаем." -ForegroundColor Gray
Set-NetFirewallRule -DisplayName $RuleName -Enabled True
}
else {
Write-Host "Создаём правило: '$RuleName'" -ForegroundColor Gray
New-NetFirewallRule `
-DisplayName $RuleName `
-Description "Allow ICMP Echo Request" `
-Protocol ICMPv4 `
-IcmpType 8 `
-Enabled True `
-Profile Any `
-Action Allow
}
$checkRule = Get-NetFirewallRule -DisplayName $RuleName | Select-Object DisplayName, Enabled, Action
Write-Host "Результат:" -ForegroundColor Green
$checkRule | Format-Table -AutoSize
Write-Host "Готово. Проверь пинг с другой машины." -ForegroundColor Green
}
Enable-ICMPEcho
# Откатить:
# Set-NetFirewallRule -DisplayName "Allow ICMPv4 Echo Request" -Enabled False
4. Дисковое пространство: диск C: снова на голодном пайке
Классика с визуализацией. Не просто сухие цифры — прогресс-бар прямо в консоли и предупреждение при критическом заполнении.
Формула в скрипте считает процент заполнения, а не процент свободного места. Не перепутай при анализе результата.
function Get-DiskSpaceReport {
[CmdletBinding()]
param(
[Parameter(Position=0)]
[string]$DriveLetter = "C",
[int]$WarnThresholdPct = 20,
[int]$CritThresholdPct = 10
)
Write-Host "Анализируем диск $DriveLetter`:\" -ForegroundColor Cyan
$disk = Get-PSDrive -Name $DriveLetter -ErrorAction SilentlyContinue
if (-not $disk) {
Write-Error "Диск $DriveLetter не найден."
return
}
$totalGB = [math]::Round(($disk.Used + $disk.Free) / 1GB, 2)
$freeGB = [math]::Round($disk.Free / 1GB, 2)
$usedGB = [math]::Round($disk.Used / 1GB, 2)
$freePct = [math]::Round(($disk.Free / ($disk.Used + $disk.Free)) * 100, 1)
$color = if ($freePct -lt $CritThresholdPct) { 'Red' }
elseif ($freePct -lt $WarnThresholdPct) { 'Yellow' }
else { 'Green' }
$filledBlocks = [math]::Round((100 - $freePct) / 5)
$bar = '[' + ('#' * $filledBlocks) + ('-' * (20 - $filledBlocks)) + ']'
Write-Host "Диск: $($disk.Root)" -ForegroundColor White
Write-Host "Всего: $totalGB GB" -ForegroundColor Gray
Write-Host "Занято: $usedGB GB" -ForegroundColor Gray
Write-Host "Свободно: $freeGB GB ($freePct%)" -ForegroundColor $color
Write-Host "Заполнение: $bar" -ForegroundColor $color
if ($freePct -lt $CritThresholdPct) {
Write-Host ""
Write-Host "КРИТИЧНО: свободного места меньше $CritThresholdPct%!" -ForegroundColor Red -BackgroundColor Black
Write-Host "Запусти Get-TopDiskConsumers чтобы найти виновника." -ForegroundColor Yellow
}
elseif ($freePct -lt $WarnThresholdPct) {
Write-Host "Предупреждение: свободного места меньше $WarnThresholdPct%." -ForegroundColor Yellow
}
}
Get-DiskSpaceReport
Get-DiskSpaceReport -DriveLetter D -WarnThresholdPct 30
5. Event Log: что там натворила система
Листать Event Log руками — как искать иголку в стоге сена. Скрипт фильтрует только ошибки и предупреждения за нужный период. Достаточно чтобы понять, почему разбудили в 3 ночи.
function Get-RecentEvents {
[CmdletBinding()]
param(
[int]$HoursBack = 24,
[ValidateSet('System', 'Application', 'Security')]
[string]$LogName = 'System',
[int]$MaxEvents = 50
)
Write-Host "Журнал '$LogName', последние $HoursBack часов..." -ForegroundColor Cyan
$startTime = (Get-Date).AddHours(-$HoursBack)
$events = Get-WinEvent -LogName $LogName -MaxEvents $MaxEvents -ErrorAction SilentlyContinue |
Where-Object { $_.TimeCreated -ge $startTime -and ($_.Level -eq 2 -or $_.Level -eq 3) } |
Select-Object TimeCreated, LevelDisplayName, ProviderName, Id, Message |
Sort-Object TimeCreated -Descending
if (-not $events) {
Write-Host "Ничего серьёзного за этот период. Хорошая ночь." -ForegroundColor Green
return
}
Write-Host "Найдено событий: $($events.Count)" -ForegroundColor Yellow
foreach ($event in $events) {
$levelColor = if ($event.LevelDisplayName -eq 'Error') { 'Red' } else { 'Yellow' }
Write-Host "$($event.TimeCreated) [$($event.LevelDisplayName)] $($event.ProviderName) | ID: $($event.Id)" -ForegroundColor $levelColor
$msg = $event.Message -replace '\r?\n', ' '
if ($msg.Length -gt 200) { $msg = $msg.Substring(0, 200) + "..." }
Write-Host " $msg" -ForegroundColor Gray
Write-Host ("-" * 60) -ForegroundColor DarkGray
}
Write-Host "Детали: eventvwr.msc -> фильтр по ID" -ForegroundColor Cyan
}
Get-RecentEvents -HoursBack 2 -LogName Application
Get-RecentEvents -HoursBack 48 -LogName System -MaxEvents 100
6. Сканер доступности хостов: пинг по списку
Новый скрипт. Берёшь список IP или имён хостов, скрипт параллельно проверяет каждый и выдаёт таблицу: живой / мёртвый, время отклика. Незаменим когда нужно быстро проверить сегмент сети или список серверов из инвентаря.
Параллельность через ForEach-Object -Parallel работает только в PowerShell 7+. На 5.1 скрипт автоматически переключается на обычный цикл.
function Test-HostsAlive {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string[]]$Hosts,
[int]$TimeoutMs = 1000,
[int]$Count = 2
)
Write-Host "Проверяем $($Hosts.Count) хостов..." -ForegroundColor Cyan
$results = @()
$psVersion = $PSVersionTable.PSVersion.Major
if ($psVersion -ge 7) {
$results = $Hosts | ForEach-Object -Parallel {
$pingResult = Test-Connection -ComputerName $_ -Count $using:Count -TimeoutSeconds ($using:TimeoutMs / 1000) -ErrorAction SilentlyContinue
[PSCustomObject]@{
Host = $_
Status = if ($pingResult) { "ALIVE" } else { "DEAD" }
AvgMs = if ($pingResult) { [math]::Round(($pingResult.Latency | Measure-Object -Average).Average, 1) } else { "-" }
Packets = if ($pingResult) { $pingResult.Count } else { 0 }
}
} -ThrottleLimit 20
}
else {
foreach ($h in $Hosts) {
$pingResult = Test-Connection -ComputerName $h -Count $Count -ErrorAction SilentlyContinue
$results += [PSCustomObject]@{
Host = $h
Status = if ($pingResult) { "ALIVE" } else { "DEAD" }
AvgMs = if ($pingResult) { [math]::Round(($pingResult.ResponseTime | Measure-Object -Average).Average, 1) } else { "-" }
Packets = if ($pingResult) { $pingResult.Count } else { 0 }
}
}
}
$alive = ($results | Where-Object Status -eq "ALIVE").Count
$dead = ($results | Where-Object Status -eq "DEAD").Count
$results | Sort-Object Status, Host | Format-Table -AutoSize | Out-String | ForEach-Object {
$color = if ($_ -match "DEAD") { 'Red' } else { 'White' }
Write-Host $_ -ForegroundColor $color
}
Write-Host "Итого: $alive живых, $dead недоступных из $($Hosts.Count)" -ForegroundColor $(if ($dead -gt 0) { 'Yellow' } else { 'Green' })
}
# Проверить несколько хостов
Test-HostsAlive -Hosts "192.168.1.1", "192.168.1.2", "8.8.8.8", "google.com"
# Из файла со списком
Test-HostsAlive -Hosts (Get-Content "servers.txt")
# Просканировать подсеть /24 быстро
$subnet = "192.168.1"
Test-HostsAlive -Hosts (1..254 | ForEach-Object { "$subnet.$_" }) -TimeoutMs 500
7. Мониторинг служб Windows: кто упал пока ты не смотрел
Классическая задача: пришёл утром, а что-то не работает. Скрипт показывает все остановленные службы с типом запуска «Автоматически» — именно они должны работать. Если служба остановлена и должна стартовать — это повод для расследования.
function Get-ServiceStatus {
[CmdletBinding()]
param(
[string[]]$ServiceNames,
[switch]$ShowOnlyStopped,
[switch]$AutoStartOnly
)
Write-Host "Проверяем службы Windows..." -ForegroundColor Cyan
$services = if ($ServiceNames) {
$ServiceNames | ForEach-Object { Get-Service -Name $_ -ErrorAction SilentlyContinue }
}
else {
Get-Service
}
if ($AutoStartOnly) {
$services = $services | Where-Object {
(Get-WmiObject -Class Win32_Service -Filter "Name='$($_.Name)'" -ErrorAction SilentlyContinue).StartMode -eq 'Auto'
}
}
if ($ShowOnlyStopped) {
$services = $services | Where-Object { $_.Status -ne 'Running' }
}
if (-not $services) {
Write-Host "Все службы работают нормально." -ForegroundColor Green
return
}
Write-Host ("{0,-40} {1,-12} {2}" -f "Имя службы", "Статус", "Отображаемое имя") -ForegroundColor Yellow
foreach ($svc in $services | Sort-Object Status, DisplayName) {
$color = switch ($svc.Status) {
'Running' { 'Green' }
'Stopped' { 'Red' }
default { 'Yellow' }
}
Write-Host ("{0,-40} {1,-12} {2}" -f $svc.Name, $svc.Status, $svc.DisplayName) -ForegroundColor $color
}
$stopped = ($services | Where-Object { $_.Status -ne 'Running' }).Count
if ($stopped -gt 0) {
Write-Host "`nОстановлено служб: $stopped" -ForegroundColor Red
Write-Host "Перезапустить: Start-Service -Name 'имя_службы'" -ForegroundColor Yellow
}
}
# Все службы с автозапуском которые не работают
Get-ServiceStatus -AutoStartOnly -ShowOnlyStopped
# Проверить конкретные службы
Get-ServiceStatus -ServiceNames "Spooler", "wuauserv", "BITS"
# Все службы
Get-ServiceStatus
8. Топ пожирателей диска: найди кто съел всё место
Диск заполнен, нужно найти виновника. Скрипт рекурсивно обходит папку и возвращает топ-N самых жирных файлов или папок. Незаменим когда 200 гигабайт «куда-то ушли» и ты не знаешь куда.
Осторожно с рекурсией на системных папках — это может занять несколько минут на медленных дисках.
function Get-TopDiskConsumers {
[CmdletBinding()]
param(
[string]$Path = "C:\",
[int]$Top = 20,
[switch]$ByFolder,
[string]$Extension
)
Write-Host "Ищем топ-$Top пожирателей пространства в $Path ..." -ForegroundColor Cyan
Write-Host "Это может занять минуту. Потерпи." -ForegroundColor Gray
if ($ByFolder) {
$items = Get-ChildItem -Path $Path -Directory -ErrorAction SilentlyContinue | ForEach-Object {
$size = (Get-ChildItem -Path $_.FullName -Recurse -File -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
[PSCustomObject]@{
Path = $_.FullName
SizeGB = [math]::Round($size / 1GB, 3)
SizeMB = [math]::Round($size / 1MB, 1)
}
} | Sort-Object SizeGB -Descending | Select-Object -First $Top
}
else {
$filter = if ($Extension) { "*.$Extension" } else { "*" }
$items = Get-ChildItem -Path $Path -Recurse -File -Filter $filter -ErrorAction SilentlyContinue |
Sort-Object Length -Descending |
Select-Object -First $Top |
ForEach-Object {
[PSCustomObject]@{
File = $_.FullName
SizeGB = [math]::Round($_.Length / 1GB, 3)
SizeMB = [math]::Round($_.Length / 1MB, 1)
Modified = $_.LastWriteTime
}
}
}
$items | Format-Table -AutoSize
$total = ($items | Measure-Object -Property SizeMB -Sum).Sum
Write-Host "Суммарно топ-$Top занимают: $([math]::Round($total / 1024, 2)) GB" -ForegroundColor Yellow
}
# Топ-20 самых больших файлов на диске C:
Get-TopDiskConsumers -Path "C:\" -Top 20
# Топ папок
Get-TopDiskConsumers -Path "C:\" -ByFolder -Top 10
# Только .log файлы
Get-TopDiskConsumers -Path "C:\logs" -Extension "log" -Top 15
# Быстро найти .dmp файлы (дампы памяти)
Get-TopDiskConsumers -Path "C:\Windows" -Extension "dmp" -Top 5
9. Проверка DNS: резолвится или нет
Простой, но часто нужный инструмент. Проверяет резолвинг хостов через системный DNS, дополнительный DNS и через публичный резолвер. Сразу видно где рвётся цепочка.
function Get-DNSCheck {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string[]]$Hostnames,
[string[]]$DNSServers = @("8.8.8.8", "1.1.1.1")
)
Write-Host "Проверяем DNS-резолвинг..." -ForegroundColor Cyan
foreach ($hostname in $Hostnames) {
Write-Host "`n[$hostname]" -ForegroundColor White
# Системный DNS
try {
$sysResult = Resolve-DnsName -Name $hostname -ErrorAction Stop
$sysIPs = ($sysResult | Where-Object { $_.Type -in 'A','AAAA' }).IPAddress -join ', '
Write-Host " Системный DNS: $sysIPs" -ForegroundColor Green
}
catch {
Write-Host " Системный DNS: ОШИБКА - $($_.Exception.Message)" -ForegroundColor Red
}
# Альтернативные DNS-серверы
foreach ($dns in $DNSServers) {
try {
$altResult = Resolve-DnsName -Name $hostname -Server $dns -ErrorAction Stop
$altIPs = ($altResult | Where-Object { $_.Type -in 'A','AAAA' }).IPAddress -join ', '
Write-Host " $dns : $altIPs" -ForegroundColor Green
}
catch {
Write-Host " $dns : ОШИБКА" -ForegroundColor Red
}
}
}
}
Get-DNSCheck -Hostnames "google.com", "internal.corp", "api.example.com"
Get-DNSCheck -Hostnames "mail.corp" -DNSServers "10.0.0.1", "8.8.8.8"
10. Сводный отчёт о системе: всё сразу одной командой
Иногда нужно быстро осмотреться на новом сервере или при старте диагностики. Этот скрипт собирает в одном месте: информацию о железе, сетевых интерфейсах, топ-процессах по памяти и CPU, и статус критических служб. Вроде fastfetch, но для инженера.
function Get-SystemSummary {
[CmdletBinding()]
param(
[switch]$SkipNetwork,
[switch]$SkipProcesses
)
Write-Host "=== СИСТЕМНЫЙ ОТЧЁТ ===" -ForegroundColor Cyan
Write-Host "Дата: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Gray
# Базовая информация
$os = Get-CimInstance -ClassName Win32_OperatingSystem
$cpu = Get-CimInstance -ClassName Win32_Processor
$cs = Get-CimInstance -ClassName Win32_ComputerSystem
Write-Host "`n[Система]" -ForegroundColor Yellow
Write-Host " Хост: $($env:COMPUTERNAME)" -ForegroundColor White
Write-Host " ОС: $($os.Caption) $($os.OSArchitecture)" -ForegroundColor White
Write-Host " Аптайм: $(New-TimeSpan -Start $os.LastBootUpTime -End (Get-Date) | Select-Object -ExpandProperty ToString)" -ForegroundColor White
Write-Host " CPU: $($cpu.Name)" -ForegroundColor White
Write-Host " RAM всего: $([math]::Round($cs.TotalPhysicalMemory / 1GB, 1)) GB" -ForegroundColor White
$freeRam = [math]::Round($os.FreePhysicalMemory / 1MB, 1)
Write-Host " RAM свободно: $freeRam GB" -ForegroundColor $(if ($freeRam -lt 1) { 'Red' } else { 'Green' })
# Диски
Write-Host "`n[Диски]" -ForegroundColor Yellow
Get-PSDrive -PSProvider FileSystem | ForEach-Object {
if ($_.Used -gt 0 -or $_.Free -gt 0) {
$total = $_.Used + $_.Free
$freePct = [math]::Round(($_.Free / $total) * 100, 1)
$color = if ($freePct -lt 10) { 'Red' } elseif ($freePct -lt 20) { 'Yellow' } else { 'Green' }
Write-Host (" {0,-4} Всего: {1,6} GB Свободно: {2,6} GB ({3}%)" -f
"$($_.Name):\",
[math]::Round($total/1GB,1),
[math]::Round($_.Free/1GB,1),
$freePct) -ForegroundColor $color
}
}
# Сеть
if (-not $SkipNetwork) {
Write-Host "`n[Сеть]" -ForegroundColor Yellow
Get-NetIPAddress -AddressFamily IPv4 |
Where-Object { $_.IPAddress -ne '127.0.0.1' } |
Select-Object InterfaceAlias, IPAddress, PrefixLength |
ForEach-Object {
Write-Host " $($_.InterfaceAlias): $($_.IPAddress)/$($_.PrefixLength)" -ForegroundColor White
}
}
# Топ-процессов
if (-not $SkipProcesses) {
Write-Host "`n[Топ-7 процессов по памяти]" -ForegroundColor Yellow
Get-Process |
Sort-Object WorkingSet -Descending |
Select-Object -First 7 |
ForEach-Object {
Write-Host (" {0,-30} {1,6} MB" -f $_.ProcessName, [math]::Round($_.WorkingSet/1MB, 1)) -ForegroundColor Gray
}
}
Write-Host "`n=== КОНЕЦ ОТЧЁТА ===" -ForegroundColor Cyan
}
# Полный отчёт
Get-SystemSummary
# Без сети и процессов - быстро
Get-SystemSummary -SkipNetwork -SkipProcesses
Собираем всё в профиль PowerShell
Смысл всего этого — иметь инструменты под рукой в любой момент, не искать скрипты по папкам. Профиль PowerShell загружается при каждом открытии терминала. Добавь туда все функции.
# Открыть профиль для редактирования
notepad $PROFILE
# Если профиля нет - создать
if (-not (Test-Path $PROFILE)) {
New-Item -ItemType File -Path $PROFILE -Force
}
В файл профиля добавь dot-sourcing или сами функции. Рекомендую вынести в отдельный файл:
# В $PROFILE добавь:
. "$HOME\Documents\PowerShell\MyFunctions.ps1"
Туда же — алиасы для быстрого вызова:
Set-Alias -Name ports -Value Get-OpenPorts
Set-Alias -Name wholocksit -Value Get-LockedFile
Set-Alias -Name allowping -Value Enable-ICMPEcho
Set-Alias -Name diskcheck -Value Get-DiskSpaceReport
Set-Alias -Name eventcheck -Value Get-RecentEvents
Set-Alias -Name hostscan -Value Test-HostsAlive
Set-Alias -Name svccheck -Value Get-ServiceStatus
Set-Alias -Name bigfiles -Value Get-TopDiskConsumers
Set-Alias -Name dnscheck -Value Get-DNSCheck
Set-Alias -Name sysinfo -Value Get-SystemSummary
После редактирования профиль перечитывается:
. $PROFILE
Диагностика рабочего дня: типовой сценарий
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
A["Утро. Что-то не работает."] --> B["sysinfo - общий осмотр"]
B --> C{Диск забит?}
C -->|Да| D["bigfiles - найти пожирателей"]
C -->|Нет| E{Сервис упал?}
E -->|Да| F["svccheck -AutoStartOnly -ShowOnlyStopped"]
E -->|Нет| G{Сеть не отвечает?}
G -->|Да| H["hostscan - проверить хосты"]
H --> I["dnscheck - проверить DNS"]
G -->|Нет| J{Ошибки в логах?}
J -->|Да| K["eventcheck -HoursBack 24"]
J -->|Нет| L["ports - проверить слушающие порты"]
L --> M["Нашёл? Разобрался."]
K --> M
I --> M
F --> M
D --> M
style A fill:#fef9c3,stroke:#eab308,stroke-width:2px,color:#713f12
style M fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#15803d
style D fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
style F fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
style H fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
style I fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
style K fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
style L fill:#f8fafc,stroke:#3b82f6,stroke-width:1px,color:#1e40af
Troubleshooting: когда что-то пошло не так
| Ошибка |
Причина |
Решение |
File cannot be loaded because running scripts is disabled |
Execution Policy = Restricted |
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser |
Access to the path is denied |
Нет прав администратора |
Перезапусти PowerShell «от имени администратора» |
Get-NetTCPConnection: The term is not recognized |
PowerShell 2.0 или Windows 7 |
Обновись до PS 5.1+. На старых системах используй netstat вручную. |
Get-WinEvent: No events found |
Журнал пуст или период слишком мал |
Увеличь -HoursBack. Проверь, включён ли журнал: Get-WinEvent -ListLog Application |
ForEach-Object -Parallel: The parameter 'Parallel' not found |
PowerShell 5.1 не поддерживает -Parallel |
Скрипт автоматически fallback на обычный цикл. Обнови до PS 7 для скорости. |
New-NetFirewallRule: Access denied |
Нет прав на изменение фаервола |
Обязательно запускай Enable-ICMPEcho от администратора |
Resolve-DnsName: DNS name does not exist |
Хост не резолвится через системный DNS |
Проверь через альтернативный DNS: -DNSServers "8.8.8.8" |
| Get-TopDiskConsumers завис |
Рекурсия на C:\ по миллиону файлов |
Указывай конкретную папку: -Path "C:\Logs" |
| Профиль не загружается |
Путь к файлу с пробелами, Execution Policy |
Проверь $PROFILE, убедись что файл существует и нет ошибок синтаксиса |
Безопасность: что нельзя забывать
Принципы безопасности при работе с PowerShell
PowerShell — мощный инструмент. Именно поэтому злоумышленники его любят не меньше, чем администраторы. Несколько правил которые не стоит игнорировать.
- Не храни учётные данные в скриптах открытым текстом. Используй
Get-Credential или Windows Credential Manager.
- Не давай всем скриптам ExecutionPolicy Unrestricted. RemoteSigned достаточно для большинства задач.
- Enable-ICMPEcho открывает хост для пинга из любой сети (Profile Any). В продакшне уточни профиль:
-Profile Domain или -Profile Private.
- Логируй действия в продакшне. PowerShell умеет писать лог через
Start-Transcript.
- Скрипт
Stop-Process -Force не спрашивает подтверждения. Проверь PID прежде чем убивать процесс.
- Не запускай скрипты от администратора без необходимости. Принцип минимальных привилегий работает и в PowerShell.
# Логировать сессию в файл - полезно при работе с продакшн-серверами
Start-Transcript -Path "$HOME\ps_log_$(Get-Date -Format 'yyyyMMdd_HHmm').txt"
# ... работаешь ...
Stop-Transcript
Профилактика: как не чинить то, что сломается завтра
Скрипты написаны. Теперь сделай так, чтобы они работали на автопилоте, а не ждали когда ты вспомнишь запустить их вручную.
Создай задачу в планировщике для ежедневного отчёта о дисках и службах:
# Создать задачу - запускать каждое утро в 08:00
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NonInteractive -File C:\Scripts\DailyCheck.ps1 -OutputPath C:\Reports\"
$trigger = New-ScheduledTaskTrigger -Daily -At "08:00"
$settings = New-ScheduledTaskSettingsSet -RunOnlyIfNetworkAvailable
Register-ScheduledTask -TaskName "DailyServerCheck" `
-Action $action -Trigger $trigger -Settings $settings `
-RunLevel Highest -Description "Ежедневная диагностика"
Файл DailyCheck.ps1 — вызывает твои функции и пишет результат в файл или отправляет на почту:
# DailyCheck.ps1
param([string]$OutputPath = "C:\Reports\")
. "$HOME\Documents\PowerShell\MyFunctions.ps1"
$reportFile = Join-Path $OutputPath "report_$(Get-Date -Format 'yyyyMMdd').txt"
Start-Transcript -Path $reportFile
Get-SystemSummary
Get-ServiceStatus -AutoStartOnly -ShowOnlyStopped
Get-DiskSpaceReport -DriveLetter C
Stop-Transcript
# Отправить письмо если есть проблемы (опционально)
# Send-MailMessage -To "admin@corp.ru" -Subject "Daily Check" -Body (Get-Content $reportFile -Raw) -SmtpServer "mail.corp.ru"
Одна строка в crontab без комментария убивает весь отдел. Комментируй задачи планировщика — твой будущий я скажет спасибо.
Альтернативные решения
PowerShell выигрывает там, где нужна автоматизация, интеграция в пайплайн и работа без GUI. Для разовой визуальной диагностики — сторонние утилиты удобнее.
FAQ
Как запустить скрипт PowerShell если написано «запуск скриптов отключён»?
Это Execution Policy. Открой PowerShell от администратора и выполни:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
RemoteSigned разрешает запускать локальные скрипты без подписи, но требует подпись для скачанных из интернета. Это разумный компромисс между безопасностью и удобством.
Почему Get-OpenPorts не показывает UDP-порты?
Командлет Get-NetTCPConnection работает только с TCP. Для UDP используй отдельный запрос:
Get-NetUDPEndpoint | Select-Object LocalAddress, LocalPort, OwningProcess,
@{N='Process';E={(Get-Process -Id $_.OwningProcess -EA SilentlyContinue).ProcessName}} |
Sort-Object LocalPort | Format-Table -AutoSize
Как проверить, что профиль PowerShell загружается корректно?
# Посмотреть путь к профилю
$PROFILE
# Проверить, что файл существует
Test-Path $PROFILE
# Проверить на синтаксические ошибки перед загрузкой
. $PROFILE
# Если ошибок нет - тихо выполнится. Ошибки появятся в консоли.
Test-HostsAlive медленно работает на PS 5.1 — как ускорить?
На PS 5.1 нет -Parallel, поэтому хосты проверяются последовательно. Варианта два: обновиться до PowerShell 7, или использовать Jobs:
# Параллельная проверка через Jobs на PS 5.1
$jobs = $hosts | ForEach-Object {
$h = $_
Start-Job -ScriptBlock { Test-Connection -ComputerName $using:h -Count 1 -Quiet }
}
$jobs | Wait-Job | Receive-Job
$jobs | Remove-Job
Можно ли эти скрипты запускать на удалённых серверах?
Да. Используй Invoke-Command:
# Запустить функцию на удалённом сервере
Invoke-Command -ComputerName "server01.corp" -ScriptBlock ${function:Get-DiskSpaceReport}
# Или передать функцию явно
$funcDef = ${function:Get-DiskSpaceReport}.ToString()
Invoke-Command -ComputerName "server01", "server02" -ScriptBlock {
param($funcDef)
Invoke-Expression "function Get-DiskSpaceReport { $funcDef }"
Get-DiskSpaceReport
} -ArgumentList $funcDef
WinRM должен быть включён на целевых серверах: Enable-PSRemoting -Force.
Что делать если Event Log показывает тысячи ошибок?
Сначала сгруппируй по источнику — это покажет системный паттерн, а не случайный шум:
Get-WinEvent -LogName System -MaxEvents 1000 -ErrorAction SilentlyContinue |
Where-Object { $_.Level -eq 2 } |
Group-Object ProviderName |
Sort-Object Count -Descending |
Select-Object -First 10 Count, Name |
Format-Table -AutoSize
Если одна служба генерирует 90% ошибок — начинай расследование с неё, а не с разбора каждого события.
Итог
Десять функций — это не коллекция скриптов ради коллекции. Это рабочий набор для конкретных задач: диагностика, поиск виновника, контроль состояния системы. Добавь всё в профиль, потрать полчаса один раз — и экономишь часы каждую неделю.
PowerShell — инструмент для тех кто хочет понять что происходит, а не просто нажать кнопку «починить». Разбирайся в том что запускаешь. Тогда и в 3 ночи будет понятно куда смотреть.
Если что-то не заработало
Пиши в комментарии — разберёмся. Укажи версию PowerShell, ОС и текст ошибки. Без этого диагностика превращается в гадание.